业内经常说的一句话是不要重复造轮子,但是有时候,只有自己造一个轮子了,才会深刻明白什么样的轮子适合山路,什么样的轮子适合平地!
我将会持续更新java基础知识,欢迎关注。
往期章节:
JAVA基础第三章-类与对象、抽象类、接口
说起集合框架,很多面试官在面试初级javaer的时候也是很喜欢问的一个知识点
我们先上一张图看看
从上面的关系图中,我们可以看到从上往下分呢~最上面的是接口,中间是抽象类,最下面就是各个具体的实现类,这个在我们上一章节中说到的抽象类与接口之间的关系的时候有提到过。
再从左往右看呢,也是大致分三块,Iterator,Collection,Map 三个最顶级的接口,但是最主要的部分还是Collection,Map 2个接口,而Iterator更多的像是附加产品。
Collection
我们先看看Collection接口,从这个接口往下,他的子接口有List、Set、Queue。
List
List的具体实现类有 ArrayList、LinkedList,Vector。
ArrayList
顾名思义,是一个“数组型”的集合,对于数组,我们应该知道的是,数组在定义的时候就确定了大小,不可更改。那优点就是数组的元素是通过索引访问的,效率较高,因为数组是一块连续的存储空间。
所以呢,ArrayList 就是在数组的基础上,增加了可以改变大小的接口(方法),如 add 、remove 等方法,方便我们去操作修改当前集合中的数据元素,当集合中新添加的数据超过了当前的存储空间大小时
会申请一个新的存储空间,然后将这些已有的数据拷贝过去,再添加新的数据 ,扩容后的集合的大小等于扩容前集合的1.5倍。
当我们删除一个元素的时候,会将当前被删除元素之后的元素统一向前移动一位,然后将最后的一位元素置为null,以便于gc回收。
所以,如果我们对一个ArrayList 有频繁的增删操作,这样对性能是一个极大的损耗。
ArrayList 的数据存储结构示意图如下:
假设上图中每一个黄色的格子都代表一个ArrayList 的存储空间
步骤1:在我们第一次调用add方法增加一个元素1的时候,那么list会直接扩容为默认的大小10,我们也可以在调用ArrayList 构造函数的时候传入参数,指定初始化空间大小;
步骤2:我们再继续添加数据,直到添加到11时,会判断当前的存储空间放不下要增加的数据了,这个时候会继续扩容,之后再放入数据11;
步骤3:在这一步,我们决定删除数据2,2的下标为1(数组的下标都是从0开始),也就是调用remove方法;
注意:当我们调用size方法获取到的是实际的存储的数据大小,而不是整个ArrayList 获得的存储空间大小,例如 ,步骤2中调用size方法返回的会是11,而不是15。
LinkedList
从这个名字上,我们也可以大概知道,link是关联的意思。LinkedList 和ArrayList 不同的一点是,他实现了Deque接口 这是一个双向链表的接口。
我们先看下存储结构示意图:
如上图中所示,每一个节点都是一个Node对象,其中每个Node都有三个属性,item 实际存储的数据元素,如上图中的绿色格子,next和prev,这样就构成了一个链表结构。
而要注意的是next 和prev 也是一个Node对象,而Node是LinkedList 中的静态内部类。如下图中代码所示:
在这个链表中还存在2个属性 first 和 last,分别用于存放整个集合链表中的头和尾,如果只有一个元素,那么first 和last就指向同一个元素。
数据的添加
当我们在链表中添加一个元素的时候,最后一个元素的null位置会设置引用新的Node节点,然后新添加的节点的prev会指向前一个元素的位置
我们从LinkedList 源码中做一些简单的分析
1 /** 2 * Appends the specified element to the end of this list. 3 * 4 * <p>This method is equivalent to { @link #addLast}. 5 * 6 * @param e element to be appended to this list 7 * @return { @code true} (as specified by { @link Collection#add}) 8 */ 9 public boolean add(E e) { 10 linkLast(e); 11 return true; 12 }
如上所示,从 “Appends the specified element to the end of this list.” 这句注释中,我们就大致可以明白其意,当我们调用add方法增加元素的时候,默认是在末尾追加数据。
这个时候add方法中会调用linkLast方法,具体代码如下:
1 /** 2 * Links e as last element. 3 */ 4 void linkLast(E e