Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Collect pairs from a stream

I have a stream of objects like this:

"0", "1", "2", "3", "4", "5",

How can I transform it to stream of pairs :

{ new Pair("0", "1"), new Pair("2", "3"), new Pair("4", "5")}.

The stream size is unknown. I am reading data from a file that might be big. I have only iterator to collection and I transform this iterator to stream using spliterator. I know that here is a answer for processing adjacent pairs with StreamEx : Collect successive pairs from a stream Can this be done in java or StreamEx ? Thanks

like image 221
niemar Avatar asked Feb 28 '18 10:02

niemar


People also ask

How do you get one element from a stream?

To get the first element, you can use the reduce() method to ignore the second element, repeatedly, till there is no second element. This reduces the set of elements in a Stream to a single element, which is first. Hence the only single element will be remain in the stream which is the first element.


2 Answers

It's not a natural fit but you can do

List input = ...
List<Pair> pairs = IntStream.range(0, input.size() / 2)
                            .map(i -> i * 2)
                            .mapToObj(i -> new Pair(input.get(i), input.get(i + 1)))
                            .collect(Collectors.toList());

To create Pairs as you go in a stream you need a stateful lambdas which should be generally avoided but can be done. Note: this will only works if the stream is single threaded. i.e. not parallel.

Stream<?> stream = 
assert !stream.isParallel();
Object[] last = { null };
List<Pair> pairs = stream.map(a -> {
        if (last[0] == null) {
            last[0] = a;
            return null;
        } else {
            Object t = last[0];
            last[0] = null;
            return new Pair(t, a);
        }
     }).filter(p -> p != null)
       .collect(Collectors.toList());
assert last[0] == null; // to check for an even number input.
like image 118
Peter Lawrey Avatar answered Sep 20 '22 17:09

Peter Lawrey


Assuming there is a Pair with left, right and getters and a constructor:

 static class Paired<T> extends AbstractSpliterator<Pair<T>> {

    private List<T> list = new ArrayList<>(2);

    private final Iterator<T> iter;

    public Paired(Iterator<T> iter) {
        super(Long.MAX_VALUE, 0);
        this.iter = iter;
    }

    @Override
    public boolean tryAdvance(Consumer<? super Pair<T>> consumer) {
        getBothIfPossible(iter);
        if (list.size() == 2) {
            consumer.accept(new Pair<>(list.remove(0), list.remove(0)));
            return true;
        }
        return false;
    }

    private void getBothIfPossible(Iterator<T> iter) {
        while (iter.hasNext() && list.size() < 2) {
            list.add(iter.next());
        }
    }

}

Usage would be:

 Iterator<Integer> iterator = List.of(1, 2, 3, 4, 5).iterator();
 Paired<Integer> p = new Paired<>(iterator);
 StreamSupport.stream(p, false)
            .forEach(pair -> System.out.println(pair.getLeft() + "  " + pair.getRight()));
like image 24
Eugene Avatar answered Sep 20 '22 17:09

Eugene