在迭代和修改java中的集合时创建临时缓冲区的优点

时间:2009-11-18 17:08:07

标签: java collections enums map

我正在迭代,并修改地图(从现有的枚举对象组创建),如下所示:

public class Dispenser {
   private Map<Ingredient, Integer> availableIngredients = 
         new EnumMap<Ingredient, Integer>(Ingredient.class);
   public void orderSandwich(SandwichType sandwichType) {
      Map<Ingredient, Integer> buffer = 
            new EnumMap<Ingredient, Integer>(availableIngredients);
      for (Map.Entry<Ingredient, Integer> entry : 
            sandwichType.getIngredients().entrySet()) {
         Integer currentUnits = buffer.get(entry.getKey());
         buffer.put(entry.getKey(), currentUnits - entry.getValue());
      }     
      availableIngredients.clear();
      availableIngredients.putAll(buffer);
   }
}

我想询问在这种情况下是否需要临时的,方法本地的,缓冲区集合。我的意思是,它工作正常,但不确定它的好处。我必须清除我的原始集合并将其替换为缓冲区集合的内容,这基本上是在循环中修改的实际映射。

因为它在没有缓冲区集合的情况下工作正常(仅使用我的原始集合),我想知道是否建议采用一种方法而不是其他方法。

非常感谢有关此方面的最佳做法的任何建议。

3 个答案:

答案 0 :(得分:1)

这就像避免ConcurrentModificationException s。

在迭代时不能修改集合,否则会抛出此类异常。你发布的是一个非常罕见的习惯用法 - 以这种或那种方式获取集合的副本,然后你可以在修改另一个时迭代一个。

即使在单线程代码中也会发生这种情况 - 例如,类似这样的事情会在具有至少两个元素的集合上抛出该异常:

for (Object o : myCollection)
{
   myCollection.remove(o);
}

另一种可能更高效的方法是明确声明Iterator(而不是使用 foreach 循环),然后使用Iterator的remove方法,如果合适的话。 (但这不适用于您的情况,因为您正在重新映射而不是删除元素。)

编辑:虽然在反思时,availableIngredients地图没有被循环,所以可以直接修改。事实证明你是对的。 :-)可能这是前重构的遗留问题,但可以用

代替
public void orderSandwich(SandwichType sandwichType) {
  for (Map.Entry<Ingredient, Integer> entry : 
        sandwichType.getIngredients().entrySet()) {
     Integer currentUnits = availableIngredients.get(entry.getKey());
     availableIngredients.put(entry.getKey(), currentUnits - entry.getValue());
  }         
}

正如你无疑期待的那样。

我想到的一个想法是,通过减少冲突更新的窗口,这可能也是一种错误的尝试,使并发问题“不太可能”。但是,误导是因为线程安全是绝对的;使展示数据竞赛的可能性降低十倍并不是一个很好的时间投入。它仍将失败,“随机”,因此是不正确的。

答案 1 :(得分:1)

您不需要buffer地图。您可以改为availableIngredients。一切都会好起来的。没有任何无用的开销。

答案 2 :(得分:0)

在这种情况下,您不必使用缓冲区,因为在修改sandwichType.getIngredients()时迭代集合availableIngredients,这些是单独的集合。

对于使用缓冲区是个好主意的情况,可以更容易地替换原始文件:

  availableIngredients = buffer;

无需更新原始集合,从现在开始使用新版本就足够了。