如何将字符串流转换为字符串流对?

我想取一串字符串并将其转换为单词对流。 例如:

我有: { "A", "Apple", "B", "Banana", "C", "Carrot" }

我想要: { ("A", "Apple"), ("Apple", "B"), ("B", "Banana"), ("Banana", "C") }

这与使用带有lambda的JDK8的Zipping流中概述的Zipping几乎相同(java.util.stream.Streams.zip)

然而,这会产生: { (A, Apple), (B, Banana), (C, Carrot) }

以下代码有效,但显然是错误的方法(不是线程安全等):

 static String buffered = null; static void output(String s) { String result = null; if (buffered != null) { result = buffered + "," + s; } else { result = null; } buffered = s; System.out.println(result); } // ***** Stream testing = Stream.of("A", "Apple", "B", "Banana", "C", "Carrot"); testing.forEach(s -> {output(s);}); 

如果你:

  1. 不喜欢创建包含流中所有字符串的列表的想法
  2. 不想使用外部库
  3. 喜欢弄脏你的手

然后,您可以创建一个方法,使用Java 8低级流构建器StreamSupportSpliterator

 class StreamUtils { public static Stream> sliding(int size, Stream stream) { return sliding(size, 1, stream); } public static Stream> sliding(int size, int step, Stream stream) { Spliterator spliterator = stream.spliterator(); long estimateSize; if (!spliterator.hasCharacteristics(Spliterator.SIZED)) { estimateSize = Long.MAX_VALUE; } else if (size > spliterator.estimateSize()) { estimateSize = 0; } else { estimateSize = (spliterator.estimateSize() - size) / step + 1; } return StreamSupport.stream( new Spliterators.AbstractSpliterator>(estimateSize, spliterator.characteristics()) { List buffer = new ArrayList<>(size); @Override public boolean tryAdvance(Consumer> consumer) { while (buffer.size() < size && spliterator.tryAdvance(buffer::add)) { // Nothing to do } if (buffer.size() == size) { List keep = new ArrayList<>(buffer.subList(step, size)); consumer.accept(buffer); buffer = keep; return true; } return false; } }, stream.isParallel()); } } 

方法和参数命名的灵感来自他们的Scala对应物。

我们来试试吧:

 Stream testing = Stream.of("A", "Apple", "B", "Banana", "C", "Carrot"); System.out.println(StreamUtils.sliding(2, testing).collect(Collectors.toList())); 

[[A,Apple],[Apple,B],[B,Banana],[香蕉,C],[C,胡萝卜]]

不重复元素怎么样:

 Stream testing = Stream.of("A", "Apple", "B", "Banana", "C", "Carrot"); System.out.println(StreamUtils.sliding(2, 2, testing).collect(Collectors.toList())); 

[[A,Apple],[B,香蕉],[C,胡萝卜]]

现在有了无限的Stream

 StreamUtils.sliding(5, Stream.iterate(0, n -> n + 1)) .limit(5) .forEach(System.out::println); 

[0,1,2,3,4]
[1,2,3,4,5]
[2,3,4,5,6]
[3,4,5,6,7]
[4,5,6,7,8]

这应该做你想要的,基于@ njzk2的两次使用流的注释,跳过第二种情况下的第一个元素。 它使用您在原始问题中链接的zip方法。

 public static void main(String[] args) { List input = Arrays.asList("A", "Apple", "B", "Banana", "C", "Carrot"); List> paired = zip(input.stream(), input.stream().skip(1), (a, b) -> Arrays.asList(a, b)) .collect(ArrayList::new, ArrayList::add, ArrayList::addAll); System.out.println(paired); } 

这将输出List> ,其内容为:

 [[A, Apple], [Apple, B], [B, Banana], [Banana, C], [C, Carrot]] 

在评论中,如果您已有Stream ,则询问如何执行此操作。 不幸的是,它很难,因为Streams不是有状态的,并且Streams中没有“相邻”元素的概念。 这里有一个很好的讨论 。

我可以想到两种方法,但我不认为你会喜欢它们中的任何一种:

  1. Stream转换为List ,然后执行上面的解决方案。 丑陋,但只要Stream不是无限的,并且性能无关紧要。
  2. 只要您使用的是StreamEx而不是Stream ,并且愿意在第三方库上添加依赖项,请使用@ TagirValeev的答案 。

与此讨论相关的还有以下问题: 我可以在Java 8中复制Stream吗? ; 这对你的问题不是好消息,但值得一读,可能会有一个更吸引你的解决方案。

您可以使用我的StreamEx库来增强标准Stream API。 有一个方法pairMap可以完全满足您的需求:

 StreamEx.of("A", "Apple", "B", "Banana", "C", "Carrot") .pairMap((a, b) -> a+","+b) .forEach(System.out::println); 

输出:

 A,Apple Apple,B B,Banana Banana,C C,Carrot 

pairMap参数是将相邻元素对转换为适合您需要的元素的函数。 如果项目中有Pair类,则可以使用.pairMap(Pair::new)来获取对的流。 如果要创建双元素列表流,可以使用:

 List> list = StreamEx.of("A", "Apple", "B", "Banana", "C", "Carrot") .pairMap((a, b) -> StreamEx.of(a, b).toList()) .toList(); System.out.println(list); // [[A, Apple], [Apple, B], [B, Banana], [Banana, C], [C, Carrot]] 

这适用于任何元素源(您可以使用StreamEx.of(collection)StreamEx.of(stream)等),如果您在pairMap之前有更多的流操作并且对并行处理非常友好,则正确工作(与涉及流的解决方案不同)荏苒)。

如果您的输入是具有快速随机访问权限的List并且您实际上希望List>作为结果,那么使用ofSubLists在我的库中实现此目的的方式更短且有所不同:

 List input = Arrays.asList("A", "Apple", "B", "Banana", "C", "Carrot"); List> list = StreamEx.ofSubLists(input, 2, 1).toList(); System.out.println(list); // [[A, Apple], [Apple, B], [B, Banana], [Banana, C], [C, Carrot]] 

在幕后input.subList(i, i+2)为每个输入列表位置调用input.subList(i, i+2) ,因此不会将数据复制到新列表,而是创建引用原始列表的子列表。

这是创建对的List>的最少量代码:

 List> pairs = new LinkedList<>(); testing.reduce((a, b)-> {pairs.add(Arrays.asList(a,b)); return b;});