java集合总结

java集合专题

[### java集合专题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4ackZ4G7-1664026107117)(/Users/wangkx/Desktop/wkx的md笔记/img/image-20220923114610756.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rqapTq32-1664026107118)(/Users/wangkx/Desktop/wkx的md笔记/img/image-20220923115135944.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MEgb9CMS-1664026154679)(/Users/wangkx/Desktop/wkx的md笔记/img/image-20220923115155108.png)]

list
  • Arraylist(不安全) 底层是Object类型的数组 , 可以放null , 调用无参构造器,默认从0扩容到10,之后每次扩容1.5倍 ; 调用有参构造器,先按指定大小扩容,之后按照1.5倍扩容

  • Vector(线程安全) , 底层依然是数组,内部每个操作都带synchronized , 如果是无参构造器,默认10, 满后就按2倍扩容,有参构造器,就按照指定大小,之后按照2倍扩容

  • LinkedList (不安全) 底层是双向链表,可放重复元素,可存储null值

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G0fLlbkP-1664026107118)(/Users/wangkx/Desktop/wkx的md笔记/img/image-20220923120340879.png)]

set --不重复元素,底层都是map
  • hashSet(无序),底层是hashmap,可存放null,索引位置取决于hash ,不保证存入顺序和取出顺序一致 , 扩容机制见hashmap , 键值是一个静态对象P1RESENT,不可改变

  • LinkedHashSet(有序) 继承hashSet , 底层是LinkedHashMap,维护了数组加双向链表,插入顺序和遍历顺序一致

  • treeSet (支持排序) , 底层是treeMap,使用无参构造器创建TreeSet时,元素无序存放,想要有序需要传入一个比较器,重写其compare方法

    TreeSet treeSet = new TreeSet(new Comparator() {
        @Override
        public int compare(Object o1, Object o2) {
            // 下面调用String的compareTo方法进行字符串大小比较
            return ((String) o2).compareTo((String) o1);  // 规则1
            return ((String) o2).length() - ((String) o1).length(); // 规则2
        }
    });
    
双列集合map
  • hashMap

    1.8及之后的hashmap扩容机制

    • HashSet底层是HashMap,map里维护了一个Node类型的table数组 , 第一次添加时,table数组扩容到16,临界值(threshold)是16*加载因子(loadFactor)是0.75 = 12

    • 意思就是如果table 数组使用到了临界值12,虽然还剩下四个,但是table数组就会扩容到16* 2 =32,新的临界值就是32*0.75 = 24,依次类推

    • 这里需要注意,临界值指的是节点数量,即使只占用table的两个位置,即拉了两条链 , 当总节点数到达12的时候, table数组的长度就会扩容2倍

    • 在java8中 ,满足以下两个条件链表就会树化成红黑树

      • 一条链表的元素个数已经到8个了
      • table数组长度达到64了
    • 如果仅仅是链表元素个数到8 , 数组长度还没到64 , 那么就不会树化,而是数组长度会扩容为2倍 , 然后重新哈希,当然链表的长度可能超过8

    • 但是如果红黑树的元素个数小于6 那么就会还原成链表, 当红黑树的元素个数不小于32的时候才会再次扩容。

    // 添加的核心方法,putVal
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        // 这是一些辅助变量
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        //table是hash数组表 ,如果table长度为0,就执行第一次扩容到16
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        /*
         * 根据key值和hash算法(h >>> 16无符号右移),计算该key值应该放在table表的哪一个位置,并且把这个位置的对象赋给p
         * 判断p是否为空,如果是空的,说明table的对应位置还没有存放元素, 那么就创建一个node放在这个位置             
        */
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
             /*
            *  p.hash : 当前索引位置对象的hash值 ; hash : 要添加的key的hash
            * 如果当前索引位置对应的链表的第一个元素和准备添加的 key 的 hash 值一样
            * 并且满足 下面两个条件之一 ,就不能加入
            * ① 准备加入的 key 和 p 指向的 Node 结点的 key 是同一个对象
            * ② p指向的node元素的equals()和准备加入的key的equals()相同
            */
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            // 判断 p 是不是一颗红黑树 , 如果是就调用putTreeVal添加节点
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                如果 table 对应索引位置,已经是一个链表, 就使用 for 循环比较(死循环)
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        // 若果该链表的长度达到8了,就调用treeifyBin方法树化成红黑树 ,转之前还需要判断               table长度是否超过64
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    // 比较过程中,如果有hash和equals()相同的情况,就直接 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;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
    

    hashmap1.7和1.8的区别

    • 底层数据结构不一样,1.7是数组+链表,1.8则是**数组+链表+红黑树结构

    • JDK1.7用的是头插法,而JDK1.8及之后使用的都是尾插法

      那么他们为什么要这样做呢?

      因为JDK1.7是用单链表进行的纵向延伸,当采用头插法就是能够提高插入的效率,但是在并发情况下也会容易出现循环链表问题。但是在JDK1.8之后是因为加入了红黑树使用尾插法,能够避免出现循环链表的问题。

      循环链表问题:假设同时有两个线程同时执行触发了同一个HashMap对象的resize()过程,他们同时进入到了transfer()方法中。。。

    • 1.7中hash值是可变的1.8中hash是final修饰,不可变

    • 扩容机制:1.7中是先扩容后插入,1.8中是先插入再扩容

  • hashTable(线程安全) ,底层方法都加了synchronized , 底层有数组 Hashtable$Entry[] 初始化大小为 11 , 临界值 threshold 8 = 11 * 0.75 , 扩容机制:左移1位再加1 , 键和值都不能为null

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KIv0L9dv-1664026107119)(/Users/wangkx/Desktop/wkx的md笔记/img/image-20220924143133945.png)]

  • treeMap(支持排序) ,使用默认的构造器创建treeMap是无序的 , 想要有序的话,和treeSet的机制相同(比较的依然是key值)

  • propryties , Properties类继承Hashtable类井且实现了Map接口,也是使用一种键值对的形式来保存数据。

collections

java.util.Collections 是一个包装类(工具类/帮助类)。它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类,用于对集合中元素进行排序、搜索以及线程安全等各种操作,服务于Java的Collection框架。

  • reverse(List):反转 List 中元素的顺序
  • shuffle(List):对 List 集合元素进行随机排序
  • sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
  • sort(List, Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
  • swap(List, int, int):将指定 list 集合中的i 处元素和j 处元素进行交换
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值