是否有可能“合并”Java8流的元素?

时间:2016-08-30 14:07:30

标签: java java-8 java-stream

我可以用某种方式用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

   }
}

3 个答案:

答案 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;
});