map
Map<K,V>
用于存放一组元素,key:唯一 且无序 value:可重复。key一般都是string或integer
数据结构 | 线程安全 | key/value是否可以为null | |
---|---|---|---|
HashMap | 位桶+单向链表+红黑树 | 否 | 是 |
LinkedHashMap | 位桶+单向链表+红黑树 | 否 | 是 |
TreeMap | 红黑树 | 否 | k不可以,v可以 |
HashTable | hash表 | 是 | 否 |
ConcurrentHashMap | 锁分段技术,乐观锁CAS | 是 | 否 |
常用方法:
HashMap<k,v>
key重复会被覆盖,但是value可以重复,常用方法:
HashMap<Integer,String> hashMap=new HashMap<>();//初始容量16,负载因子0.75
hashMap.put(key,value);
hashMap.remove(key);
hashMap.remove(key, value);
hashMap.replace(key, value, replacedvalue);
hashMap.get(key);//得到key对应的值
hashMap.getOrDefault(key, defaultvalue)//可以用这个判断是否有该元素,因为它可以避免空指针异常
hashMap.forEach(new BiConsumer<String, Object>());//遍历
hashMap.forEach((key,value)->{});//遍历的lamda写法
hashMap.entrySet();//将map元素封装成entryset对象,就可以用set的迭代器和for来遍历
hashMap.keySet();//不推荐使用,效率比较低
当数组上单项链表大于7并且size大于64的时候会自动将链表转换成树结构,遍历:
private static void demo1() {
HashMap<Integer, String> hashMap = new HashMap<>();
hashMap.put(78, "abc");
Set<Map.Entry<Integer, String>> entries = hashMap.entrySet();
for (Map.Entry<Integer, String> entry : entries) {
System.out.println(entry.getKey()+entry.getValue());//78abc
System.out.println(entry);//78=abc
}
}
put源码:
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
transient Node<K,V>[] table;
static final int TREEIFY_THRESHOLD = 8;
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)//k值相同的时候,该位置不为null
tab[i] = newNode(hash, key, value, null);
else {
//解决key值重复(hash冲突)的方式
Node<K,V> e; K k;
if (p.hash == hash &&//hash值相同并且key的值相同
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
//hash一致,但是元素数据不同
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
//当binCount大于7,走下面合并方法
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;//key相同时候覆盖value并且直接return,下面没有继续
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
static final float DEFAULT_LOAD_FACTOR = 0.75f;
static final int MAXIMUM_CAPACITY = 1 << 30;
int threshold;
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
static final int MIN_TREEIFY_CAPACITY = 64;
final void treeifyBin(Node<K,V>[] tab, int hash) {
int n, index; Node<K,V> e;
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)//小于64的时候扩容
resize();//扩容前阈值12,容量16,容量大于12时候扩容,阈值24,容量32
else if ((e = tab[index = (n - 1) & hash]) != null) {
//当数组长度大于64时候,将元素放到树里
TreeNode<K,V> hd = null, tl = null;
do {
TreeNode<K,V> p = replacementTreeNode(e, null);
if (tl == null)
hd = p;
else {
p.prev = tl;
tl.next = p;
}
tl = p;
} while ((e = e.next) != null);
if ((tab[index] = hd) != null)
hd.treeify(tab);
}
}
TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {
return new TreeNode<>(p.hash, p.key, p.value, next);
}
红黑树:
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
TreeNode<K,V> parent; // red-black tree links
TreeNode<K,V> left;
TreeNode<K,V> right;
TreeNode<K,V> prev; // needed to unlink next upon deletion
boolean red;
TreeNode(int hash, K key, V val, Node<K,V> next) {
super(hash, key, val, next);
}
/**
* Returns root of tree containing this node.
*/
final TreeNode<K,V> root() {
for (TreeNode<K,V> r = this, p;;) {
if ((p = r.parent) == null)
return r;
r = p;
}
}
方法实践如下:
private static void demo4() {
HashMap<String,Object> hashMap=new HashMap<>(16);
hashMap.put("id",1001);
hashMap.put("id",1002);
hashMap.put("name","张三");
hashMap.put("age","23");
hashMap.put("balance",2000.79);
System.out.println(hashMap);//{balance=2000.79, name=张三, id=1002, age=张三}
hashMap.remove("id");//删除成功,返回map{balance=2000.79, name=张三, age=张三}
hashMap.remove("id", 1002);//不存在这个值,删除失败,返回false
System.out.println(hashMap.replace("id", "name"));//null
System.out.println(hashMap.replace("id", "name", 23));//false
System.out.println(hashMap.get("age"));//得到key对应的值
System.out.println(hashMap.getOrDefault("name1", "空"));//可以用这个判断是否有该元素,因为它可以避免空指针异常
//遍历,没有迭代器,因为这不是collection的子类
hashMap.forEach(new BiConsumer<String, Object>(
) {
@Override
public void accept(String key, Object value) {
System.out.println(key+":"+value);
}
});
System.out.println("===========");
hashMap.forEach((key,value)->{
System.out.println(key+":"+value);
});
System.out.println("===========");
hashMap.forEach(SetDemo::accept);
Set<Map.Entry<String, Object>> entries = hashMap.entrySet();
//将map里面的每一组元素都封装成一个个entry对象,再将entry对象储存set
//用迭代器
Iterator<Map.Entry<String, Object>> iterator = entries.iterator();
while (iterator.hasNext()){
Map.Entry<String, Object> next = iterator.next();
System.out.println(next.getKey()+":"+next.getValue());
}
//用for
for (Map.Entry<String, Object> entry : entries) {
System.out.println(entry);
}
Set<String> strings = hashMap.keySet();
//将map所有的key都存储在set集合中
for(String key:strings){
System.out.println(key+":"+hashMap.get(key));
}
}
例题:求字符串中每个字符出现的次数
方法一:
private static void demo5() {
String str="23456hjhjdada654";
Map<String,Integer> map=new HashMap<>();
int len=str.length();
for (int i = 0; i < len; i++) {
String s=String.valueOf(str.charAt(i));
Integer count=map.get(s);
if(count==null){
map.put(s,1);
}else {
map.put(s,++count);
}
}
System.out.println(map);
}
方法二:用containskey,但是在判断的时候还走一次,因此效率会低一些
private static void demo5() {
String str = "23456hjhjdada654";
Map<String, Integer> map = new HashMap<>(16);
int len = str.length();
for (int i = 0; i < len; i++) {
String s = String.valueOf(str.charAt(i));
if (!map.containsKey(s)) {
map.put(s,1);
}else {
map.put(s, map.get(s)+1);
}
}
System.out.println(map);
LinkedHashMap<K,V>
TreeMap<K,V>
元素有序,要求key的类型必须提供排序规则