Map.entry详解
https://blog.youkuaiyun.com/kyi_zhu123/article/details/52769469
Map.entrySet() 这个方法返回的是一个Set<Map.Entry<K,V>>,Map.Entry 是Map中的一个接口,他的用途是表示一个映射项(里面有Key和Value),而Set<Map.Entry<K,V>>表示一个映射项的Set。Map.Entry里有相应的getKey和getValue方法,即JavaBean,让我们能够从一个项中取出Key和Value。
下面是遍历Map的四种方法:
-
public static void main(String[] args) { -
Map<String, String> map = new HashMap<String, String>(); -
map.put("1", "value1"); -
map.put("2", "value2"); -
map.put("3", "value3"); -
//第一种:普遍使用,二次取值 -
System.out.println("通过Map.keySet遍历key和value:"); -
for (String key : map.keySet()) { -
System.out.println("key= "+ key + " and value= " + map.get(key)); -
} -
//第二种 -
System.out.println("通过Map.entrySet使用iterator遍历key和value:"); -
Iterator<Map.Entry<String, String>> it = map.entrySet().iterator(); -
while (it.hasNext()) { -
Map.Entry<String, String> entry = it.next(); -
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue()); -
} -
//第三种:推荐,尤其是容量大时 -
System.out.println("通过Map.entrySet遍历key和value"); -
for (Map.Entry<String, String> entry : map.entrySet()) { -
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue()); -
} -
//第四种 -
System.out.println("通过Map.values()遍历所有的value,但不能遍历key"); -
for (String v : map.values()) { -
System.out.println("value= " + v); -
} -
}
下面是HashMap的源代码:
首先HashMap的底层实现用的时候一个Entry数组
-
java] view plain copy -
<pre name="code" class="java"> /** -
* The table, resized as necessary. Length MUST Always be a power of two. -
*/ -
transient Entry[] table; //声明了一个数组 -
........ -
public HashMap() { -
this.loadFactor = DEFAULT_LOAD_FACTOR; -
threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR); -
table = new Entry[DEFAULT_INITIAL_CAPACITY];//初始化数组的大小为DEFAULT_INITIAL_CAPACITY(这里是16) -
init(); -
}</pre><br>
再来看一下Entry是在什么地方定义的,继续上源码,我们在HashMap的源码的674行发现了它的定义,原来他是HashMap的一个内部类,并且实现了Map.Entry接口,以下有些地方是转载
-
static class Entry<K,V> implements Map.Entry<K,V> { -
final K key; -
V value; -
Entry<K,V> next; -
final int hash; -
/** -
* Creates new entry. -
*/ -
Entry(int h, K k, V v, Entry<K,V> n) { -
value = v; -
next = n; -
key = k; -
hash = h; -
} -
public final K getKey() { -
return key; -
} -
public final V getValue() { -
return value; -
} -
public final V setValue(V newValue) { -
V oldValue = value; -
value = newValue; -
return oldValue; -
} -
public final boolean equals(Object o) { -
if (!(o instanceof Map.Entry)) -
return false; -
Map.Entry e = (Map.Entry)o; -
Object k1 = getKey(); -
Object k2 = e.getKey(); -
if (k1 == k2 || (k1 != null && k1.equals(k2))) { -
Object v1 = getValue(); -
Object v2 = e.getValue(); -
if (v1 == v2 || (v1 != null && v1.equals(v2))) -
return true; -
} -
return false; -
} -
public final int hashCode() { -
return (key==null ? 0 : key.hashCode()) ^ -
(value==null ? 0 : value.hashCode()); -
} -
public final String toString() { -
return getKey() + "=" + getValue(); -
} -
/** -
* This method is invoked whenever the value in an entry is -
* overwritten by an invocation of put(k,v) for a key k that's already -
* in the HashMap. -
*/ -
void recordAccess(HashMap<K,V> m) { -
} -
/** -
* This method is invoked whenever the entry is -
* removed from the table. -
*/ -
void recordRemoval(HashMap<K,V> m) { -
} -
}
既然这样那我们再看一下Map.Entry这个接口是怎么定义的,原来他是Map的一个内部接口并且定义了一些方法
-
interface Entry<K,V> { -
/** -
* Returns the key corresponding to this entry. -
* -
* @return the key corresponding to this entry -
* @throws IllegalStateException implementations may, but are not -
* required to, throw this exception if the entry has been -
* removed from the backing map. -
*/ -
K getKey(); -
/** -
* Returns the value corresponding to this entry. If the mapping -
* has been removed from the backing map (by the iterator's -
* <tt>remove</tt> operation), the results of this call are undefined. -
* -
* @return the value corresponding to this entry -
* @throws IllegalStateException implementations may, but are not -
* required to, throw this exception if the entry has been -
* removed from the backing map. -
*/ -
V getValue(); -
/** -
* Replaces the value corresponding to this entry with the specified -
* value (optional operation). (Writes through to the map.) The -
* behavior of this call is undefined if the mapping has already been -
* removed from the map (by the iterator's <tt>remove</tt> operation). -
* -
* @param value new value to be stored in this entry -
* @return old value corresponding to the entry -
* @throws UnsupportedOperationException if the <tt>put</tt> operation -
* is not supported by the backing map -
* @throws ClassCastException if the class of the specified value -
* prevents it from being stored in the backing map -
* @throws NullPointerException if the backing map does not permit -
* null values, and the specified value is null -
* @throws IllegalArgumentException if some property of this value -
* prevents it from being stored in the backing map -
* @throws IllegalStateException implementations may, but are not -
* required to, throw this exception if the entry has been -
* removed from the backing map. -
*/ -
V setValue(V value); -
/** -
* Compares the specified object with this entry for equality. -
* Returns <tt>true</tt> if the given object is also a map entry and -
* the two entries represent the same mapping. More formally, two -
* entries <tt>e1</tt> and <tt>e2</tt> represent the same mapping -
* if<pre> -
* (e1.getKey()==null ? -
* e2.getKey()==null : e1.getKey().equals(e2.getKey())) && -
* (e1.getValue()==null ? -
* e2.getValue()==null : e1.getValue().equals(e2.getValue())) -
* </pre> -
* This ensures that the <tt>equals</tt> method works properly across -
* different implementations of the <tt>Map.Entry</tt> interface. -
* -
* @param o object to be compared for equality with this map entry -
* @return <tt>true</tt> if the specified object is equal to this map -
* entry -
*/ -
boolean equals(Object o); -
/** -
* Returns the hash code value for this map entry. The hash code -
* of a map entry <tt>e</tt> is defined to be: <pre> -
* (e.getKey()==null ? 0 : e.getKey().hashCode()) ^ -
* (e.getValue()==null ? 0 : e.getValue().hashCode()) -
* </pre> -
* This ensures that <tt>e1.equals(e2)</tt> implies that -
* <tt>e1.hashCode()==e2.hashCode()</tt> for any two Entries -
* <tt>e1</tt> and <tt>e2</tt>, as required by the general -
* contract of <tt>Object.hashCode</tt>. -
* -
* @return the hash code value for this map entry -
* @see Object#hashCode() -
* @see Object#equals(Object) -
* @see #equals(Object) -
*/ -
int hashCode(); -
}
看到这里的时候大伙儿估计都明白得差不多了为什么HashMap为什么要选择Entry数组来存放key-value对了吧,因为Entry实现的Map.Entry接口里面定义了getKey(),getValue(),setKey(),setValue()等方法相当于一个javaBean,对键值对进行了一个封装便于后面的操作,从这里我们其实也可以联想到不光是HashMap,譬如LinkedHashMap,TreeMap 等继承自map的容器存储key-value对都应该使用的是Entry只不过组织Entry的形式不一样,HashMap用的是数组加链表的形式,LinkedHashMap用的是链表的形式,TreeMap应该使用的二叉树的形式,不信的话上源码
LinkedHashMap:
-
/** -
* The head of the doubly linked list. -
*/ -
/定义了链头 -
private transient Entry<K,V> header;
初始化链表的方法:
-
void init() { -
header = new Entry<K,V>(-1, null, null, null); -
header.before = header.after = header; -
}
TreeMap:
-
[java] view plain copy -
//定义根节点 -
private transient Entry<K,V> root = null;
再看他的put方法,是不是很面熟(二叉排序树的插入操作)
-
public V put(K key, V value) { -
Entry<K,V> t = root; -
if (t == null) { -
// TBD: -
// 5045147: (coll) Adding null to an empty TreeSet should -
// throw NullPointerException -
// -
// compare(key, key); // type check -
root = new Entry<K,V>(key, value, null); -
size = 1; -
modCount++; -
return null; -
} -
int cmp; -
Entry<K,V> parent; -
// split comparator and comparable paths -
Comparator<? super K> cpr = comparator; -
if (cpr != null) { -
do { -
parent = t; -
cmp = cpr.compare(key, t.key); -
if (cmp < 0) -
t = t.left; -
else if (cmp > 0) -
t = t.right; -
else -
return t.setValue(value); -
} while (t != null); -
} -
else { -
if (key == null) -
throw new NullPointerException(); -
Comparable<? super K> k = (Comparable<? super K>) key; -
do { -
parent = t; -
cmp = k.compareTo(t.key); -
if (cmp < 0) -
t = t.left; -
else if (cmp > 0) -
t = t.right; -
else -
return t.setValue(value); -
} while (t != null); -
} -
Entry<K,V> e = new Entry<K,V>(key, value, parent); -
if (cmp < 0) -
parent.left = e; -
else -
parent.right = e; -
fixAfterInsertion(e); -
size++; -
modCount++; -
return null; -
}
ok,明白了各种Map的底层存储key-value对的方式后,再来看看如何遍历map吧,这里用HashMap来演示吧
Map提供了一些常用方法,如keySet()、entrySet()等方法,keySet()方法返回值是Map中key值的集合;entrySet()的返回值也是返回一个Set集合,此集合的类型为Map.Entry。
so,很容易写出如下的遍历代码
-
[java] view plain copy -
1. Map map = new HashMap(); -
Irerator iterator = map.entrySet().iterator(); -
while(iterator.hasNext()) { -
Map.Entry entry = iterator.next(); -
Object key = entry.getKey(); -
// -
} -
2.Map map = new HashMap(); -
Set keySet= map.keySet(); -
Irerator iterator = keySet.iterator; -
while(iterator.hasNext()) { -
Object key = iterator.next(); -
Object value = map.get(key); -
// -
} -
另外,还有一种遍历方法是,单纯的遍历value值,Map有一个values方法,返回的是value的Collection集合。通过遍历collection也可以遍历value,如 -
[java] view plain copy -
Map map = new HashMap(); -
Collection c = map.values(); -
Iterator iterator = c.iterator(); -
while(iterator.hasNext()) { -
Object value = iterator.next();
614

被折叠的 条评论
为什么被折叠?



