在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);
}
}
我想问一下临时的、方法本地的、缓冲区集合在此是否是必要的案件。我的意思是,它工作得很好,但不确定它的好处。我必须清除原始集合并将其替换为缓冲区集合的内容,这基本上是在循环内修改的实际映射。
因为,它在没有缓冲区集合的情况下工作得很好(仅使用我的原始集合),我想知道是否推荐一种方法而不是其他方法以及为什么。
非常感谢您提供有关这方面最佳实践的建议。
I am iterating over, and modifying a map (which is created from an existing group of enum objects) like the following:
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);
}
}
I wanted to ask if the temporary, method-local, buffer collection is necessary in this case. I mean, it works fine as it is but not sure its benefits. I have to clear my original collection and replace it with the contents of the buffer collection, which is basically the actual map that is modified within the loop.
Since, it works fine without the buffer collection (using only my original collection), I was wondering if one approach is recommended over the oter and why.
Many thanks for any advice on best practices on this.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您不需要
buffer
地图。您可以改为对availableIngredients
进行操作。一切都会好起来的。没有无用的开销。You don't need
buffer
map. You can operate onavailableIngredients
instead. Everything will be fine. With no useless overhead.就像避免 ConcurrentModificationException 一样。
迭代集合时不能修改集合,否则将引发此类异常。您发布的内容是处理此问题的一种并不罕见的习惯用法 - 以一种或另一种方式获取集合的副本,然后您可以在修改另一个的同时迭代一个集合。
即使在单线程代码中也可能发生这种情况 - 例如,类似这样的事情会在至少包含两个元素的集合上抛出该异常:
另一种可能更高效的处理此问题的方法是显式声明迭代器(而不是而不是使用 foreach 循环),然后使用迭代器的 删除 方法(如果适用)。 (但这不适用于您的情况,因为您正在重新映射而不是删除元素)。
编辑:不过,经过反思,
availableIngredients
映射没有被循环,因此可以直接修改。事实证明,你感到困惑是对的。 :-) 这很可能是以前重构的痕迹,但毫无疑问它可以被替换为您所期望的。
我刚刚想到的一个想法是,这也可能是一种误导性的尝试,旨在通过减少冲突更新的窗口来“降低”并发问题的可能性。但这是误导,因为线程安全是绝对的;将出现数据竞争的可能性降低十倍并不是一项好的时间投资。它仍然会“随机”失败,因此是不正确的。
It's like to avoid ConcurrentModificationExceptions.
You cannot modify a collection while iterating over it, or such an exception will be thrown. What you have posted is a not-uncommon idiom for dealing with this - take a copy of the collection in one way or another, then you can iterate over one while modifying the other.
This can happen even in single-threaded code - for example, something like this will throw that exception on a collection with at least two elements:
An alternative, and likely more performant, way to deal with this is to explicitly declare the Iterator (rather than using the foreach loop), and then use the Iterator's remove method, if appropriate. (This doesn't apply to your case, though, because you're remapping rather than removing elements).
Edit: On reflection though, the
availableIngredients
map isn't being looped over so can simply be modified directly. You're right to be confused as it turns out. :-) Chances are this is vestigial from former refactoring, but it can be replaced byas you no doubt expect.
A thought that's just come to mind is that this might also have been a misguided attempt to make concurrency problems "less likely", by reducing the window of conflicting updates. Misguided, though, because threadsafety is absolute; making something ten times less likely to exhibit data races is not a good investment of time. It will still fail, "randomly", and is thus incorrect.
在这种情况下,您不必使用缓冲区,因为您在修改
availableIngredients
时迭代集合sandwichType.getIngredients()
时,这些是单独的集合。对于使用缓冲区是个好主意的情况,替换原始集合可以更容易地完成,如下所示:
不需要更新原始集合,从现在开始使用新版本就足够了。
You do not have to use a buffer in this case, as you iterate over the collection
sandwichType.getIngredients()
while modifyingavailableIngredients
these are separate collections.For the cases where using a buffer is a good idea, replacing the original can be done easier like this:
there is no need to update the original collection, it suffices to use the new version from now on.