2009-11-18 18 views
1

修改集合我遍历,以及修改的映射(其由枚举对象的现有的组创建)类似下面的:优点创建迭代时的临时缓冲器和在Java

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

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

因为它工作正常,没有缓冲区集合(只使用我的原始集合),我想知道是否有一种方法建议超过oter和为什么。

非常感谢您对此的最佳实践的任何建议。

+1

你的问题基本上是,“我加2和3就是这样:2 + 3 + 2 - 3。它的工作原理,但我不清楚增加和减少3的好处。” 你真的没有解释为什么这似乎是需要在第一个地方。你为什么不直接修改availableIngredients?我假设,从“玩具程序”的外观来看,你并不关心多线程? 顺便说一下,看起来你可以从google-collections.googlecode.com中使用EnumMultiset而不是Map来获益,但是如果这是一个学校项目,我想这是无关紧要的。 – 2009-11-18 17:20:57

回答

1

您并不需要buffer地图。您可以改用availableIngredients进行操作。一切都会好起来的。没有无用的开销。

+0

那么,前面的答案中提到的ConcurrentModificationException风险是什么? – denchr 2009-11-18 17:18:04

+0

它发生了什么? – 2009-11-18 17:18:25

+0

我没有发生,我只是想明白使用复制/缓冲区集合的好处。我想在这种情况下,没有优势,因为没有并发功能 – denchr 2009-11-18 17:22:03

1

这就像要避免ConcurrentModificationException s。

您不能在迭代集合的同时修改集合,否则会引发这样的异常。你发布的是一个非常罕见的用于处理这个问题的习惯用法 - 以某种方式获取该集合的副本,然后在修改另一个副本时迭代一个副本。

这可能发生,即使在单线程代码 - 例如,这样的事情会抛出异常的集合至少有两个要素:

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

的替代形式,可能更高性能,方式处理这是明确声明迭代器(而不是使用foreach循环),然后在适当的情况下使用迭代器的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()); 
    }   
} 

如你所料,

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

+2

我没有得到。迭代时他在哪里修改集合? – 2009-11-18 17:14:23

+0

谢谢,即使并发根本没有解决,集合还是会抛出ConcurrentModificationException异常吗?我的意思是,我的应用程序不是多线程的,所以不能有多个线程在同一集合上同时运行 – denchr 2009-11-18 17:15:08

+0

我认为这里有一些困惑。该方法在sandwichType.getIngredients()上进行迭代并修改availableIngredients。也许早期版本正在迭代和修改相同的集合,但我认为这不是必要的。 – 2009-11-18 17:22:01

0

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

对于情况下,利用一个缓冲区是一个好主意,取代了原来可以这样做更容易:

availableIngredients = buffer; 

没有必要更新原来的集合,只要使用新版本从现在开始。