Search code examples
javajava-8java-stream

Is there a concise way to iterate over a stream with indices in Java 8?


Is there a concise way to iterate over a stream whilst having access to the index in the stream?

String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};

List<String> nameList;
Stream<Integer> indices = intRange(1, names.length).boxed();
nameList = zip(indices, stream(names), SimpleEntry::new)
        .filter(e -> e.getValue().length() <= e.getKey())
        .map(Entry::getValue)
        .collect(toList());

which seems rather disappointing compared to the LINQ example given there

string[] names = { "Sam", "Pamela", "Dave", "Pascal", "Erik" };
var nameList = names.Where((c, index) => c.Length <= index + 1).ToList();

Is there a more concise way?

Further it seems the zip has either moved or been removed...


Solution

  • The cleanest way is to start from a stream of indices:

    String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"};
    IntStream.range(0, names.length)
             .filter(i -> names[i].length() <= i)
             .mapToObj(i -> names[i])
             .collect(Collectors.toList());
    

    The resulting list contains "Erik" only.


    One alternative which looks more familiar when you are used to for loops would be to maintain an ad hoc counter using a mutable object, for example an AtomicInteger:

    String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"};
    AtomicInteger index = new AtomicInteger();
    List<String> list = Arrays.stream(names)
                              .filter(n -> n.length() <= index.incrementAndGet())
                              .collect(Collectors.toList());
    

    Note that using the latter method on a parallel stream could break as the items would not necesarily be processed "in order".