一、Java基础系列之集合

版权声明:话不在多,在于精 https://blog.csdn.net/qq_29857681/article/details/89450008

List

ArrayList

  1. 可以进行快速查找
  2. 注意扩容,默认大小为10,当大于10时扩容,每次扩size>>1 (也就是1/2),底层使用Arrays.copyOf

LinkedList

  1. 可以操作头节点和尾节点,因此可以当作队列,栈使用
  2. 缺点:查找时需要遍历

CopyOnWriteArrayList

  1. 场景:可以支持 边查询,边写入,写入时不影响查询
  2. 实现:底层写方法使用ReentrantLock和Arrays.copyOf实现
  3. 缺点:数据是最终一致的,不是实时的。

Stack

Set

HashSet

  1. 可以去重,如果存储的是对象必须重写hashcode和equals,缺一不可,hashcode为了确定对象存储的位置,当hashcode一样时,比较对象的equals方法。
  2. 底层使用hashmap实现,你传进去的对象会作为hashmap的key,而key判断不重复是通过hashcode实现,因此需要重写对象的hashcode方法

TreeSet

  1. 默认按照自然排序,比如String 底层会调用String的compareTo方法,按照每个字符进行比较
  2. 底层使用TreeMap实现
  3. 自定义排序时,需要实现Comparable接口,重写compareTo方法

LinkedHashSet

  1. 继承hashset 底层使用linkedHashMap实现 (典型的适配器模式)
  2. add方法底层使用hashmap的put方法 将key放入数组中
  3. 同时覆盖hashmap的 newNode方法 ,将每次创建的节点插入链表尾部,维持顺序
  4. 遍历时遍历链表,按照插入顺序

CopyOnWriteArraySet

  1. 底层使用CopyOnWriteArrayList实现
  2. addIfAbsent 如果没有则添加 有了就不添加

SortedSet

  1. 可排序set 子接口NavigableSet
  2. 实现类treeset
  3.      ArrayList<Integer> arrayList = Lists.newArrayList(1, 2, 6, 4, 5);
            NavigableSet<Integer> treeSet = new TreeSet<>(arrayList);
            System.out.println("lower:" + treeSet.lower(2)); //NavigableSet
            System.out.println("floor:" + treeSet.floor(2));  //floor:2
            System.out.println("ceiling:" + treeSet.ceiling(4)); //ceiling:4
            System.out.println("higher:" + treeSet.higher(4));  //higher:5
            System.out.println("subSet:" + treeSet.subSet(1,true, 4,true)); //subSet:[1, 2, 4]
            System.out.println("headSet:" + treeSet.headSet(3));  //headSet:[1, 2]

Queue

BlockingQueue(接口)

扫描二维码关注公众号,回复: 6430462 查看本文章
  1. 主要作用: 阻塞队列,主要为了设置队列上限,当达到上线后就阻塞。
  2. 入队 (A)
    1. 如果while( count == items.length )则 notFull.await(); 底层使用 LockSupport.park(this); 
    2. 如果不等 则  入队 然后notEmpty.signal();
  3. 出队   (B)
    1. 如果while(count == 0) 则notEmpty.await();
    2. 如果不等 则 出队  然后notFull.signal();
  4. 执行顺序
    1.  A先执行B后执行    一直入队 2.2 - > 2.1 -> 3.2 ->3.1->2.2->3.2
    2. B先执行A后执行    3.1 ->2.2->3.2 可能出队快于入队 ->3.1 -> 2.2 ->3.2
    3.                                  3.1 ->2.2->3.2 可能入队快于出队 ->2.1 -> 3.2 ->2.2

ArrayBlockingQueue

this.items = new Object[capacity];

ConcurrentLinkedQueue(无锁线程安全的队列)

head = tail = new Node<E>(null);

offer方法

UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val)

LinkedBlockingQueue

last = head = new Node<E>(null);

PriorityBlockingQueue

优先级队列是一个基于堆的无界并发安全优先级队列

this.queue = new Object[initialCapacity];
  1. 无界  自动扩容  
    int newCap = oldCap + ((oldCap < 64) ?
                           (oldCap + 2) : // grow faster if small
                           (oldCap >> 1)
  2. System.arraycopy
  3. 并发安全 CAS
  4. 优先级 comparator
    1. 元素自身实现Comparable接口
    2. 创建一个比较器,在创建队列时传入

SynchronousQueue

同步阻塞队列

一个线程必须同步等待另外一个线程把相关信息/时间/任务传递给它

没有容量的概念,进一个出一个

Map

接口:ConcurrentMap  SortedMap

类:   

ConcurrentHashMap   (https://www.cnblogs.com/banjinbaijiu/p/9147434.html)

put

如果桶为空,则 U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);

不为空,则对桶中第一个元素加锁,实现分段锁

final V putVal(K key, V value, boolean onlyIfAbsent) {
        if (key == null || value == null) throw new NullPointerException(); // 键或值为空,抛出异常
        // 键的hash值经过计算获得hash值
        int hash = spread(key.hashCode());
        int binCount = 0;
        for (Node<K,V>[] tab = table;;) { // 无限循环
            Node<K,V> f; int n, i, fh;
            if (tab == null || (n = tab.length) == 0) // 表为空或者表的长度为0
                // 初始化表
                tab = initTable();
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { // 表不为空并且表的长度大于0,并且该桶不为空
                if (casTabAt(tab, i, null,
                             new Node<K,V>(hash, key, value, null))) // 比较并且交换值,如tab的第i项为空则用新生成的node替换
                    break;                   // no lock when adding to empty bin
            }
            else if ((fh = f.hash) == MOVED) // 该结点的hash值为MOVED
                // 进行结点的转移(在扩容的过程中)
                tab = helpTransfer(tab, f);
            else {
                V oldVal = null;
                synchronized (f) { // 加锁同步
                    if (tabAt(tab, i) == f) { // 找到table表下标为i的节点
                        if (fh >= 0) { // 该table表中该结点的hash值大于0
                            // binCount赋值为1
                            binCount = 1;
                            for (Node<K,V> e = f;; ++binCount) { // 无限循环
                                K ek;
                                if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) { // 结点的hash值相等并且key也相等
                                    // 保存该结点的val值
                                    oldVal = e.val;
                                    if (!onlyIfAbsent) // 进行判断
                                        // 将指定的value保存至结点,即进行了结点值的更新
                                        e.val = value;
                                    break;
                                }
                                // 保存当前结点
                                Node<K,V> pred = e;
                                if ((e = e.next) == null) { // 当前结点的下一个结点为空,即为最后一个结点
                                    // 新生一个结点并且赋值给next域
                                    pred.next = new Node<K,V>(hash, key,
                                                              value, null);
                                    // 退出循环
                                    break;
                                }
                            }
                        }
                        else if (f instanceof TreeBin) { // 结点为红黑树结点类型
                            Node<K,V> p;
                            // binCount赋值为2
                            binCount = 2;
                            if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                           value)) != null) { // 将hash、key、value放入红黑树
                                // 保存结点的val
                                oldVal = p.val;
                                if (!onlyIfAbsent) // 判断
                                    // 赋值结点value值
                                    p.val = value;
                            }
                        }
                    }
                }
                if (binCount != 0) { // binCount不为0
                    if (binCount >= TREEIFY_THRESHOLD) // 如果binCount大于等于转化为红黑树的阈值
                        // 进行转化
                        treeifyBin(tab, i);
                    if (oldVal != null) // 旧值不为空
                        // 返回旧值
                        return oldVal;
                    break;
                }
            }
        }
        // 增加binCount的数量
        addCount(1L, binCount);
        return null;
    }

HashMap  

  1. put 方法实现
    1. 根据key取hashcode 
    2. 判断此hashcode对应的值 (a) ==null,如果=null 直接插入
    3. 不能null 判断(a) 的hashcode 与key == 传进来的hashcode与key 相等,则替换
    4. 不等 如果 (a) 属于 TreeNode 则  ((TreeNode<K,V>)p).putTreeVal
    5. 不属于判断(a).next== null 如果为null 则 p.next = newNode ,然后判断entry个数 >=  阈值-1 , 大于则转换为红黑树
    6. 不大于 则判断(a).next  == newNode  相等则替换
    7. 如果(a).next  != null ,则 if (!onlyIfAbsent || oldValue == null) 则替换旧值

LinkedHashMap 

覆盖 new Node() 维护自身链表

Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
        LinkedHashMap.Entry<K,V> p =
            new LinkedHashMap.Entry<K,V>(hash, key, value, e);
        linkNodeLast(p);
        return p;
    }

Properties  

根据class Loader去加载文件 必须相同的classLoader才能加载到

properties.load(this.getClass().getClassLoader().getResourceAsStream("prop/my.properties"));

TreeMap  

红黑树,即平衡二叉搜索树

规则

变换: 变色 旋转

WeakHashMap

key是弱引用  有利于垃圾回收

在不使用弱引用时,比如,将对象放入hashmap中,当一个key对应的值为null时,并不能有效释放掉该对象空间,是因为hashmap依旧引用着该对象,导致GC时 root链还能搜索到该引用,不能进行对象GC

但是使用弱引用后,就很好解决这个问题,对象可以正常释放。

猜你喜欢

转载自blog.csdn.net/qq_29857681/article/details/89450008