【问题标题】:Whats the replacement of For-Each loop for filtering?过滤的 For-Each 循环的替换是什么?
【发布时间】:2010-12-03 01:13:57
【问题描述】:

虽然 for-each 循环有很多优点,但问题是,当您想要过滤(过滤意味着从列表中删除元素)列表时它不起作用,您能否请任何替换,因为即使遍历索引也不是不错的选择..

【问题讨论】:

    标签: java list foreach


    【解决方案1】:

    如果你和我一样,不喜欢在迭代它的元素时修改集合,或者如果迭代器没有提供删除的实现,你可以使用临时集合来收集你想要删除的元素.是的,是的,与修改迭代器相比,它的效率较低,但对我来说,更清楚地了解发生了什么:

    List<Object> data = getListFromSomewhere();
    List<Object> filter = new ArrayList<Object>();
    
    // create Filter
    for (Object item: data) {
      if (throwAway(item)) {
        filter.add(item);
      }
    }
    
    // use Filter
    for (Object item:filter) {
      data.remove(item);
    }
    
    filter.clear();
    filter = null;
    

    【讨论】:

      【解决方案2】:

      我已经成功使用了

      filter(java.util.Collection collection, Predicate predicate) 
      

      commons 集合中 CollectionUtils 的方法。

      http://commons.apache.org/collections/api-2.1.1/org/apache/commons/collections/CollectionUtils.html#filter(java.util.Collection,%20org.apache.commons.collections.Predicate)

      【讨论】:

      • 能否请您在这里详细说明您的答案,这对我以及将来对其他人都非常有用
      • 这些接口没有通用化真是太烦人了。这就是我喜欢谷歌收藏的原因
      【解决方案3】:

      “过滤”是什么意思?从列表中删除某些元素?如果是这样,您可以使用iterator

      for(Iterator<MyElement> it = list.iterator(); it.hasNext(); ) {
          MyElement element = it.next();
          if (some condition) {
            it.remove();
          }
      }
      

      更新(基于 cmets):

      考虑以下示例来说明迭代器的工作原理。假设我们有一个包含“A”和“B”的列表:

      A A B B A

      我们想删除所有那些讨厌的Bs。因此,使用上面的循环,代码将如下工作:

      1. 有下一个()?是的。下一个()。 element 指向第一个 A。
      2. 有下一个()?是的。下一个()。 element 指向第二个 A。
      3. 有下一个()?是的。下一个()。 element 指向第一个 B.remove()。迭代器计数器不会改变,它仍然指向 B 所在的位置(从技术上讲,这并不完全正确,但从逻辑上讲,它就是这样工作的)。如果你现在再次调用 remove(),你会得到一个异常(因为列表元素不再存在)。
      4. 有下一个()?是的。下一个()。 element指向第2个B,其余同#3
      5. 有下一个()?是的。下一个()。 element 指向第 3 个 A。
      6. 有下一个()?不,我们已经完成了。列表现在有 3 个元素。

      更新 #2remove() 操作在迭代器上确实是可选的 - 但只是因为它在基础集合上是可选的。这里的底线是 - 如果您的集合支持它(Java Collection Framework 中的所有集合都支持),那么迭代器也支持。如果您的收藏不支持它,那么您无论如何都不走运。

      【讨论】:

      • @ChssPly76:谢谢回复,是的,我的意思是一样的,但是当我删除一个元素时它不会影响迭代,在迭代器是来自同一个实例。我想从 List(list) 中删除元素
      • 我很确定在迭代器上调用 remove() 会影响底层列表。否则真的没有意义。
      • 它肯定会从底层列表中删除该元素。但是,它不会影响迭代 - remove() 在“当前”元素(从最后一个 next() 方法调用返回的元素)上调用,并且不会更改“当前”元素。下次调用 next() 将返回列表中的以下元素。
      • 你能详细说明你的答案吗,有些我不清楚。请帮助我
      • 不幸的是,Iterator#remove() 的 javadoc 有这两个词,大部分都被忽视了; “可选操作”。甚至没有办法检查是否只能尝试使用,这意味着 .remove() 根本不可靠,除非您 110% 确定它有效,否则不应使用。
      【解决方案4】:

      ChssPly76 的答案是正确的方法 - 但我对您在“遍历索引不是一个好的选择”背后的想法很感兴趣。在许多情况下——特别是常见的情况是ArrayList——它非常有效。 (事实上​​,在 arraylist 的情况下,我相信重复调用 get(i++) 比使用迭代器要快一些,尽管还远远不足以牺牲可读性)。

      一般来说,如果有问题的对象实现了java.util.RandomAccess,那么通过索引访问顺序元素的速度应该与使用迭代器的速度大致相同。如果不是(例如LinkedList 将是一个很好的反例),那么您是对的;但不要立即放弃这个选项。

      【讨论】:

      • 是的,但这不会改变事情 - 如果在这种特定情况下您的列表是 ArrayList(或类似的),那么基于索引的访问将会快速(甚至比使用迭代器快一点),所以这是一个不错的选择。如果这是一个通用库方法,它可能采用各种列表,而您无法控制其实现 - 那么假设是不好的(尽管您仍然可以检查接口实现)。
      • @dtsazza - 关于性能(+1),您是完全正确的。但是,索引循环的问题是,如果您需要从集合中删除某些元素,则非常容易将其搞砸。在 for 循环中修改索引只是恶业;而while循环需要额外的外部计数器,感觉不太干净。
      猜你喜欢
      • 1970-01-01
      • 2011-12-05
      • 1970-01-01
      • 2022-01-23
      • 1970-01-01
      • 2015-11-12
      • 1970-01-01
      • 2015-04-08
      • 2021-10-01
      相关资源
      最近更新 更多