学习笔记-集合

记录,符合自己思维的描述,原码jdk17。

一、Collection(集合)

继承结构图: 图1:

在这里插入图片描述Collection 接口继承Iterable接口。所以所有实现Colletcion接口的类都在一个iterator()方法。该方法会返回一个Iterable对象(迭代器)。

1、List,有序可重复

(1)ArrayList

ArrayList.iterator()方法会返回一个Itr对象,该对象实现了Iterable接口。该类提供了两个int类型的属性,cursor(cursor代表下一个索引),lasRet=-1(最后一个元素索引,没有返回-1)。在next()方法中,cursor代表索引若小于当前集合的size(这里会进行两次if(索引>=长度)操作),会返回当前索引指向的对象。返回的对象是Object[]的指定下标的元素。该Object[]在ArrayList实例化时被创建出来,不指定大小的话则是一个空的Object[]。
ArrayList.add(Object)方法会调用重载的add(Object, Object[], int size),将Object 放入Object[]中,并将集合size+1,这里会先判断容量是否满足然后调用grow(),再调用重载grow(size+1)。不指定ArrayList大小,首次调用add会将ArrayList容量扩充为10。
ArraryList.remove()调用fastRemove(),将Object[]指定元素删除,size-1。
foreach()使用了Iterable。
ArrayList扩容机制:
不指定ArrayList大小,首次调用add会将ArrayList容量扩充为10。当容量不够时将容量扩充为当前容量的1.5倍,下图中oldCapacity >> 1 。指定ArrayList大小,当容量不够时将容量扩充为当前容量的1.5倍 。

在这里插入图片描述

(2)Vector

是线程安全的,方法上有synchronized关键字,适合多线程操作一个集合使用。
默认无参构造方法会调用重载有参方法传入初始容量为10。
在这里插入图片描述
需要扩容时,增加当前容量,即当前容量的2倍。
在这里插入图片描述

(3)LinkedList

有两个Node属性:
transient Node first;----指向上一个节点
transient Node last;----指向下一个节点
LinkedList.add(o),该方法调用了linkLast(o),这个方法很有意思的,很厉害!看源码

		void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }

首次添加节点时,first 和last 都会指向新的节点。再次添加时,first指向首节点,last指向尾节点。听着就像是费话,实在不知道该怎么描述。就是觉得写的从很厉害,太聪明了。remove也差不多。脑子才是程序员的第一武器。(前途堪忧啊)

Vector和ArrayList都有一个核心属性数组,可以说它们两个是基于数组实现。而LinkedList是基于一个双向链表。ArrayList 和linkedList都不是线程安全的。而Vector是线程安全的。

2、Set,无序不重复

(1)HashSet

保存数据通过HashMap实现,HashMap通过数组加单向链表的形式实现。
创建实例时,调用了HashMap的无参构造方法,实例化了一个HashMap对象,给负载因子赋值0.75。
调用add方法调用map的put方法,put中调用和putVal(hash(key), key, value ,false, true)。
*hash()得到的是key的hashCode值与该值无符号左移16位进行异或后的int。为的是该值不重复*

			static final int hash(Object key) {
			        int h;
			        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
			    }

putVal中第一个if语句会判断当前table(核心Node数组)当前是否为空。为空调用resize()创建一个长度为16的Node[],设置其扩容临界值为当前数组长度16 * 负载因子0.75 = 12;第二个if会判断当前key要存放的位置是否为空,空则存放,不为空则判断当前要存放的key和链表中k是否相同,相同返回已存在的value,不同则保存到链表最后。当链表长度>8时,会调用treeifyBin()方法,判断数组长度是不超过64,超过则转成红黑树,不超过则将元素追加到链表后并对Node[]扩容到当前数组长度的2倍直到不小于64进行红黑树转化,此时扩容阈值也随着扩大2倍。第三个if会判断,当哈希表中的Node数量大于扩容阈值就进行扩容,这里的Node数量指的不仅仅是数组索引处的第一个元素,还包括数组索引处链表上的所有元素的数量

(2)LinkedHashSet

保存数据基于数组加双向链表实现。
创建LinkedhashSet调用了LinkedHashMap构造器,LHM又调用了HashMap()构造器Node[]数组长度为16,负载因子为0.75。值得注意的是这里table(Node[])存放的元素是LinkedHashMap内部静态类Entry,该类继承了HashMap.Node,多了2个Entry属性:before指向存入的前一个元素,after指向存入的下一个元素。且该集合有head,tail两个Entry属性分别指向头尾节点。
在这里插入图片描述
在这里插入图片描述

(3)TreeSet

继承结构图: 图2:

在这里插入图片描述

二、 Map,保存映射关系键值对(k-v)数据的集合

1、HashMap

参考HashSet。
key相同时,再次put,新value会替换旧value。
key不可以重复,value可以重复。
key只可以保存一个null, value可以保存多个null。
存取顺序不同。
不是线程安全。
在这里插入图片描述

EntrySet extends AbstractSet<Map.Entry<K,V>>是HashMap的内部类。

该类并没有属性用来保存table-----Node<K,V>[] (或者说是K-V),只是在其内部的方法引用了table。
如:
在这里插入图片描述

2、Hashtable

存储方式是数组加单向链表
存取顺序不同。
线程安全的。
Key,Value都不能为null。

实例化时new Hashtable();会调用this(11, 0.75),设置初始数组长度为11,加载因子为0.75,阈值为数组长度 * 加载因子。
put时,会判断k,v是否为null,不为空,根据hashCode获取对应数组下标,判断该索引处的链表是否存在相同的k,若存在新v替换旧v;若不存在相同的k,调用addEntry(),首先会所有元素个数是否大于等于阈值;超过调用rehash()进行扩容:数组长度 * 2 + 1 ,阈值为数组长度 * 加载因子,同时将旧数组所有元素复制到新数组中。回到不存在相同k,会new Entry,其属性next将指向新数组对应的索引处,将new Entry放在对应数组索引处完成添加。put方法加了关键字synchronized,所以是线程安全的。

3、Properties

数组加单向链表。
该类继承了Hashtable。但是存储数据没有使用从Hashtable继承过来的table属性,而是使用自己的map属性—ConcurrentHashMap.Node<K,V>[]。put时初始长度为16。

4、TreeMap

在这里插入图片描述
HashMapjdk8是数组+链表+树,jdk7是数组+链表

三、 Collections 工具类

sort();排序时,可以自定义比较器Comparator作为参数。
copy();拷贝数组,目标数组的size要大于等于源数组的size;不是数组长度,而是数组中元素的个数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值