如何在 Java8 中将一个流缩减为另一个流?
How to reduce a stream into another stream in Java8?
举个例子,我想像这样创建无限的十位组流:
0=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1=[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
2=[20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
...
我想使用无限的整数流作为输入,然后应该对其进行分组。如果第一个流迭代 10 次,结果流应该只迭代一次。
我的工作但不是很优雅的代码如下所示:
// create a stream from 0 (inclusive) to 100 (exclusive)
IntStream.iterate(0, i -> i+1).boxed().limit(100)
// slow down
.peek((i) -> {try {Thread.sleep(50);} catch (InterruptedException e) {}})
// group by tens
/* ugly: */.collect(Collectors.groupingBy(i -> i / 10)).entrySet()
/* not working: */ //.makeSequentialGroups(i -> i / 10)
// print to console
.forEach(System.out::println);
如何在不必收集和重新流式传输的情况下对 int 流进行分组? (如果可能甚至不用装箱)
我怀疑是否有办法,因为您不能在不收集的情况下从 sequence to a Map in java 8 进行映射,也不能在不收集的情况下进行分组。您可以创建自己的流,但我怀疑您是否真的想走那条路。
所以,虽然这不是答案,但如果您想节省一些时钟周期,我会选择这样的方法:
IntStream.range(0, 10)
.boxed()
.collect(Collectors.toMap(
Function.identity(),
(x) -> IntStream.range(x * 10, x * 10 + 10)
))
似乎如果一个流基于另一个流,那么它总是必须具有完全相同的条目数。
但是我找到了解决问题的方法:我将消费者包装到 "GroupingConsumer" 中。这将终止初始流,但仍然可以无限执行。
截取的结果代码:
// create a stream from 0 (inclusive) to infinity!
IntStream.iterate(0, i -> i+1).boxed()
// slow down
.peek((i) -> {try {Thread.sleep(50);} catch (InterruptedException e) {}})
// terminate the stream of single items (ungrouped)
.forEach(
// create a wrap-around
GroupingConsumer.create(
// define the grouping rule
i -> i/10,
// the wrapped consumer
System.out::println
));
GroupingConsumer
class:
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* Forwards a group of items, whenever the grouping-key changes
*
* @param <K> the type of the grouping key
* @param <T> the type of the single entries
*/
class GroupingConsumer<K, T> implements Consumer<K> {
private Function<K, T> keyCalculator;
private Consumer<Entry<T, List<K>>> consumer;
Entry<T, List<K>> currentGroup;
/**
* Wraps your consumer, so that it will get groups of items instead of single items.
*
* @param keyCalculator the "grouping by"
* @param consumer your consumer, that will be called less frequently
* @return the wrapped consumer
*/
public static <K, T> GroupingConsumer<K,T> create(Function<K, T> keyCalculator, Consumer<Entry<T, List<K>>> consumer) {
GroupingConsumer<K, T> groupingConsumer = new GroupingConsumer<K, T>();
groupingConsumer.consumer = consumer;
groupingConsumer.keyCalculator = keyCalculator;
return groupingConsumer;
}
@Override
public void accept(K nextValue) {
T key = keyCalculator.apply(nextValue);
boolean newGroupRequired = false;
if (currentGroup == null)
newGroupRequired = true;
else if (!currentGroup.getKey().equals(key)) {
newGroupRequired = true;
consumer.accept(currentGroup);
}
if (newGroupRequired)
currentGroup = new SimpleEntry<T, List<K>>(key, new ArrayList<K>());
currentGroup.getValue().add(nextValue);
}
}
我的 StreamEx library and called groupRuns
中提供了这样的功能:您可以根据提供的谓词将相邻元素收集到中间列表中。示例:
IntStreamEx.iterate(0, i -> i+1).boxed().limit(100)
.peek((i) -> {try {Thread.sleep(50);} catch (InterruptedException e) {}})
.groupRuns((a, b) -> a/10 == b/10)
.forEach(System.out::println);
您可以将数组视为键类型为原始 int
的 Map,唯一的区别在于,您不是通过 map.get(i)
查找值,而是通过 myArray[i]
。使用数组对数组进行分组也可以让您按照要求避免装箱。这是一个无需装箱即可产生类似结果的解决方案。
int[][] results = IntStream.iterate(0, i -> i + 10)
.limit(10)
.mapToObj(i -> (int[]) IntStream.range(i, i + 10).toArray())
.toArray(int[][]::new);
System.out.println(Arrays.deepToString(results));
举个例子,我想像这样创建无限的十位组流:
0=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1=[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
2=[20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
...
我想使用无限的整数流作为输入,然后应该对其进行分组。如果第一个流迭代 10 次,结果流应该只迭代一次。
我的工作但不是很优雅的代码如下所示:
// create a stream from 0 (inclusive) to 100 (exclusive)
IntStream.iterate(0, i -> i+1).boxed().limit(100)
// slow down
.peek((i) -> {try {Thread.sleep(50);} catch (InterruptedException e) {}})
// group by tens
/* ugly: */.collect(Collectors.groupingBy(i -> i / 10)).entrySet()
/* not working: */ //.makeSequentialGroups(i -> i / 10)
// print to console
.forEach(System.out::println);
如何在不必收集和重新流式传输的情况下对 int 流进行分组? (如果可能甚至不用装箱)
我怀疑是否有办法,因为您不能在不收集的情况下从 sequence to a Map in java 8 进行映射,也不能在不收集的情况下进行分组。您可以创建自己的流,但我怀疑您是否真的想走那条路。
所以,虽然这不是答案,但如果您想节省一些时钟周期,我会选择这样的方法:
IntStream.range(0, 10)
.boxed()
.collect(Collectors.toMap(
Function.identity(),
(x) -> IntStream.range(x * 10, x * 10 + 10)
))
似乎如果一个流基于另一个流,那么它总是必须具有完全相同的条目数。
但是我找到了解决问题的方法:我将消费者包装到 "GroupingConsumer" 中。这将终止初始流,但仍然可以无限执行。
截取的结果代码:
// create a stream from 0 (inclusive) to infinity!
IntStream.iterate(0, i -> i+1).boxed()
// slow down
.peek((i) -> {try {Thread.sleep(50);} catch (InterruptedException e) {}})
// terminate the stream of single items (ungrouped)
.forEach(
// create a wrap-around
GroupingConsumer.create(
// define the grouping rule
i -> i/10,
// the wrapped consumer
System.out::println
));
GroupingConsumer
class:
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* Forwards a group of items, whenever the grouping-key changes
*
* @param <K> the type of the grouping key
* @param <T> the type of the single entries
*/
class GroupingConsumer<K, T> implements Consumer<K> {
private Function<K, T> keyCalculator;
private Consumer<Entry<T, List<K>>> consumer;
Entry<T, List<K>> currentGroup;
/**
* Wraps your consumer, so that it will get groups of items instead of single items.
*
* @param keyCalculator the "grouping by"
* @param consumer your consumer, that will be called less frequently
* @return the wrapped consumer
*/
public static <K, T> GroupingConsumer<K,T> create(Function<K, T> keyCalculator, Consumer<Entry<T, List<K>>> consumer) {
GroupingConsumer<K, T> groupingConsumer = new GroupingConsumer<K, T>();
groupingConsumer.consumer = consumer;
groupingConsumer.keyCalculator = keyCalculator;
return groupingConsumer;
}
@Override
public void accept(K nextValue) {
T key = keyCalculator.apply(nextValue);
boolean newGroupRequired = false;
if (currentGroup == null)
newGroupRequired = true;
else if (!currentGroup.getKey().equals(key)) {
newGroupRequired = true;
consumer.accept(currentGroup);
}
if (newGroupRequired)
currentGroup = new SimpleEntry<T, List<K>>(key, new ArrayList<K>());
currentGroup.getValue().add(nextValue);
}
}
我的 StreamEx library and called groupRuns
中提供了这样的功能:您可以根据提供的谓词将相邻元素收集到中间列表中。示例:
IntStreamEx.iterate(0, i -> i+1).boxed().limit(100)
.peek((i) -> {try {Thread.sleep(50);} catch (InterruptedException e) {}})
.groupRuns((a, b) -> a/10 == b/10)
.forEach(System.out::println);
您可以将数组视为键类型为原始 int
的 Map,唯一的区别在于,您不是通过 map.get(i)
查找值,而是通过 myArray[i]
。使用数组对数组进行分组也可以让您按照要求避免装箱。这是一个无需装箱即可产生类似结果的解决方案。
int[][] results = IntStream.iterate(0, i -> i + 10)
.limit(10)
.mapToObj(i -> (int[]) IntStream.range(i, i + 10).toArray())
.toArray(int[][]::new);
System.out.println(Arrays.deepToString(results));