我目前想出的唯一解决方案是实现自己的自定义收集器。
在阅读解决方案之前,我想添加一些有关此的注释。我将这个问题更多地当作编程练习,我不确定是否可以使用并行流来完成。
因此,您必须意识到,如果 并行* 运行管道,它将 无声地中断 。 *
这 不是 理想的行为,应该 避免 。这就是为什么我在组合器部分(而不是
(l1, l2) -> {l1.addAll(l2); returnl1;})中引发异常,因为在组合两个列表时会并行使用它,以便您有一个异常而不是错误的结果。同样,由于列表复制,这种方法也不是很有效(尽管它使用本机方法来复制基础数组)。
因此,这是收集器的实现:
private static Collector<String, List<List<String>>, List<List<String>>> splitBySeparator(Predicate<String> sep) { final List<String> current = new ArrayList<>(); return Collector.of(() -> new ArrayList<List<String>>(), (l, elem) -> { if (sep.test(elem)) { l.add(new ArrayList<>(current)); current.clear(); } else { current.add(elem); } }, (l1, l2) -> { throw new RuntimeException("Should not run this in parallel"); }, l -> { if (current.size() != 0) { l.add(current); return l; } );}以及如何使用它:
List<List<String>> ll = list.stream().collect(splitBySeparator(Objects::isNull));
输出:
[[a, b], [c], [d, e]]
当JoopEggen的答案出炉时,看来可以并行完成(为此感谢他!)。这样,它将自定义收集器实现简化为:
private static Collector<String, List<List<String>>, List<List<String>>> splitBySeparator(Predicate<String> sep) { return Collector.of(() -> new ArrayList<List<String>>(Arrays.asList(new ArrayList<>())), (l, elem) -> {if(sep.test(elem)){l.add(new ArrayList<>());} else l.get(l.size()-1).add(elem);}, (l1, l2) -> {l1.get(l1.size() - 1).addAll(l2.remove(0)); l1.addAll(l2); return l1;});}这使有关并行性的段落过时了,但是我将其保留下来,因为它可以很好地提醒您。
请注意,Stream API并不总是可以替代。有些任务使用流更容易且更适合,有些则不是。对于您的情况,您还可以为此创建一个实用程序方法:
private static <T> List<List<T>> splitBySeparator(List<T> list, Predicate<? super T> predicate) { final List<List<T>> finalList = new ArrayList<>(); int fromIndex = 0; int toIndex = 0; for(T elem : list) { if(predicate.test(elem)) { finalList.add(list.subList(fromIndex, toIndex)); fromIndex = toIndex + 1; } toIndex++; } if(fromIndex != toIndex) { finalList.add(list.subList(fromIndex, toIndex)); } return finalList;}并称它为
List<List<String>> list = splitBySeparator(originalList,Objects::isNull);。
可以改进以检查边缘情况。



