[英]How can I convert a Stream of Strings to Stream of String pairs?
我想取一串字符串并将其转换为单词对流。 例如:
我有: { "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<String> testing = Stream.of("A", "Apple", "B", "Banana", "C", "Carrot");
testing.forEach(s -> {output(s);});
这应该做你想要的,基于@ njzk2的两次使用流的注释,跳过第二种情况下的第一个元素。 它使用您在原始问题中链接的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);
}
这将输出List<List<String>>
,其内容为:
[[A, Apple], [Apple, B], [B, Banana], [Banana, C], [C, Carrot]]
在评论中,如果您已有Stream
,则询问如何执行此操作。 不幸的是,这是困难的,因为Streams
是无状态的,并没有真正在的“相邻”元素的概念Stream
。 这里有一个很好的讨论 。
我可以想到两种方法,但我不认为你会喜欢它们中的任何一种:
Stream
转换为List
,然后执行上面的解决方案。 丑陋,但只要Stream
不是无限的,并且性能无关紧要。 StreamEx
而不是Stream
,并且愿意在第三方库中添加依赖项。 与此讨论相关的还有以下问题: 我可以在Java 8中复制Stream吗? ; 这对你的问题不是好消息,但值得一读,可能会有一个对你更有吸引力的解决方案。
如果你:
然后,您可以创建一个方法,使用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());
}
}
方法和参数命名的灵感来自他们的Scala对应物。
我们来测试一下:
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],[香蕉,C],[C,胡萝卜]]
不重复元素怎么样:
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,香蕉],[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]
您可以使用我的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<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]]
这适用于任何元素源(您可以使用StreamEx.of(collection)
, StreamEx.of(stream)
等),如果您在pairMap
之前有更多流操作并且对并行处理非常友好,则正确工作(与涉及流的解决方案不同)荏苒)。
如果您的输入是具有快速随机访问权限的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]]
在幕后input.subList(i, i+2)
为每个输入列表位置调用input.subList(i, i+2)
,因此不会将数据复制到新列表,而是创建引用原始列表的子列表。
这是创建对的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.