是否有一种简洁的方法在流上迭代,同时访问流中的索引?

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());

与这里给出的LINQ示例相比,这似乎相当令人失望

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

有更简洁的方式吗?

此外,似乎拉链已经移动或被拆除…


当前回答

下面是标准Java的解决方案:

在线解决方案:

Arrays.stream("zero,one,two,three,four".split(","))
        .map(new Function<String, Map.Entry<Integer, String>>() {
            int index;

            @Override
            public Map.Entry<Integer, String> apply(String s) {
                return Map.entry(index++, s);
            }
        })
        .forEach(System.out::println);

更可读的解决方案与实用方法:

static <T> Function<T, Map.Entry<Integer, T>> mapWithIntIndex() {
    return new Function<T, Map.Entry<Integer, T>>() {
        int index;

        @Override
        public Map.Entry<Integer, T> apply(T t) {
            return Map.entry(index++, t);
        }
    };
}

...
Arrays.stream("zero,one,two,three,four".split(","))
        .map(mapWithIntIndex())
        .forEach(System.out::println);

其他回答

自番石榴21起,就可以使用了

Streams.mapWithIndex()

示例(来自官方文档):

Streams.mapWithIndex(
    Stream.of("a", "b", "c"),
    (str, index) -> str + ":" + index)
) // will return Stream.of("a:0", "b:1", "c:2")

与https://github.com/poetix/protonpack 你可以做到,zip:

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

List<String> nameList;
Stream<Integer> indices = IntStream.range(0, names.length).boxed(); 

nameList = StreamUtils.zip(indices, stream(names),SimpleEntry::new)
        .filter(e -> e.getValue().length() <= e.getKey()).map(Entry::getValue).collect(toList());                   

System.out.println(nameList);

最简洁的方法是从一系列指数开始:

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());

结果列表只包含“Erik”。


当你习惯for循环时,另一种看起来更熟悉的方法是使用可变对象维护一个临时计数器,例如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());

注意,在并行流上使用后一种方法可能会中断,因为项目不一定会“按顺序”处理。

使用列表,你可以尝试一下

List<String> strings = new ArrayList<>(Arrays.asList("First", "Second", "Third", "Fourth", "Fifth")); // An example list of Strings
strings.stream() // Turn the list into a Stream
    .collect(HashMap::new, (h, o) -> h.put(h.size(), o), (h, o) -> {}) // Create a map of the index to the object
        .forEach((i, o) -> { // Now we can use a BiConsumer forEach!
            System.out.println(String.format("%d => %s", i, o));
        });

输出:

0 => First
1 => Second
2 => Third
3 => Fourth
4 => Fifth

如果你试图获得一个基于谓词的索引,试试这个:

如果你只关心第一个索引:

OptionalInt index = IntStream.range(0, list.size())
    .filter(i -> list.get(i) == 3)
    .findFirst();

或者如果你想找到多个索引:

IntStream.range(0, list.size())
   .filter(i -> list.get(i) == 3)
   .collect(Collectors.toList());

添加.orElse (1);如果你想返回一个没有找到的值。