【问题标题】:Changing iterated item's reference in enhanced for loop在增强的 for 循环中更改迭代项的引用
【发布时间】:2014-08-06 17:06:38
【问题描述】:

为什么在 Java 中使用增强的 for 循环,我不能更改我通过引用迭代的内容?不是迭代器,而是我正在迭代的数据类型的字段。

我想遍历Maps(准确地说是MultiValueMaps)的列表,并检查地图的值是否可以更新。

我的问题是更改它的实际逻辑嵌套在基于地图的其他循环(下面的大纲代码)的深处,如果没有ConcurrentModificationExceptions,我找不到进行替换的方法抛出。原因是当我得到ConcurrentModificationException 处理一个循环时,由于代码嵌套很深,它会破坏另一个循环。

在主循环中,遍历地图,我想创建一个新地图,我只是添加东西,最后用它替换迭代地图。但这不起作用。

为什么会这样,我可以做些什么或研究什么来帮助我解决这个问题?

伪代码

for( Map loopMap : listOfMaps ) {
    Map tempMap = new HashMap();

    Set entrySet = loopMap.entrySet();
    Iterator<MultiValueMap.Entry> iter = entrySet.iterator();

    while( iter.hasNext() ) {
        Map.Entry entry = (Entry) iter.next();

        if( entry.getValue() == "test" ) {
            tempMap.put( entry.getKey(), "new value" );
        }
    }
    loopMap = tempMap;
}

为什么我不能改变地图所指的东西,即使我能够改变所指的价值?

【问题讨论】:

  • 如果你能给出一个简短但完整的例子会有所帮助 - 你提供了很多我们不需要的东西的伪代码,而我相信你可以演示真正的问题在我们可以编译的大约 10 行真实代码中......
  • 改用常规的 for 循环?
  • @JonSkeet 添加了实际代码,尽可能将其清理为只有与问题相关的代码
  • @DavidPostill 我知道异常是如何工作的,并且发布的内容最初对我有帮助,但真正的问题是我有嵌套循环,它们都被编辑了,我不知道如何保留异常从被抛出两个

标签: java for-loop reference


【解决方案1】:

因为您使用的是引用值的副本,而不是直接引用。请注意,增强的 for 循环将在幕后为您使用 Iterator。它看起来像这样:

for (Iterator<YourClass> it = yourCollection.iterator(); it.hasNext(); ) {
    YourClass yourClass = it.next();
    //do whatever...
    //and looks like you change yourClass value here
    //which is a local variable, not the real object reference inside your collection

    //also, you cannot add/remove an element here to the collection being iterated
    //because this will throw a ConcurrentModificationException
}

解决方案是将所有新元素添加到新集合中(ListMapSet 或您正在使用的任何东西),并且根据您的所有逻辑,用您的新集合替换当前集合.这将是伪代码(我无法提供更准确的代码,因为您没有提供足够的信息):

Collection<YourData> newCollection = ... //initialize it
for (... : currentCollection) {
     if (...) {
         YourData yourData = new YourData();
         //fill yourData variable
         //...
         //add it into newCollection
         newCollection.add(yourData);
     }
}
//roughly, you will end doing this or similar
currentCollection = newCollection;

对于您的新示例,要修改的元素将是 Map,因此将新的键值对填充到新的 Map 中,最后遍历此映射以替换您的元素当前地图:

Map<YourKey, YourValue> newMap = new HashMap<>(); //the implementation doesn't really matter for this one
for(Map.EntrySet<YourKey, YourValue> entrySet : currentMap.entrySet()) {
      if (...) {
          YourValue newValue = ...;
          //compute the newValue data
          //...
          newMap.put(entrySet.getKey(), newValue); 
      }
}
for (Map.EntrySet<YourKey, YourValue> entrySet : newMap.entrySet()) {
    currentMap.put(entrySet.getKey(), entrySet.getValue());
}

【讨论】:

  • 我曾考虑过创建一个临时 List&lt;Map&gt;,但我正在处理的数据可能会变得非常大,而且我不想拥有完整的副本。是否可以在地图的基础上进行?这样我只需要一张额外地图的内存,而不是全部。
  • @FreakyDan 唯一的额外内存将是新地图和计算的新值。请注意,其他对象引用在堆中一次,无论它们存储了多少集合。
猜你喜欢
  • 2015-11-03
  • 2016-01-05
  • 2010-09-22
  • 2020-08-07
  • 2014-11-16
  • 1970-01-01
  • 2012-08-07
  • 2014-02-13
  • 2014-02-23
相关资源
最近更新 更多