[英]How can I convert a Stream of Strings to Stream of String pairs?
I want to take a stream of strings and turn it into a stream of word pairs. 我想取一串字符串并将其转换为单词对流。 eg:
例如:
I have: { "A", "Apple", "B", "Banana", "C", "Carrot" }
我有:
{ "A", "Apple", "B", "Banana", "C", "Carrot" }
I want: { ("A", "Apple"), ("Apple", "B"), ("B", "Banana"), ("Banana", "C") }
. 我想要:
{ ("A", "Apple"), ("Apple", "B"), ("B", "Banana"), ("Banana", "C") }
。
This is nearly the same as Zipping, as outlined at Zipping streams using JDK8 with lambda (java.util.stream.Streams.zip) 这与使用带有lambda的JDK8的Zipping流中概述的Zipping几乎相同(java.util.stream.Streams.zip)
However, that produces: { (A, Apple), (B, Banana), (C, Carrot) }
然而,这产生:
{ (A, Apple), (B, Banana), (C, Carrot) }
The following code works, but is clearly the wrong way to do it (not thread safe etc etc): 以下代码有效,但显然是错误的方法(不是线程安全等):
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<String> testing = Stream.of("A", "Apple", "B", "Banana", "C", "Carrot");
testing.forEach(s -> {output(s);});
This should do what you want, based on @njzk2's comment of using the stream twice, skipping the first element in the second case. 这应该做你想要的,基于@ njzk2的两次使用流的注释,跳过第二种情况下的第一个元素。 It uses the
zip
method that you link in your original question. 它使用您在原始问题中链接的
zip
方法。
public static void main(String[] args) {
List<String> input = Arrays.asList("A", "Apple", "B", "Banana", "C", "Carrot");
List<List<String>> 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);
}
This outputs a List<List<String>>
with contents: 这将输出
List<List<String>>
,其内容为:
[[A, Apple], [Apple, B], [B, Banana], [Banana, C], [C, Carrot]]
In the comments, you asked how to do this if you already have a Stream
. 在评论中,如果您已有
Stream
,则询问如何执行此操作。 Unfortunately, it's difficult, because Streams
are not stateful, and there isn't really a concept of the "adjacent" element in the Stream
. 不幸的是,这是困难的,因为
Streams
是无状态的,并没有真正在的“相邻”元素的概念Stream
。 There is a good discussion on this here . 这里有一个很好的讨论 。
I can think of two ways to do it, but I don't think you're going to like either of them: 我可以想到两种方法,但我不认为你会喜欢它们中的任何一种:
Stream
to a List
, and then do my solution above. Stream
转换为List
,然后执行上面的解决方案。 Ugly, but works as long as the Stream
isn't infinite and performance doesn't matter very much. Stream
不是无限的,并且性能无关紧要。 StreamEx
and not a Stream
, and willing to add a dependency on a third party library. StreamEx
而不是Stream
,并且愿意在第三方库中添加依赖项。 Also relevant to this discussion is this question here: Can I duplicate a Stream in Java 8? 与此讨论相关的还有以下问题: 我可以在Java 8中复制Stream吗? ;
; it's not good news for your problem, but is worth reading and may have a solution that's more appealing to you.
这对你的问题不是好消息,但值得一读,可能会有一个对你更有吸引力的解决方案。
If you: 如果你:
Then you can create a method to group elements from a stream using Java 8 low-level stream builders StreamSupport
and Spliterator
: 然后,您可以创建一个方法,使用Java 8低级流构建器
StreamSupport
和Spliterator
:
class StreamUtils {
public static<T> Stream<List<T>> sliding(int size, Stream<T> stream) {
return sliding(size, 1, stream);
}
public static<T> Stream<List<T>> sliding(int size, int step, Stream<T> stream) {
Spliterator<T> 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<List<T>>(estimateSize, spliterator.characteristics()) {
List<T> buffer = new ArrayList<>(size);
@Override
public boolean tryAdvance(Consumer<? super List<T>> consumer) {
while (buffer.size() < size && spliterator.tryAdvance(buffer::add)) {
// Nothing to do
}
if (buffer.size() == size) {
List<T> keep = new ArrayList<>(buffer.subList(step, size));
consumer.accept(buffer);
buffer = keep;
return true;
}
return false;
}
}, stream.isParallel());
}
}
Methods and parameters naming was inspired in their Scala counterparts. 方法和参数命名的灵感来自他们的Scala对应物。
Let's test it: 我们来测试一下:
Stream<String> 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], [Banana, C], [C, Carrot]]
[[A,Apple],[Apple,B],[B,Banana],[香蕉,C],[C,胡萝卜]]
What about not repeating elements: 不重复元素怎么样:
Stream<String> testing = Stream.of("A", "Apple", "B", "Banana", "C", "Carrot");
System.out.println(StreamUtils.sliding(2, 2, testing).collect(Collectors.toList()));
[[A, Apple], [B, Banana], [C, Carrot]]
[[A,Apple],[B,香蕉],[C,胡萝卜]]
And now with an infinite Stream
: 现在有一个无限的
Stream
:
StreamUtils.sliding(5, Stream.iterate(0, n -> n + 1))
.limit(5)
.forEach(System.out::println);
[0, 1, 2, 3, 4]
[0,1,2,3,4]
[1, 2, 3, 4, 5][1,2,3,4,5]
[2, 3, 4, 5, 6][2,3,4,5,6]
[3, 4, 5, 6, 7][3,4,5,6,7]
[4, 5, 6, 7, 8][4,5,6,7,8]
You can use my StreamEx library which enhances standard Stream API. 您可以使用我的StreamEx库来增强标准Stream API。 There is a method
pairMap
which does exactly what you need: 有一个方法
pairMap
可以完全满足您的需求:
StreamEx.of("A", "Apple", "B", "Banana", "C", "Carrot")
.pairMap((a, b) -> a+","+b)
.forEach(System.out::println);
Output: 输出:
A,Apple
Apple,B
B,Banana
Banana,C
C,Carrot
The pairMap
argument is the function which converts the pair of adjacent elements to something which is suitable to your needs. pairMap
参数是将相邻元素对转换为适合您需要的元素的函数。 If you have a Pair
class in your project, you can use .pairMap(Pair::new)
to get the stream of pairs. 如果项目中有
Pair
类,则可以使用.pairMap(Pair::new)
来获取对的流。 If you want to create a stream of two-element lists, you can use: 如果要创建双元素列表流,可以使用:
List<List<String>> 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]]
This works with any element source (you can use StreamEx.of(collection)
, StreamEx.of(stream)
and so on), correctly works if you have more stream operations before pairMap
and very friendly to parallel processing (unlike solutions which involve stream zipping). 这适用于任何元素源(您可以使用
StreamEx.of(collection)
, StreamEx.of(stream)
等),如果您在pairMap
之前有更多流操作并且对并行处理非常友好,则正确工作(与涉及流的解决方案不同)荏苒)。
In case if your input is a List
with fast random access and you actually want List<List<String>>
as a result, there's a shorter and somewhat different way to achieve this in my library using ofSubLists
: 如果您的输入是具有快速随机访问权限的
List
并且您实际上希望List<List<String>>
作为结果,那么使用ofSubLists
在我的库中实现此目的的方式更短且有所不同:
List<String> input = Arrays.asList("A", "Apple", "B", "Banana", "C", "Carrot");
List<List<String>> list = StreamEx.ofSubLists(input, 2, 1).toList();
System.out.println(list); // [[A, Apple], [Apple, B], [B, Banana], [Banana, C], [C, Carrot]]
Here behind the scenes input.subList(i, i+2)
is called for each input list position, so your data is not copied to the new lists, but subLists are created which refer to the original list. 在幕后
input.subList(i, i+2)
为每个输入列表位置调用input.subList(i, i+2)
,因此不会将数据复制到新列表,而是创建引用原始列表的子列表。
Here's a minimal amount of code that creates a List<List<String>>
of the pairs: 这是创建对的
List<List<String>>
的最少量代码:
List<List<String>> pairs = new LinkedList<>();
testing.reduce((a, b)-> {pairs.add(Arrays.asList(a,b)); return b;});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.