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

后端 未结 22 2167
天命终不由人
天命终不由人 2020-11-22 01:42

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\",          


        
相关标签:
22条回答
  • 2020-11-22 01:47

    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".

    0 讨论(0)
  • 2020-11-22 01:49

    This question (Stream Way to get index of first element matching boolean) has marked the current question as a duplicate, so I can not answer it there; I am answering it here.

    Here is a generic solution to get the matching index that does not require an external library.

    If you have a list.

    public static <T> int indexOf(List<T> items, Predicate<T> matches) {
            return IntStream.range(0, items.size())
                    .filter(index -> matches.test(items.get(index)))
                    .findFirst().orElse(-1);
    }
    

    And call it like this:

    int index = indexOf(myList, item->item.getId()==100);
    

    And if using a collection, try this one.

       public static <T> int indexOf(Collection<T> items, Predicate<T> matches) {
            int index = -1;
            Iterator<T> it = items.iterator();
            while (it.hasNext()) {
                index++;
                if (matches.test(it.next())) {
                    return index;
                }
            }
            return -1;
        }
    
    0 讨论(0)
  • 2020-11-22 01:52

    If you happen to use Vavr(formerly known as Javaslang), you can leverage the dedicated method:

    Stream.of("A", "B", "C")
      .zipWithIndex();
    

    If we print out the content, we will see something interesting:

    Stream((A, 0), ?)
    

    This is because Streams are lazy and we have no clue about next items in the stream.

    0 讨论(0)
  • 2020-11-22 01:53

    One possible way is to index each element on the flow:

    AtomicInteger index = new AtomicInteger();
    Stream.of(names)
      .map(e->new Object() { String n=e; public i=index.getAndIncrement(); })
      .filter(o->o.n.length()<=o.i) // or do whatever you want with pairs...
      .forEach(o->System.out.println("idx:"+o.i+" nam:"+o.n));
    

    Using an anonymous class along a stream is not well-used while being very useful.

    0 讨论(0)
  • 2020-11-22 01:55

    Just for completeness here's the solution involving my StreamEx library:

    String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
    EntryStream.of(names)
        .filterKeyValue((idx, str) -> str.length() <= idx+1)
        .values().toList();
    

    Here we create an EntryStream<Integer, String> which extends Stream<Entry<Integer, String>> and adds some specific operations like filterKeyValue or values. Also toList() shortcut is used.

    0 讨论(0)
  • 2020-11-22 01:55

    If you need the index in the forEach then this provides a way.

      public class IndexedValue {
    
        private final int    index;
        private final Object value;
    
        public IndexedValue(final int index, final Object value) { 
            this.index = index;
            this.value = value;
        }
    
        public int getIndex() {
            return index;
        }
    
        public Object getValue() {
            return value;
        }
    }
    

    Then use it as follows.

    @Test
    public void withIndex() {
        final List<String> list = Arrays.asList("a", "b");
        IntStream.range(0, list.size())
                 .mapToObj(index -> new IndexedValue(index, list.get(index)))
                 .forEach(indexValue -> {
                     System.out.println(String.format("%d, %s",
                                                      indexValue.getIndex(),
                                                      indexValue.getValue().toString()));
                 });
    }
    
    0 讨论(0)
提交回复
热议问题