选择列表的元素,直到Java 8 Lambdas满足条件

我试图改变主意去思考function方式,最近我遇到了一个情况,我需要从列表中获取元素,直到满足条件,我找不到一种简单的自然方式来实现这一点。 显然我还在学习。

说我有这个清单:

List tokens = Arrays.asList("pick me", "Pick me", "pick Me", "PICK ME", "pick me and STOP", "pick me", "pick me and Stop", "pick me"); // In a non lambdas was you would do it like below List myTokens = new ArrayList(); for (String token : tokens) { myTokens.add(token); if (token.toUpperCase().endsWith("STOP")) { break; } } 

提前感谢您的意见

注意:在发布之前,我读了一个谓词限制流,但我看不出如何能够根据我的问题调整答案。 任何帮助将不胜感激谢谢。

一个选项使用需要两个函数的收集器,一个用于向列表添加字符串,另一个用于组合先前可能并行创建的列表。 对于每个,只有当前一个部分输出不以以STOP结尾的元素结束时,它才会添加字符串或整个列表:

 tokens.stream().collect(() -> new ArrayList(), (l, e) -> { if(l.isEmpty() || !l.get(l.size()-1).toUpperCase().endsWith("STOP")) l.add(e); }, (l1, l2) -> { if(l1.isEmpty() || !l1.get(l1.size()-1).toUpperCase().endsWith("STOP")) l1.addAll(l2); }); 

在JDK9中,将会有一个名为takeWhile的新Stream操作,它可以完成与您需要的操作相似的操作。 我将此操作向后移植到我的StreamEx库,因此即使在Java-8中也可以使用它:

 List list = StreamEx.of(tokens) .takeWhile(t -> !t.toUpperCase().endsWith("STOP")) .toList(); 

不幸的是它没有"STOP"元素本身,所以第二遍是必要的手动添加它:

 list.add(StreamEx.of(tokens).findFirst(t -> t.toUpperCase().endsWith("STOP")).get()); 

请注意, takeWhilefindFirst都是短路操作(如果不必要,它们不会处理整个输入流),因此您可以使用非常长或甚至无限的流。

但是,使用StreamEx,您可以使用groupRuns的技巧在单次传递中解决它。 groupRuns方法基于提供的谓词将相邻的Stream元素分组到List ,该谓词告知是否应该对两个给定的相邻元素进行分组。 我们可以认为该组以包含"STOP"的元素结束。 然后我们只需要采取第一组:

 List list = StreamEx.of(tokens) .groupRuns((a, b) -> !a.toUpperCase().endsWith("STOP")) .findFirst().get(); 

第一组完成后,此解决方案也不会执行额外的工作。

如果您真的必须使用Streams API,请保持简单并使用索引流:

 int lastIdx = IntStream.range(0, tokens.size()) .filter(i -> tokens.get(i).toUpperCase().endsWith("STOP")) .findFirst() .orElse(-1); List myTokens = tokens.subList(0, lastIdx + 1); 

如果您想要一个没有原始列表支持的独立副本,或者从子列表中创建一个新列表。

虽然上述答案完全有效,但它们需要在处理元素之前收集和/或预取元素(如果Stream非常长,则两者都可能成为问题)。

根据我的需要 ,我因此改编了路易斯对朱利安指出的问题的回答 ,并对其进行了调整以保留停止/rest项目。 请参阅keepBreak参数::

 public static  Spliterator takeWhile(final Spliterator splitr, final Predicate predicate, final boolean keepBreak) { return new Spliterators.AbstractSpliterator(splitr.estimateSize(), 0) { boolean stillGoing = true; @Override public boolean tryAdvance(final Consumer consumer) { if (stillGoing) { final boolean hadNext = splitr.tryAdvance(elem -> { if (predicate.test(elem)) { consumer.accept(elem); } else { if (keepBreak) { consumer.accept(elem); } stillGoing = false; } }); return hadNext && (stillGoing || keepBreak); } return false; } }; } public static  Stream takeWhile(final Stream stream, final Predicate predicate, final boolean keepBreak) { return StreamSupport.stream(takeWhile(stream.spliterator(), predicate, keepBreak), false); } 

用法:

 public List values = Arrays.asList("some", "words", "before", "BREAK", "AFTER"); @Test public void testStopAfter() { Stream stream = values.stream(); //how to filter stream to stop at the first BREAK stream = stream.filter(makeUntil(s -> "BREAK".equals(s))); final List actual = stream.collect(Collectors.toList()); final List expected = Arrays.asList("some", "words", "before", "BREAK"); assertEquals(expected, actual); } 

免责声明 :我不是100%肯定这将在并行(新流肯定不是并行)或非顺序流上工作。 如果您对此有一些提示,请评论/编辑。

严格使用Java 8 API:

 public static  Stream takeUntil(Iterator iterator, Predicate stopFilter) { return StreamSupport.stream(Spliterators.spliteratorUnknownSize(new Iterator() { private R next = null; private boolean hasTaken = true; private boolean stopIteration = !iterator.hasNext(); @Override public boolean hasNext() { if (stopIteration) { return false; } if (!hasTaken) { return true; } if (!iterator.hasNext()) { stopIteration = true; return false; } next = iterator.next(); stopIteration = stopFilter.test(next); hasTaken = stopIteration; return !stopIteration; } @Override public R next() { if (!hasNext()) { throw new NoSuchElementException("There are no more items to consume"); } hasTaken = true; return next; } }, 0), false); } 

然后,您可以通过以下方式对其进行专门化:

对于溪流

 public static  Stream takeUntil(Stream stream, Predicate stopFilter) { return takeUntil(stream.iterator(), stopFilter); } 

对于collections

 public static  Stream takeUntil(Collection col, Predicate stopFilter) { return takeUntil(col.iterator(), stopFilter); }