keySet():
返回的是只存放key值的Set集合,使用迭代器方式遍历该Set集合,在迭代器中再使用get方法获取每一个键对应的值
代码案例:
keySet遍历代码
/**
* 通过keySet遍历
* @param map
*/
public static void keySetTest(Map<String ,Object> map){
Iterator<String> keys = map.keySet().iterator();
while(keys.hasNext()){
String key = keys.next();
Object value = map.get(key);
}
}
源码解析
1.new HashMap().keySet().iterator();
public Set<K> keySet() {
Set<K> ks = keySet;
if (ks == null) {
ks = new KeySet();
keySet = ks;
}
return ks;
}
final class KeySet extends AbstractSet<K> {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
public final Iterator<K> iterator() { return new KeyIterator(); }
public final boolean contains(Object o) { return containsKey(o); }
public final boolean remove(Object key) {
return removeNode(hash(key), key, null, false, true) != null;
}
public final Spliterator<K> spliterator() {
return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public final void forEach(Consumer<? super K> action) {
Node<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next)
action.accept(e.key);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
final class KeyIterator extends HashIterator
implements Iterator<K> {
public final K next() { return nextNode().key; }
}
从上诉源码看出HashMap.keySet().iterator()遍历的 key
2.new HashMap().get();
JDK1.7 HashMap 数据结构:数组+链表
JDK1.8 HashMap 数据结构:数组+红黑树(平衡二叉树)
JDK1.7源码解析
public V get(Object key) {
if (key == null)
return getForNullKey();
Entry<K,V> entry = getEntry(key);
return null == entry ? null : entry.getValue();
}
final Entry<K,V> getEntry(Object key) {
if (size == 0) {
return null;
}
int hash = (key == null) ? 0 : hash(key);
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}
JDK1.8源码解析 查找数据走TreeNode分支
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
/**
* Implements Map.get and related methods.
*
* @param hash hash for key
* @param key the key
* @return the node, or null if none
*/
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
if ((e = first.next) != null) {
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
/**
* Calls find for root node.
*/
final TreeNode<K,V> getTreeNode(int h, Object k) {
return ((parent != null) ? root() : this).find(h, k, null);
}
/**
* Finds the node starting at root p with the given hash and key.
* The kc argument caches comparableClassFor(key) upon first use
* comparing keys.
*/
final TreeNode<K,V> find(int h, Object k, Class<?> kc) {
TreeNode<K,V> p = this;
do {
int ph, dir; K pk;
TreeNode<K,V> pl = p.left, pr = p.right, q;
if ((ph = p.hash) > h)
p = pl;
else if (ph < h)
p = pr;
else if ((pk = p.key) == k || (k != null && k.equals(pk)))
return p;
else if (pl == null)
p = pr;
else if (pr == null)
p = pl;
else if ((kc != null ||
(kc = comparableClassFor(k)) != null) &&
(dir = compareComparables(kc, k, pk)) != 0)
p = (dir < 0) ? pl : pr;
else if ((q = pr.find(h, k, kc)) != null)
return q;
else
p = pl;
} while (p != null);
return null;
}
entrySet():
返回的是存放了映射关系的Set集合(一个映射关系就是一个键-值对),就是把(key-value)作为一个整体一对一对地存放到Set集合当中的
entrySet遍历代码
/**
* 通过entrySet遍历
* @param map
*/
public static void entrySetTest(Map<String,Object>map){
Iterator<Map.Entry<String,Object>> entrys = map.entrySet().iterator();
while(entrys.hasNext()){
Map.Entry<String,Object> entry = entrys.next();
Object value = entry.getValue();
}
}
源码接下
1.new HashMap().entrySet().iterator()
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
public final Iterator<Map.Entry<K,V>> iterator() {
return new EntryIterator();
}
public final boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
Node<K,V> candidate = getNode(hash(key), key);
return candidate != null && candidate.equals(e);
}
public final boolean remove(Object o) {
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
Object value = e.getValue();
return removeNode(hash(key), key, value, true, true) != null;
}
return false;
}
public final Spliterator<Map.Entry<K,V>> spliterator() {
return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
Node<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next)
action.accept(e);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
final class EntryIterator extends HashIterator
implements Iterator<Map.Entry<K,V>> {
public final Map.Entry<K,V> next() { return nextNode(); }
}
从上诉源码看出HashMap.entrySet().iterator()遍历的 数据本身
综上分析:
keySet与entrySet性能差异主要是:使用get方法获取键数据需要遍历数组+链表(红黑树)
测试数据如下(测试数据仅供参考)
JDK1.7环境下 keySet 耗时是 entrySet 约3倍
JDK1.8环境下 keySet 耗时是 entrySet 约2倍
JDK1.7,1.8环境下 entrySet性能优化差异不大,略微差于1.8环境
遍历 | JDK | 1K | 10K | 100K | 1000K | 备注 |
keySet | 1.7 | 9-11ms | 16-18ms | 45-48ms | 313-317ms | 遍历50个key,遍历1K,10K,100K,1000K |
entrySet | 1.7 | 2-3ms | 5-6ms | 16-18ms | 103-109ms | 遍历50个key,遍历1K,10K,100K,1000K |
keySet | 1.8 | 9-11ms | 11-13ms | 34-35ms | 195-202ms | 遍历50个key,遍历1K,10K,100K,1000K |
entrySet | 1.8 | 2-3ms | 6-8ms | 15-17ms | 101-102ms | 遍历50个key,遍历1K,10K,100K,1000K |