keySet():
返回的是只存放key值的Set集合,使用迭代器方式遍历该Set集合,在迭代器中再使用get方法获取每一个键对应的值
代码案例:
keySet遍历代码
/**
* 通过keySet遍历
* @param map
*/
public static void keySetTest(Map map){
Iterator keys = map.keySet().iterator();
while(keys.hasNext()){
String key = keys.next();
Object value = map.get(key);
}
}
源码解析
1.new HashMap().keySet().iterator();
public Set keySet() {
Set ks = keySet;
if (ks == null) {
ks = new KeySet();
keySet = ks;
}
return ks;
}
final class KeySet extends AbstractSet {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
public final Iterator 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 spliterator() {
return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public final void forEach(Consumer super K> action) {
Node[] 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 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 {
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 entry = getEntry(key);
return null == entry ? null : entry.getValue();
}
final Entry getEntry(Object key) {
if (size == 0) {
return null;
}
int hash = (key == null) ? 0 : hash(key);
for (Entry 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 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 getNode(int hash, Object key) {
Node[] tab; Node 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)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 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 find(int h, Object k, Class> kc) {
TreeNode p = this;
do {
int ph, dir; K pk;
TreeNode 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(Mapmap){
Iterator> entrys = map.entrySet().iterator();
while(entrys.hasNext()){
Map.Entry entry = entrys.next();
Object value = entry.getValue();
}
}
源码接下
1.new HashMap().entrySet().iterator()
public Set> entrySet() {
Set> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
final class EntrySet extends AbstractSet> {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
public final Iterator> 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 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> spliterator() {
return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public final void forEach(Consumer super Map.Entry> action) {
Node[] 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 e = tab[i]; e != null; e = e.next)
action.accept(e);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
final class EntryIterator extends HashIterator
implements Iterator> {
public final Map.Entry 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 |