在 Java 8 中是否有一种简洁的方法来迭代带有索引的流?

新手上路,请多包涵

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

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

有没有更简洁的方法?

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

原文由 Graeme Moss 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 518
2 个回答

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

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

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

原文由 assylias 发布,翻译遵循 CC BY-SA 3.0 许可协议

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

正如我上面提到的,这利用了数据源(名称数组)可直接索引的事实。否则,此技术将无法工作。

我承认这不符合挑战 2 的意图。尽管如此,它确实相当有效地解决了问题。

编辑

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

原文由 Stuart Marks 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题