在迭代完成后设置 Map.Entry 值?
Set Map.Entry value AFTER iteration complete?
我可以将 Map.Entry 个对象保存在临时 Map 中,然后在迭代完成后返回并更改它们的值吗?
例如,下面分两步实现一种交易。在第 1 步中,递归处理条目映射和条目映射(因此是一棵树)。条目被处理成新值,这些值被保存到一个临时映射中,由相应的 Map.Entry 键控。如果所有条目的计算都没有异常,则第 2 步只是简单地遍历临时映射并分配相应的新值。
void performPerilousProcedure(Object val) throws Exception
{
if (processing of val fails)
throw new Exception();
}
void recurivePerilousProcedure(Map someMap, Map tmp) throws Exception
{
Iterator<Map.Entry<String,Object>> iter1;
iter1 = someMap.entrySet().iterator();
while (iter1.hasNext()) {
Map.Entry<String,Object> entry1 = iter1.next();
Object val = entry1.getValue();
if (val instanceof Map) {
recursivePerilousProcedure((Map)val, tmp);
} else {
Object newval = performPerilousProcedure(val);
// ok to use Map.Entry as key across iter?
tmp.put(entry1, newval);
}
}
}
void doit(Map<String,Object> someMap) throws Exception
{
HashMap<Map.Entry<String,Object>,Object> tmp = new HashMap();
// Try to process map of entries and maps of entries and ma ...
recursivePerilousProcedure(someMap, tmp);
// All entries success processed, now simply assign new vals
Iterator<Map.Entry<String,Object>> iter2;
iter2 = tmp.keySet().iterator();
while (iter2.hasNext()) {
Map.Entry<String,Object> entry2 = iter2.next();
Object newval = tmp.get(entry2);
entry2.setValue(newval); // commit new value
}
}
问题是:
用作 tmp
键的 Map.Entry
对象能否在迭代之外存活?
假设没有并发修改,这是对 Map 接口的有效使用吗?
如果我跟着你,你可以这样做:
for (Entry<String, Object> entry : someMap.entrySet()) {
Object newVal = performPerilousProcedure(entry.getValue());
tmp.put(entry.getKey(), newVal);
}
someMap.putAll(tmp);
documentation of Map.Entry
说的很清楚了:
These instances maintain a connection to the original, backing map. This connection to the backing map is valid only for the duration of iteration over the entry-set view. During iteration of the entry-set view, if supported by the backing map, a change to a Map.Entry
's value via the setValue
method will be visible in the backing map. The behavior of such a Map.Entry
instance is undefined outside of iteration of the map's entry-set view.
未定义的行为并不妨碍执行预期的操作,实际上,当映射为 HashMap
或 TreeMap
时,只要您不修改映射(与在条目上调用 setValue
的方式不同)。
问题是您是否要在这种实现特定行为的基础上构建您的应用程序,虽然长期存在,但尚未明确指定。
还有一个问题是您将这些条目用作另一个映射中的键。条目的相等性由键和值的组合决定,而不是由关联的映射决定。换句话说,如果不同映射的两个条目恰好具有相同的键和值,则会发生冲突,并且尝试放置另一个条目将保留已经存在的条目,但将其与不适用于该条目的新值相关联(或更精确,不适用于此目标地图)。
更糟糕的是,当条目实例已经用作键时,您正在更改相等性,方法是对它们调用 setValue
,which is definitely illegal 并将 HashMap
变成一个不一致状态:
Note: great care must be exercised if mutable objects are used as map keys. The behavior of a map is not specified if the value of an object is changed in a manner that affects equals
comparisons while the object is a key in the map.
如果您要做的只是迭代 tmp
一次,则无需使用地图,因为不需要查找操作。 List<Map.Entry<Key,Value>>
可以代替 Map<Key,Value>
,以保持可以迭代的键和值的组合。事实上,任何能够保存两个值的对象都可以用来代替 Map.Entry<Key,Value>
。如果用一个能够保存三个值的对象替换它,您可以跟踪目标 Map
、键和新值,并在最后执行一个简单的 put
。
所以第一种方法看起来像
void recursivePerilousProcedure(Map<String,Object> someMap,
List<Map.Entry<Map.Entry<String,Object>,Object>> tmp)
throws Exception {
for(Map.Entry<String,Object> entry1: someMap.entrySet()) {
Object val = entry1.getValue();
if (val instanceof Map) {
recursivePerilousProcedure((Map<String,Object>)val, tmp);
} else {
Object newval = performPerilousProcedure(val);
tmp.add(new AbstractMap.SimpleEntry<>(entry1, newval));
}
}
}
void doit(Map<String,Object> someMap) throws Exception {
List<Map.Entry<Map.Entry<String,Object>,Object>> tmp = new ArrayList<>();
// Try to process map of entries and record new values
recursivePerilousProcedure(someMap, tmp);
// All entries success processed, now simply assign new vals
// relies on Map implementation detail
for(Map.Entry<Map.Entry<String,Object>,Object> entry: tmp) {
Object newval = entry.getValue();
entry.getKey().setValue(newval); // assign new value
}
}
它依赖于实现细节,但有一点优势,即调用 setValue
时不需要地图查找。或者干净的解决方案:
record NewValue(Map<String,Object> target, String key, Object newValue) {
void apply() {
target.put(key, newValue);
}
}
void recursivePerilousProcedure(Map<String,Object> someMap,
List<NewValue> tmp) throws Exception {
for(Map.Entry<String,Object> entry1: someMap.entrySet()) {
Object val = entry1.getValue();
if (val instanceof Map) {
recursivePerilousProcedure((Map<String,Object>)val, tmp);
} else {
Object newval = performPerilousProcedure(val);
tmp.add(new NewValue(someMap, entry1.getKey(), newval));
}
}
}
void doit(Map<String,Object> someMap) throws Exception {
List<NewValue> tmp = new ArrayList<>();
// Try to process map of entries and record new values
recursivePerilousProcedure(someMap, tmp);
// All entries success processed, now simply assign new vals
// assign all new values, cleanly
tmp.forEach(NewValue::apply);
}
根据您的应用,可能还有第三种选择。只需在递归处理过程中构建一个匹配预期目标结构的新映射,在没有错误发生时替换原始映射。
我可以将 Map.Entry 个对象保存在临时 Map 中,然后在迭代完成后返回并更改它们的值吗?
例如,下面分两步实现一种交易。在第 1 步中,递归处理条目映射和条目映射(因此是一棵树)。条目被处理成新值,这些值被保存到一个临时映射中,由相应的 Map.Entry 键控。如果所有条目的计算都没有异常,则第 2 步只是简单地遍历临时映射并分配相应的新值。
void performPerilousProcedure(Object val) throws Exception
{
if (processing of val fails)
throw new Exception();
}
void recurivePerilousProcedure(Map someMap, Map tmp) throws Exception
{
Iterator<Map.Entry<String,Object>> iter1;
iter1 = someMap.entrySet().iterator();
while (iter1.hasNext()) {
Map.Entry<String,Object> entry1 = iter1.next();
Object val = entry1.getValue();
if (val instanceof Map) {
recursivePerilousProcedure((Map)val, tmp);
} else {
Object newval = performPerilousProcedure(val);
// ok to use Map.Entry as key across iter?
tmp.put(entry1, newval);
}
}
}
void doit(Map<String,Object> someMap) throws Exception
{
HashMap<Map.Entry<String,Object>,Object> tmp = new HashMap();
// Try to process map of entries and maps of entries and ma ...
recursivePerilousProcedure(someMap, tmp);
// All entries success processed, now simply assign new vals
Iterator<Map.Entry<String,Object>> iter2;
iter2 = tmp.keySet().iterator();
while (iter2.hasNext()) {
Map.Entry<String,Object> entry2 = iter2.next();
Object newval = tmp.get(entry2);
entry2.setValue(newval); // commit new value
}
}
问题是:
用作 tmp
键的 Map.Entry
对象能否在迭代之外存活?
假设没有并发修改,这是对 Map 接口的有效使用吗?
如果我跟着你,你可以这样做:
for (Entry<String, Object> entry : someMap.entrySet()) {
Object newVal = performPerilousProcedure(entry.getValue());
tmp.put(entry.getKey(), newVal);
}
someMap.putAll(tmp);
documentation of Map.Entry
说的很清楚了:
These instances maintain a connection to the original, backing map. This connection to the backing map is valid only for the duration of iteration over the entry-set view. During iteration of the entry-set view, if supported by the backing map, a change to a
Map.Entry
's value via thesetValue
method will be visible in the backing map. The behavior of such aMap.Entry
instance is undefined outside of iteration of the map's entry-set view.
未定义的行为并不妨碍执行预期的操作,实际上,当映射为 HashMap
或 TreeMap
时,只要您不修改映射(与在条目上调用 setValue
的方式不同)。
问题是您是否要在这种实现特定行为的基础上构建您的应用程序,虽然长期存在,但尚未明确指定。
还有一个问题是您将这些条目用作另一个映射中的键。条目的相等性由键和值的组合决定,而不是由关联的映射决定。换句话说,如果不同映射的两个条目恰好具有相同的键和值,则会发生冲突,并且尝试放置另一个条目将保留已经存在的条目,但将其与不适用于该条目的新值相关联(或更精确,不适用于此目标地图)。
更糟糕的是,当条目实例已经用作键时,您正在更改相等性,方法是对它们调用 setValue
,which is definitely illegal 并将 HashMap
变成一个不一致状态:
Note: great care must be exercised if mutable objects are used as map keys. The behavior of a map is not specified if the value of an object is changed in a manner that affects
equals
comparisons while the object is a key in the map.
如果您要做的只是迭代 tmp
一次,则无需使用地图,因为不需要查找操作。 List<Map.Entry<Key,Value>>
可以代替 Map<Key,Value>
,以保持可以迭代的键和值的组合。事实上,任何能够保存两个值的对象都可以用来代替 Map.Entry<Key,Value>
。如果用一个能够保存三个值的对象替换它,您可以跟踪目标 Map
、键和新值,并在最后执行一个简单的 put
。
所以第一种方法看起来像
void recursivePerilousProcedure(Map<String,Object> someMap,
List<Map.Entry<Map.Entry<String,Object>,Object>> tmp)
throws Exception {
for(Map.Entry<String,Object> entry1: someMap.entrySet()) {
Object val = entry1.getValue();
if (val instanceof Map) {
recursivePerilousProcedure((Map<String,Object>)val, tmp);
} else {
Object newval = performPerilousProcedure(val);
tmp.add(new AbstractMap.SimpleEntry<>(entry1, newval));
}
}
}
void doit(Map<String,Object> someMap) throws Exception {
List<Map.Entry<Map.Entry<String,Object>,Object>> tmp = new ArrayList<>();
// Try to process map of entries and record new values
recursivePerilousProcedure(someMap, tmp);
// All entries success processed, now simply assign new vals
// relies on Map implementation detail
for(Map.Entry<Map.Entry<String,Object>,Object> entry: tmp) {
Object newval = entry.getValue();
entry.getKey().setValue(newval); // assign new value
}
}
它依赖于实现细节,但有一点优势,即调用 setValue
时不需要地图查找。或者干净的解决方案:
record NewValue(Map<String,Object> target, String key, Object newValue) {
void apply() {
target.put(key, newValue);
}
}
void recursivePerilousProcedure(Map<String,Object> someMap,
List<NewValue> tmp) throws Exception {
for(Map.Entry<String,Object> entry1: someMap.entrySet()) {
Object val = entry1.getValue();
if (val instanceof Map) {
recursivePerilousProcedure((Map<String,Object>)val, tmp);
} else {
Object newval = performPerilousProcedure(val);
tmp.add(new NewValue(someMap, entry1.getKey(), newval));
}
}
}
void doit(Map<String,Object> someMap) throws Exception {
List<NewValue> tmp = new ArrayList<>();
// Try to process map of entries and record new values
recursivePerilousProcedure(someMap, tmp);
// All entries success processed, now simply assign new vals
// assign all new values, cleanly
tmp.forEach(NewValue::apply);
}
根据您的应用,可能还有第三种选择。只需在递归处理过程中构建一个匹配预期目标结构的新映射,在没有错误发生时替换原始映射。