我有多個相同長度的 Java 流,我想對每個流的相應元素執行一些操作。例如,添加所有流的第一個元素、所有流的第二個元素和所有流的第三個元素等等。
我們如何在不首先減少每個流的情況下做到這一點?
對于最小可重現示例,我有以下測驗片段
@Test
void aggregateMultipleStreams() {
Stream<Integer> s1 = Stream.of(1, 2);
Stream<Integer> s2 = Stream.of(4, 5);
Stream<Integer> s3 = Stream.of(7, 8);
assertEquals(List.of(1 4 7, 2 5 8), aggregate(s1, s2, s3, 2));
}
我可以aggregate通過首先減少所有流來撰寫如下方法。
private List<Integer> aggregate(Stream<Integer> s1, Stream<Integer> s2, Stream<Integer> s3, int streamSize) {
final List<List<Integer>> reduced = Stream.of(s1, s2, s3)
.map(s -> s.collect(Collectors.toList())).collect(Collectors.toList());
return IntStream.range(0, streamSize).mapToObj(n -> IntStream.range(0, reduced.size())
.map(v -> reduced.get(v).get(n)).sum()).collect(Collectors.toList());
}
但這可能是一個存盤麻煩,如果每個流包含許多記錄,對于 N 條記錄,我們這里需要 3N 存盤。
我們可以在不首先減少的情況下完成不同流中相應元素的添加嗎?我們可以在 Java 中一次減少多個流嗎?
在下面實作@jb_dk的解決方案后,解決方案代碼片段變成了:
private List<Integer> aggregate(Stream<Integer> s1, Stream<Integer> s2, Stream<Integer> s3, int streamSize) {
final List<Iterator<Integer>> iterators = Stream.of(s1, s2, s3)
.map(Stream::iterator).collect(Collectors.toList());
return IntStream.range(0, streamSize).mapToObj(n -> IntStream.range(0, iterators.size())
.map(v -> iterators.get(v).next()).sum()).collect(Collectors.toList());
}
uj5u.com熱心網友回復:
形成一個輸入流物件的陣列,而不是 3 個命名變數,然后創建一個輸出串列或流,并在流長度上使用一個外回圈和一個遍歷輸入流陣列并從每個輸入流中讀取一個元素的內回圈,添加到輸出陣列元素。
類似的東西(代碼未經測驗,可能存在語法錯誤)
...
{
List<Integer> results; // NOT final, can be a stream builder instead
final List<Stream<Integer>> instrms = Stream.Of(s1, s2, s3);
Iterator<Integer>[] initers = new Iterator<Integer>[instrms.length]
// Get the iterator (not rewindable) for each stream
int i = 0;
for (Stream<Integer> instrm : instrms) {
initers[i ] = ((Iterator<Integer>)instrm::iterator);
}
// Actually loop over the stream elements, outputting one
// sum element for each element. Assumes all input streams
// are same length as the first one.
while(! initers[0].hasNext()) {
Integer res1 = 0;
for (Iterator<Integer> initer : initers) {
res1 = initer.next();
}
results.Add(res1);
}
return results; // results.build() if a stream builder
}
uj5u.com熱心網友回復:
這基本上是一個 zip 操作,其中每個流的位置元素i與其對應的另一個流中的第 i 個元素相加。
Iterator您可以通過從每個流回傳并構建一個結果來實作您所需要的,該結果Spliterator會壓縮流的迭代器回傳的每個第 i 個元素。
基本上,結果Spliterator可以建立在一個自定義的基礎上Iterator,它簡單地迭代和求和由流回傳的每個 next() 元素Iterators。
這是一個實作:
public static <T> Stream<T> zipStreams(BiFunction<? super T, ? super T, ? extends T> funZip, Stream<? extends T>... vetStreams) {
if (vetStreams == null || vetStreams.length == 0) {
return null;
}
//Creating a List of Spliterator for each given stream
List<Spliterator<? extends T>> listSliterators = new ArrayList();
for (Stream<? extends T> s : vetStreams) {
listSliterators.add(s.spliterator());
}
// Creating a final Spliterator built on the Spliterators of every stream.
// The final Spliterator will be implemented with a custom Iterator which basically iterates every stream's iterator and "sum" their elements.
// The sum is actually performed via the given function funZip to keep the data type generic.
//Retrieving the common characteristics from the streams' spliterators
int commonCharacteristics = listSliterators.get(0).characteristics();
for (Spliterator spliterator : listSliterators) {
commonCharacteristics = commonCharacteristics & spliterator.characteristics();
}
//zipping two streams loses the distinct property
commonCharacteristics = commonCharacteristics & ~(Spliterator.DISTINCT);
//Retrieving the common minimum size in case streams of different lengths have been passed.
//This parameter is necessary to instantiate the final Spliterator and create the resulting stream.
long commonSize = -1;
if ((commonCharacteristics & Spliterator.SIZED) != 0) {
commonSize = listSliterators.stream().map(s -> s.getExactSizeIfKnown()).min(Comparator.naturalOrder()).orElse(-1L);
}
//Creating a list of iterators from the Spliterators created from the given streams
List<Iterator<? extends T>> listIterators = new ArrayList<>();
for (Spliterator spliterator : listSliterators) {
listIterators.add(Spliterators.iterator(spliterator));
}
//Creating a result iterator built on the streams' iterators
Iterator<? extends T> resIterator = new Iterator<>() {
@Override
public boolean hasNext() {
//If any iterator has not a hasNext() then false is returned; otherwise true
return listIterators.stream().anyMatch(i -> !i.hasNext()) ? false : true;
}
@Override
public T next() {
//Summing every Iterator's next() element
T n = listIterators.get(0).next();
for (int i = 1; i < listIterators.size(); i ) {
n = funZip.apply(n, listIterators.get(i).next());
}
return n;
}
};
//The Spliterator is created as parallel only if every given stream is parallel
boolean isAnyStreamParallel = Arrays.stream(vetStreams).anyMatch(s -> !s.isParallel()) ? false : true;
//Returning a stream built from a spliterator which is in turn built on the resulting iterator zipping every given streams' element
return StreamSupport.stream(Spliterators.spliterator(resIterator, commonSize, commonCharacteristics), isAnyStreamParallel);
}
輸出
12
15
這里還有一個鏈接可以測驗不同資料型別的代碼:
https://www.jdoodle.com/iembed/v0/rQr
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/486725.html
上一篇:python字串和內部條件
