Java流在一次传递中将不同的逻辑应用于最后一个元素



我有一个来自使用Spring data Jpa的数据库的数据流,该数据流需要Json序列化并写入Http响应,而不存储在内存中。这是示例代码。

try (Stream<Employee> dataStream = empRepo.findAllStream()) {
response.setHeader("content-type", "application/json");
PrintWriter respWriter = response.getWriter();
respWriter.write("[");     // array begin
dataStream.forEach(data -> {
try {
respWriter.write(jsonSerialize(data));
respWriter.write(",");
} catch (JsonProcessingException e) {
log(e);
}
entityManager.detach(data);
});
respWriter.write("]");    // array end
respWriter.flush();
} catch (IOException e) {
log(e);
}
}

但是这个逻辑会在最后一个元素后面写一个额外的逗号。如果respWriter.write(",");是最后一个元素,我怎么能不做呢?

有一些流运算符的解决方案-peekreduce等,但最优化的解决方案是什么?是否有类似Stream.hasNext()的东西,以便我可以在forEach中使用if条件?

首先,我想说的是,我认为您的问题不适合单个管道流。您使用write调用和detach调用都会产生副作用。也许你更擅长正常的for循环?还是使用多个流?

也就是说,你可以使用Eran在回答这个问题时描述的技术:用分离器交错流中的元素

try (Stream<Employee> dataStream = empRepo.findAllStream()) {
response.setHeader("content-type", "application/json");
PrintWriter respWriter = response.getWriter();
respWriter.write("[");     // array begin
dataStream.map(data -> {
try {
String json = jsonSerialize(data);
// NOTE! It is confusing to have side effects like this in a stream!
entityManager.detach(data);
return json;
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
})
.flatMap(json -> Stream.of(",", json))
.skip(1)
.forEach(respWriter::write);
respWriter.write("]");    // array end
respWriter.flush();
} catch (IOException e) {
log(e);
}

对于这个特定场景,您可以使用Collectors.joining

printWriter.write(dataStream
.map(this::deserializeJson)
.peek(entityManager::detach)
.collect(Collectors.joining(","));

然而,由于您正在执行流中不鼓励的副作用,并且您特别询问了hasNext()操作,并且由于此流解决方案将在内存中构建一个大字符串,因此您可能更喜欢将流转换为迭代器并使用命令式循环:

Iterator<Employee> it = dataStream.iterator();
while(it.hasNext()) {
Employee data = it.next();
...
// skip writing delimiter on last entry
if (it.hasNext()){
respWriter.write(",")
}
}

相关内容

最新更新