我可以用某种方式用Java8流分析先前和/或下一个元素吗?
例如,我可以计算相同的相邻数字吗?
public class Merge {
public static void main(String[] args) {
Stream<Integer> stream = Stream.of(0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1);
// How to get 3, 2, 2, 4 from above
}
}
答案 0 :(得分:5)
如果您希望它是懒惰的,则必须通过Stream.iterator()
或Stream.spliterator()
转义Stream API。
否则,执行此操作的方法是使用自定义收集器调用终端操作Stream.collect(Collector)
,该收集器将使用整个流。
@Test
public void test() {
Stream<Integer> input = Stream.of(0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1);
UniqCountSpliterator uniqCountSpliterator = new UniqCountSpliterator(input.spliterator());
long[] output = uniqCountSpliterator.stream()
.toArray();
long[] expected = {3, 2, 2, 4};
assertArrayEquals(expected, output);
}
import java.util.Spliterator;
import java.util.function.LongConsumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class UniqCountSpliterator implements Spliterator.OfLong {
private Spliterator wrapped;
private long count;
private Object previous;
private Object current;
public UniqCountSpliterator(Spliterator wrapped) {
this.wrapped = wrapped;
}
public LongStream stream() {
return StreamSupport.longStream(this, false);
}
@Override
public OfLong trySplit() {
return null;
}
@Override
public long estimateSize() {
return Long.MAX_VALUE;
}
@Override
public int characteristics() {
return NONNULL | IMMUTABLE;
}
@Override
public boolean tryAdvance(LongConsumer action) {
while (wrapped.tryAdvance(next -> current = next) && (null == previous || current.equals(previous))) {
count++;
previous = current;
}
if (previous == null) {
return false;
}
action.accept(count);
count = 1;
previous = null;
return true;
}
}
答案 1 :(得分:0)
您可以几乎使用flatMap。它适用于无限流,有限流我没有看到从它内部检测流的结束的方法。
Stream<Integer> stream = Stream.of(0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1);
Stream<Integer> flatMap = stream.flatMap(new Function<Integer, Stream<Integer>>() {
Integer prev = null;
int count;
public java.util.stream.Stream<Integer> apply(Integer i) {
if ( i.equals(prev)) {
count++;
return Stream.empty();
} else {
int c = count;
count = 1;
prev = i;
if ( c > 0 ) {
return Stream.of(c);
} else {
return Stream.empty();
}
}
};
});
flatMap.forEach(i -> {
System.out.println(i);
});
说,你可以从rxjava中获得更好的里程数来获得这样的东西(你可以使用Subject根据你的意愿发出值,并能够检测到流的结束)。
当然,如果你想逃离Stream界限,有很多选择,正如Christoffers的回答所示。
答案 2 :(得分:0)
如果你不介意两个陈述,你可以设置一个列表来填写计数,然后使用reduce
:
List<Integer> counts = new ArrayList<>();
Stream.of(0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1).reduce((i, j) -> {
if (counts.isEmpty()) {
counts.add(1);
}
if (j == i) {
int index = counts.size() - 1;
counts.set(index, counts.get(index) + 1);
} else {
counts.add(1);
}
return j;
});