有没有一种简洁的方法来迭代Java 8中的索引流?

2022-08-31 04:25:31

有没有一种简洁的方法来迭代流,同时可以访问流中的索引?

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

有没有更简洁的方法?

此外,拉链似乎已经移动或被删除...


答案 1

最干净的方法是从索引流开始:

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

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


答案 2

Java 8 流 API 缺乏获取流元素索引的功能,以及将流压缩在一起的功能。这是不幸的,因为它使某些应用程序(如 LINQ 挑战)比其他应用程序更加困难。

但是,通常有解决方法。通常,这可以通过使用整数范围“驱动”流,并利用原始元素通常位于数组或索引可访问的集合中这一事实来完成。例如,挑战 2 问题可以通过以下方式解决:

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

List<String> nameList =
    IntStream.range(0, names.length)
        .filter(i -> names[i].length() <= i)
        .mapToObj(i -> names[i])
        .collect(toList());

正如我上面提到的,这利用了数据源(names数组)可直接索引的事实。如果不是这样,这种技术将不起作用。

我承认这并不能满足挑战2的意图。尽管如此,它确实合理有效地解决了这个问题。

编辑

我之前的代码示例用于融合筛选器和映射操作,但这很麻烦,并且没有提供任何优势。我已经根据Holger的评论更新了示例。flatMap


推荐