什么是java集合
- 对线性表,链表,哈希表这些常用的数据结构,在进行Java开发时,JDK已经为我们提供了一系列相应的类来实现基本的数据结构。因此,我们一般的程序员不必自己开发相关的方法
- 集合(框架):java提供了一组对数组、链表数据结构操作的API,这组API即集合;存在于java.util
- Collection接口的依赖图
这篇文章主要讲List下ArrayList、LinkedList的两个实现。
List接口实现之–ArrayList
List 接口的大小可变数组的实现。实现了所有可选列表操作,并允许包括 null 在内的所有元素。线程不安全
1.使用
List list = new ArryList(); list.add(111); list.remove(下标|元素)
Arrays.asList(new Integer[]{1,2,3})
2.源码分析 构造方法 底层结构
//存储数据的桶
transient Object[] elementData;
//new 构造方法
public ArrayList() {
//elementData 就是一个空数组
//private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
3.添加元素add
//add
public boolean add(E e) {
//确保内部容量 传参当前数组长度+1
ensureCapacityInternal(size + 1);
//赋值且size+1
elementData[size++] = e;
return true;
}
//ensureCapacityInternal 计算与扩容
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//calculateCapacity
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//如果当前data == 默认的数组 即刚初始化添加元素的时候
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//取默认容量DEFAULT_CAPACITY=10 和所需容量两者之间的最大值
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
//不为空 之间返回所需容量
return minCapacity;
}
//ensureExplicitCapacity 扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
//所需容量大于当前数组的容量
if (minCapacity - elementData.length > 0)
//扩容 ###
grow(minCapacity);
}
//grow 扩容
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//旧数组的容量+本身右移1位 简单计算为右移的容量/2的一次方 即原始容量的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
//如果新容量还是少于所需容量 直接把容量设为所需容量
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
//取Integer.MAX_VALUE 或者 Integer.MAX_VALUE-8
//-8一些VM在数组中保留一些标题字,分配更大可能会导致OutOfMemoryError:请求的数组大小超过VM限制
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
//Arrays.copyOf 截取原数组或用 null 填充以获得指定的长度 扩容结束
elementData = Arrays.copyOf(elementData, newCapacity);
}
4.remove移除元素
//下标删除元素 index 所需移除的目标下标
public E remove(int index) {
//传入下标 index >= size 抛越界异常
rangeCheck(index);
modCount++;
//获取对应下标元素
E oldValue = elementData(index);
//这里是获取移除元素后面的个数
int numMoved = size - index - 1;
if (numMoved > 0)
//这里采用的是按下标移动数组的方式 下面是参数意义
//Object elementData: 原数组 int index+1 : 从元数据的起始位置开始
//Object elementData: 目标数组 int index : 目标数组的开始起始位置
//int numMoved: 要copy的数组的长度
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
List接口实现之 – LinkedList
List 接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括 null),线程不安全,使用是一样的
1.底层数据结构
//最底层的静态内部类链表
private static class Node<E> {
E item; //元素
Node<E> next; //下一个元素链表
Node<E> prev; //前一个元素链表
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
2.add添加元素
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
//把成员变量Node<E> last; 赋给 node l
final Node<E> l = last;
//new 一个新的node pre传last next传null
final Node<E> newNode = new Node<>(l, e, null);
//把当前操作的node赋给成员变量last 用于下次添加的l node
last = newNode;
//第一次添加肯定是空的
if (l == null)
//记录当前数组的第一个
first = newNode;
else
//赋值给l的下一个
l.next = newNode;
//加size
size++;
modCount++;
}
3.get获取元素
public E get(int index) {
//检查是否越界
checkElementIndex(index);
// 返回节点值 ###
return node(index).item;
}
//node
Node<E> node(int index) {
// assert isElementIndex(index);
//对半取法 小于一半从头开始检索到指定index下标
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
//取到目标下标前一个的next,也可以算是少了一次遍历
x = x.next;
return x;
} else {
//否则从后面开始检索
Node<E> x = last;
for (int i = size - 1; i > index; i--)
//同理
x = x.prev;
return x;
}
}
4.remove删除元素
//元素删除也是先找到下标 所以直接贴下标删除的代码
public E remove(int index) {
checkElementIndex(index);
//上面看了node方法 就是根据下标找到目标元素node
return unlink(node(index));
}
//unlink
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;
//分别取出目标元素的上下链
final Node<E> next = x.next;
final Node<E> prev = x.prev;
//直接连通
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
//直接连通
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
//返回删除元素
return element;
}
以上就是ArrayList、LinkedList源码分析的全部内容了。
下一篇:Java 集合 – Set接口实现之HashSet、TreeSet源码分析
纸上学来终觉浅,要知此事须躬行。