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的类型必须提供排序规则

posted @ 2022-11-22 00:11  Liku007  阅读(72)  评论(0编辑  收藏  举报