带有索引的Java 8 forEach [重复]

Posted

技术标签:

【中文标题】带有索引的Java 8 forEach [重复]【英文标题】:Java 8 forEach with index [duplicate] 【发布时间】:2014-05-12 15:32:18 【问题描述】:

有没有办法在 Java 8 中构建一个使用索引进行迭代的 forEach 方法?理想情况下,我想要这样的东西:

params.forEach((idx, e) -> query.bind(idx, e));

我现在能做的最好的就是:

int idx = 0;
params.forEach(e -> 
  query.bind(idx, e);
  idx++;
);

【问题讨论】:

如果合并增量 query.bind(idx++, e); 会缩短一行,但这就是我能想到的全部 另外,您不应该能够在该 lambda 中修改 idx @SotiriosDelimanolis 它实际编译 如果idx 是实例变量或其他东西,它会。如果您发布的代码在方法/构造函数主体中,则不会。 @assylias 我投票重新提出这个问题,因为我不认为它与链接的问题完全相同。链接问题的发帖人想在流处理中获取索引,而本题的重点只是在(终端)forEach方法中获取索引(基本上是为了替代传统的for循环在哪个索引是手动操作的)。我认为我们不应该阻止在这里添加更多答案。实际上,我想提供一个适合这个问题的答案,但不适用于链接的问题。 【参考方案1】:

由于您正在迭代一个可索引的集合(列表等),我认为您可以只使用元素的索引进行迭代:

IntStream.range(0, params.size())
  .forEach(idx ->
    query.bind(
      idx,
      params.get(idx)
    )
  )
;

生成的代码类似于使用经典 i++ 样式的 for 循环迭代列表,除了更容易并行化(当然,假设对 params 的并发只读访问是安全的)。

【讨论】:

@TomerCagan 您可以:将索引映射到首选顺序,如果所述顺序可以表示为函数(要反向迭代参数,请使用 IntStream.range(0, params.size( )).map(i -> params.size() - 1 - i)),或者如果特定顺序不是可以从 IntStream.range(0, params. size()),如 Stream.iterate(new int[] 0, 1, ia -> new int[] ia[1], ia[0] + ia[1]).mapToInt(ia -> ia[0]).filter(i -> i 唯一需要考虑的是,当您以这种方式迭代 LinkedList 时,您将得到 O(n^2) 而不是 O(n) 再一次,Java 及其用于简单事物的糟糕语法。 java什么时候会提供更简单的替代方案。我想知道 这个答案比int[] idx = 0 ; params.forEach(e -> query.bind(idx[0]++, e));快吗?【参考方案2】:

如果您捕获一个包含一个元素的数组,它会与 params 一起使用,该元素包含当前索引。

int[] idx =  0 ;
params.forEach(e -> query.bind(idx[0]++, e));

上面的代码假设方法 forEach 以遇到的顺序遍历元素。 Iterable 接口为所有类指定此行为,除非另有说明。显然它适用于标准库中 Iterable 的所有实现,并且将来更改此行为会破坏向后兼容性。

如果您使用 Streams 而不是 Collections/Iterables,则应使用 forEachOrdered,因为 forEach 可以同时执行,并且元素可以以不同的顺序出现。以下代码适用于顺序流和并行流:

int[] idx =  0 ;
params.stream().forEachOrdered(e -> query.bind(idx[0]++, e));

【讨论】:

最好使用AtomicInteger,而不是使用int[] 数组。这也将确保如果遇到乱序的元素,我们至少会为每个元素获得一个唯一索引。 @BrettRyan:我不同意。 AtomicInteger表达了错误的意图,效率较低,这种情况下保证顺序执行 Apache lang 库中的 MutableInt 不能正常工作吗?如果使用 forEachOrdered,它不需要是同步结构,对吗? @Skychan:MutableInt 在这种情况下没有帮助,因为它缺少一些操作。特别是像getAndIncrement. @nosid 我不认为使用int[]AtomicInteger 更能揭示意图。如果性能是一个问题,也许 for 循环会更好,因为它避免了 Stream 机制,这可能会使高度优化的 Atomic* 类相形见绌。【参考方案3】:

有一些变通方法,但没有干净/简短/甜蜜的方式来处理流,老实说,你可能会更好:

int idx = 0;
for (Param p : params) query.bind(idx++, p);

或旧样式:

for (int idx = 0; idx < params.size(); idx++) query.bind(idx, params.get(idx));

【讨论】:

迟到但完全同意。当需要索引时,普通的 for 循环就足够了。 @Vortex 更具可读性。

以上是关于带有索引的Java 8 forEach [重复]的主要内容,如果未能解决你的问题,请参考以下文章

使用java 8中的forEach(..)而不是java 5中的forEach循环的任何优势[重复]

Java 8中嵌套的forEach抛出异常[重复]

Java,如何在“for each”循环中获取当前索引/键[重复]

从foreach循环中获取当前索引[重复]

带索引的 foreach [重复]

Java 8 forEach简单例子