Java 不';通过entrySet()进行迭代会创建太多的Map.Entry实例吗?
我不确定是Java 不';通过entrySet()进行迭代会创建太多的Map.Entry实例吗?,java,performance,Java,Performance,我不确定是HashMap还是TreeMap本身存储Map.Entry。也就是说,当调用entrySet().iterator().next()时,它可能会返回动态创建的Map.Entry实例 就我个人而言,我认为这种形式可能更好: class Entry { Object key; Object value; } interface InplaceIterator { boolean next(); } Entry entryBuf = new Entry(); In
HashMap
还是TreeMap
本身存储Map.Entry
。也就是说,当调用entrySet().iterator().next()
时,它可能会返回动态创建的Map.Entry
实例
就我个人而言,我认为这种形式可能更好:
class Entry {
Object key;
Object value;
}
interface InplaceIterator {
boolean next();
}
Entry entryBuf = new Entry();
InplaceIterator it = map.entrySet().inplaceIterator(entryBuf);
while (it.next()) {
// do with entryBuf...
}
因此,可以避免创建条目
我不知道Java编译器是如何工作的,Java编译器是否会通过分析数据流优化Map.Entry的创建,并获得Map.Entry
可以安全重用的知识
或者,是否有人已经编写了另一个集合框架来启用就地迭代?您所描述的(拥有一个迭代器本地映射。Entry对象并将其用于所有next()
返回值)是一种可能的映射实现,我认为一些特殊用途的映射正在使用它
例如,EnumMap.entrySet().iterator()
(这里是OpenJDK的版本,1.6.0\u 20)的实现只是将迭代器对象本身用作next()
方法返回的条目对象:
/**
* Since we don't use Entry objects, we use the Iterator itself as entry.
*/
private class EntryIterator extends EnumMapIterator<Map.Entry<K,V>>
implements Map.Entry<K,V>
{
public Map.Entry<K,V> next() {
if (!hasNext())
throw new NoSuchElementException();
lastReturnedIndex = index++;
return this;
}
public K getKey() {
checkLastReturnedIndexForEntryUse();
return keyUniverse[lastReturnedIndex];
}
public V getValue() {
checkLastReturnedIndexForEntryUse();
return unmaskNull(vals[lastReturnedIndex]);
}
public V setValue(V value) {
checkLastReturnedIndexForEntryUse();
V oldValue = unmaskNull(vals[lastReturnedIndex]);
vals[lastReturnedIndex] = maskNull(value);
return oldValue;
}
// equals, hashCode, toString
private void checkLastReturnedIndexForEntryUse() {
if (lastReturnedIndex < 0)
throw new IllegalStateException("Entry was removed");
}
}
/**
*因为我们不使用条目对象,所以我们使用迭代器本身作为条目。
*/
私有类EntryInterator扩展了EnumMapIterator
实现Map.Entry
{
publicmap.Entry next(){
如果(!hasNext())
抛出新的NoTouchElementException();
lastReturnedIndex=index++;
归还这个;
}
公共K getKey(){
checkLastReturnedIndexForEntryUse();
return-keyUniverse[lastReturnedIndex];
}
public V getValue(){
checkLastReturnedIndexForEntryUse();
返回unmaskNull(VAL[lastReturnedIndex]);
}
公共V设置值(V值){
checkLastReturnedIndexForEntryUse();
V oldValue=unmaskNull(VAL[lastReturnedIndex]);
VAL[lastReturnedIndex]=maskNull(值);
返回旧值;
}
//等于,哈希代码,toString
私有void checkLastReturnedIndexForEntryUse()的{
如果(最后返回的索引<0)
抛出新的非法状态异常(“条目已删除”);
}
}
这是可能的,因为国家(我强调):
映射条目(键值对)。Map.entrySet
方法返回映射的集合视图,
其元素属于此类。获取地图条目引用的唯一方法是从
此集合视图的迭代器这些Map.Entry
对象仅在持续时间内有效
迭代的;更正式地说,如果备份映射具有
在迭代器返回条目后进行了修改,但通过setValue操作除外
在地图条目上
如果要同时使用所有条目,则必须使用map.entrySet().toArray()
,这可能会创建
条目的不可变副本
下面是关于默认映射的更多观察(都在OpenJDK 1.6.020中,可以在Ubuntu的
openjdk6源代码
包中找到):
- 通用映射
和HashMap
(以及传统的TreeMap
)已经在使用一些 类似于哈希表
对象作为其内部结构(表或树)的一部分,因此它们可以简化 对象实现Map.Entry并返回它们。它们不是由迭代器动态创建的 这同样适用于条目
(在强引用中具有WeakHashMap
对象不会避免 如果我理解正确的话,垃圾收集是关键——但只要你不在服务器上调用条目
迭代器,迭代器持有当前条目中的键)next()
在内部使用一个简单的IdentityHashMap
, 使用交替键和值,所以这里也没有条目对象,因此也可以将迭代器作为条目重用对象[]
使用的节点对象不实现任何内容,因此其迭代器返回ConcurrentSkipListMap
newAbstractMap.SimpleImmutableEntry(n.key,v)代码>。这意味着您不能使用它们的
方法, 如课堂文档中所述: 此类中的方法及其视图返回的所有setValue()
对表示映射的快照 在它们生产的时候。它们不支持Map.Entry
方法。(但请注意 可以使用Entry.setValue
、put
或putIfAbsent
更改关联映射中的映射,具体取决于 您需要的确切效果。)replace
在内部使用一个类似于HashMap的ConcurrentHashMap
类,但这不是 实施任何事情。此外,还有一个内部类HashEntry
(扩展WriteThroughEntry
),其AbstractMap.SimpleEntry
方法委托给映射的setValue()
方法。迭代器 返回此put
类的新对象WriteThroughEntry
private Multimap<Integer, String> store = ArrayListMultimap.create();
private Multimap store=ArrayListMultimap.create();
迭代多重映射
for (Map.Entry<Integer, String> entry: store.entries()) {}
for(Map.Entry:store.entries()){
如果您希望避免Map.Entry,则提取键集并从那里开始:
List<Integer> keys = new ArrayList<Integer>(store.keySet());
for(Long key : keys){
ArrayList<String> stored_strings = new ArrayList<String>(store.get(key));
}
List keys=new ArrayList(store.keySet());
用于(长键:键){
ArrayList stored_strings=新的ArrayList(store.get(key));
}
通常,小型、寿命短的对象几乎是免费的。考虑<代码> F1 < /代码>和<代码> F2 < /代码>
static Entry f1(int i){ return new Entry(i); }
static Entry entry = new Entry(0);
static Entry f2(int i){ entry.i=i; return entry; }
static class Entry
{
Entry(int i){ this.i=i; }
int i;
int get(){ return i; }
}
这是您描述的问题的一个实际测试用例-每次迭代重用相同的对象,而不是每次迭代创建一个新对象。在这两种情况下,一些数据保存在对象中,并传递到调用si
int r = 0;
for(int i=0; i<1000000000; i++)
{
test0: r += i;
test1: r += f1(i).get();
test2: r += f2(i).get();
}
print(r);