一、集合体系图介绍
对上面的接口,抽象类,实现类,进行说明
1.所有集合类都位于java.util包下。Java的集合类主要由两个接口派生而出:Collection和Map,Collection和Map是Java集合框架的根接口,这两个接口又包含了一些子接口或实现类。
2. 集合接口:6个接口(短虚线表示),表示不同集合类型,是集合框架的基础。
3. 抽象类:5个抽象类(长虚线表示),对集合接口的部分实现。可扩展为自定义集合类。
4. 实现类:8个实现类(实线表示),对接口的具体实现。
5. Collection 接口是一组允许重复的对象。
6. Set 接口继承 Collection,集合元素不重复。
7. List 接口继承 Collection,允许重复,维护元素插入顺序。
8. Map接口是键-值对象,与Collection接口没有什么关系。
9.Set、List和Map可以看做集合的三大类:
List集合是有序集合,集合中的元素可以重复,访问集合中的元素可以根据元素的索引来访问。
Set集合是无序集合,集合中的元素不可以重复,访问集合中的元素只能根据元素本身来访问(也是集合里元素不允许重复的原因)。
Map集合中保存Key-value对形式的元素,访问时只能根据每项元素的key来访问其value。
二、java中的运算符
<< : 左移运算符,num << 1,相当于num乘以2
>> : 右移运算符,num >> 1,相当于num除以2
>>> : 无符号右移,忽略符号位,空位都以0补齐
上面这个一定要搞清楚,因为我们在下面手写ArrayList的时候会用到扩容机制,JDK的是实现方式就是通过位运算来1.5倍扩容数组的,这个我们下面将详细介绍
三、数组拷贝基础知识
Arrays.copyOf功能是实现数组的复制,返回复制后的数组。参数是被复制的数组和复制的长度
System.arraycopy System.arraycopy方法:如果是数组比较大,那么使用System.arraycopy会比较有优势,因为其使用的是内存复制,省去了大量的数组寻址访问等时间
复制指定源数组src到目标数组dest。复制从src的srcPos索引开始,复制的个数是length,复制到dest的索引从destPos开始。
src:源数组; srcPos:源数组要复制的起始位置;
dest:目的数组; destPos:目的数组放置的起始位置; length:复制的长度。
注意:src and dest都必须是同类型或者可以进行转换类型的数组.
有趣的是这个函数可以实现自己到自己复制,比如:
int[] fun ={0,1,2,3,4,5,6};
System.arraycopy(fun,0,fun,3,3);
则结果为:{0,1,2,0,1,2,6};
实现过程是这样的,先生成一个长度为length的临时数组,将fun数组中srcPos
到srcPos+length-1之间的数据拷贝到临时数组中,再执行System.arraycopy(临时数组,0,fun,3,3).
结果:
结果:
注意:System.arraycopy 效率很高,底层走的C语言的方法
四、纯手写ArrayList底层源码(超详细)
前期准备:
- 底层使用数组实现
- 怎样保证集合中存放无限大小,###数组的扩容技术
- JDk1.7之后,数组默认大小扩容代码存放在add方法(JDK1.6初始化的时候默认构造函数初始化elementData大小)
- ArrayList底层数据初始化默认值为10
- ArrayList底层采用数组实现数组名称elementData
定义一个存储数组和实际的数组大小
//定义数组存储的数组
private transient Object[] elementData;
//定义实际数组的大小
private int size;
编写构造方法实现初始化容量
//定义初始化容量大小为10
private static final int DEFAULT_CAPACITY = 10;
//创建初始化容量,放在构造方法中
public NewArrayList() {
// 默认初始容量为10
size=DEFAULT_CAPACITY;
}
public NewArrayList(int initialCapacity){
//判断值是否为零
if (initialCapacity < 0) {
throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
}
// 初始化数组容量
elementData = new Object[initialCapacity];
}
添加元素后大于当前数组的长度,则进行扩容,将数组的长度增加原来数组的一半。
//扩容数组
private void ensureCapacityInternal(int minCapacity) {
// 如果存入的数据,超出了默认数组初始容量 就开始实现扩容
if (size == elementData.length) {
// 获取原来数组的长度 2
int oldCapacity = elementData.length;
// oldCapacity >> 1 理解成 oldCapacity/2 新数组的长度是原来长度1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1); // 3
if (newCapacity < minCapacity) {
// 最小容量比新容量要小的,则采用初始容量minCapacity
newCapacity = minCapacity;
}
//注意:源码中还有对 最大容量和最小容量的判断,我们在这个就不进行判断了
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
判断是否越界,然后添加,通过系统提供的System.arrayCopy方法
//添加方法
public void add(E e) {
//判断进行是否进行扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
//默认添加到数组的后面
elementData[size++] = e;
}
public void add(int index, Object object) {
//判断数组是否越界
rangeCheck(index);
//判断是否扩容
ensureCapacityInternal(size + 1);
//通过系统提供输的数组拷贝
System.arraycopy(elementData, index, elementData, index + 1, size - index);
//覆盖我们通过上面的方法,移出来的位置index ,我们进行覆盖就达到了添加的功能
elementData[index] = object;
//数组的实际长度增加
size++;
}
//判断数组是否越界
private void rangeCheck(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException("数组越界");
}
添加的原理图分析:
删除元素:两种形式id删除和对象删除
//删除 通过id
public Object remove(int index) {
//通过id获得该数据
Object object = get(index);
//计算需要移动的距离
int numMoved = elementData.length - index - 1;
//判断移动距离是否大于0 ,如果不大于0 就是数组的最后一个数
if (numMoved > 0)
System.arraycopy(elementData, index + 1, elementData, index, numMoved);
//移动后,最后一个置为空
elementData[--size] = null;
return object;
}
//删除对象
public Object remove(Object object) {
for (int i = 0; i < elementData.length; i++) {
Object element = elementData[i];
//通过数组遍历查询相等的对象
if (element.equals(object)) {
//相等就删除 注意:默认删除第一个
remove(i);
return true;
}
}
return false;
}
画图分析:
五、Vector和ArrayList的区别以及Vector底层实现原理
Vector是线程安全的,但是性能比ArrayList要低。
ArrayList,Vector主要区别为以下几点:
(1):Vector是线程安全的,源码中有很多的synchronized可以看出,而ArrayList不是。导致Vector效率无法和ArrayList相比;
(2):ArrayList和Vector都采用线性连续存储空间,当存储空间不足的时候,ArrayList默认增加为原来的50%,Vector默认增加为原来的一倍;
(3):Vector可以设置capacityIncrement,而ArrayList不可以,从字面理解就是capacity容量,Increment增加,容量增长的参数。
(4) :初始化容量为10
与ArrayList的不同之处我们看看:
下一节手写LinkedList底层实现过程