建议Debug模式调试
一、ArrayList的底层操作机制源码分析
- ArrayList中维护了一个Object类型的数组elementData。
//TODO transient:表示瞬间的、短暂的;表示该属性不会被序列化
transient Object[] elementData
- 当创建ArrayList对象时,如果使用的是无参构造器,则初始化elementData容量为0,第一次添加,则扩容elementData为10,如需要再次扩容,则扩容elementData为1.5倍
- 如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍
/**
* ArrayList采用无参构造器创建
*/
private static void noArgumentConstructor() {
//使用无参构造器创建ArrayList对象
ArrayList<Integer> list = new ArrayList<>();
//使用有参构造器创建ArrayList对象
// ArrayList<Integer> list = new ArrayList<>(8);
//使用for循环给list集合添加1-10数据
for (int i = 1; i <= 10; i++) {
list.add(i);
}
//使用for循环给list集合添加11-15数据
for (int i = 11; i <= 15; i++) {
list.add(i);
}
list.add(100);
list.add(200);
list.add(null);
}
二、LinkedList源码分析
1、LinkedList 的全面说明
- LinkedList底层实现了双向链表和双端队列特点
- 可以添加任意元素(元素可以重复),包括null
- 线程不安全,没有实现同步
2、LinkedList 的底层操作机制
- LinkedList底层维护了一个双向链表
- LinkedList中维护了两个属性first和last分别指向首节点和尾节点
- 每个节点(Node对象),里面又维护了prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个节点。最终实现双向链表
- 所以LinkedList的元素的添加和删除,不是通过数组完成的。相对来说效率较高
3、模拟LinkedList双向链表
- 定义一个内部类Node,表示双向链表的一个结点
/**
* 定义一个 Node 类,Node 对象 表示双向链表的一个结点
*/
static class Node {
public Object item;
public Node next;
public Node pre;
public Node(Object name) {
this.item = name;
}
public String toString() {
return "Node name=" + item;
}
}
main方法
public static void main(String[] args) {
//模拟一个简单的双向链表
Node jack = new Node("jack");
Node tom = new Node("tom");
Node hsp = new Node("老刘");
//连接三个结点,形成双向链表
//jack -> tom -> hsp
jack.next = tom;
tom.next = hsp;
//hsp -> tom -> jack
hsp.pre = tom;
tom.pre = jack;
Node first = jack;//让 first 引用指向 jack,就是双向链表的头结点
Node last = hsp; //让 last 引用指向 hsp,就是双向链表的尾结点
//演示,从头到尾进行遍历
System.out.println("===从头到尾进行遍历===");
while (true) {
if (first == null) {
break;
}//输出 first 信息
System.out.println(first);
first = first.next;
}
//演示,从尾到头的遍历
System.out.println("====从尾到头的遍历====");
while (true) {
if (last == null) {
break;
}//输出 last 信息
System.out.println(last);
last = last.pre;
}
//演示链表的添加对象/数据,是多么的方便
//要求,是在 tom --------- 老刘直接,插入一个对象 smith
//1. 先创建一个 Node 结点,name 就是 smith
Node smith = new Node("smith");
//下面就把 smith 加入到双向链表了
smith.next = hsp;
smith.pre = tom;
hsp.pre = smith;
tom.next = smith;
//让 first 再次指向 jack
first = jack;//让 first 引用指向 jack,就是双向链表的头结点
System.out.println("===从头到尾进行遍历===");
while (true) {
if (first == null) {
break;
}
//输出 first 信息
System.out.println(first);
first = first.next;
}
last = hsp; //让 last 重新指向最后一个结点
//演示,从尾到头的遍历
System.out.println("====从尾到头的遍历====");
while (true) {
if (last == null) {
break;
}
//输出 last 信息
System.out.println(last);
last = last.pre;
}
}
4、LinkedList增删改查源码分析
public static void linkedListCrud() {
LinkedList linkedList = new LinkedList();
linkedList.add(1);
linkedList.add(2);
linkedList.add(3);
System.out.println("linkedList=" + linkedList);
//演示一个删除结点的
linkedList.remove(); // 这里默认删除的是第一个结点
// linkedList.remove(2);
System.out.println("linkedList=" + linkedList);
//修改某个结点对象
linkedList.set(1, 999);
System.out.println("linkedList=" + linkedList);
//得到某个结点对象
//get(1) 是得到双向链表的第二个对象
Object o = linkedList.get(1);
System.out.println(o);//999
//因为 LinkedList 是 实现了 List 接口, 遍历方式
System.out.println("===LinkeList 遍历迭代器====");
Iterator iterator = linkedList.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println("next=" + next);
}
System.out.println("===LinkeList 遍历增强 for====");
for (Object o1 : linkedList) {
System.out.println("o1=" + o1);
}
System.out.println("===LinkeList 遍历普通 for====");
for (int i = 0; i < linkedList.size(); i++) {
System.out.println(linkedList.get(i));
}
/**
* TODO 链表add元素:源码阅读.
*
* 1、 LinkedList linkedList = new LinkedList();
* public LinkedList() {}
* 2、这时 linkeList 的属性 first = null last = null
* 3. 执行 添加
* public boolean add(E e) {
* linkLast(e);
* return true;
* }
* 4.将新的结点,加入到双向链表的最后
* 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++;
* }
*
*/
//TODO linkedList.remove();:源码阅读.
/**
* 1. 执行 removeFirst
* public E remove() {
* return removeFirst();
* }
* 2. 执行
* public E removeFirst() {
* final Node<E> f = first;
* if (f == null)
* throw new NoSuchElementException();
* return unlinkFirst(f);
* }
* 3. 执行 unlinkFirst, 将 f 指向的双向链表的第一个结点拿掉
* private E unlinkFirst(Node<E> f) {
* // assert f == first && f != null;
* final E element = f.item;
* final Node<E> next = f.next;
* f.item = null;
* f.next = null; // help GC
* first = next;
* if (next == null)
* last = null;
* else
* next.prev = null;
* size--;
* modCount++;
* return element;
* }
*
*/
}
三、Vector源码分析
public static void main(String[] args) {
//
Vector<Object> vector = new Vector<>();
for (int i = 0; i < 10; i++) {
vector.add(i);
}
vector.add(100);//扩容
System.out.println("vector=" + vector);
//源码分析
//1、new Vector<>()实际上还是调用了有参的构造方法,默认容量为10个
//2、调用add(),内部调用ensureCapacityHelper来确定是否需要扩容(minCapacity - elementData.length > 0则需要扩容)
//3、vector.add(100)时,触发扩容
}
四、Vector和ArrayList的比较
| 名字 | 底层结构 | 版本 | 线程安全(同步)效率 | 扩容备份 |
|---|---|---|---|---|
| ArrayList | 可变数组 | jdk1.2 | 不安全,效率高 | 如果使用有参构造器按照1.5倍扩容,如果是无参构造器。1.第一次扩容10;2.从第二次开始按照1.5倍 |
| Vector | 可变数组Object[] | jdk1.0 | 安全,效率不高 | 如果是无参,默认10,满后,按照2倍扩容。如果是指定大小创建Vector,则每次按照2倍扩容. |

本文对Java中List相关数据结构进行源码分析。涵盖ArrayList底层操作机制,包括不同构造器下的扩容规则;LinkedList的全面说明、底层双向链表机制、模拟实现及增删改查源码;还涉及Vector源码分析,并对Vector和ArrayList进行了比较。
772

被折叠的 条评论
为什么被折叠?



