一、List接口
1、List
集合类中元素有序,且可重复
2、List
集合中的每个元素都有其对应的顺序索引,即支持索引
package com.level7.List_;
import java.util.ArrayList;
import java.util.List;
public class List_ {
public static void main(String[] args) {
// 1.List集合类中元素有序,且可重复
List list = new ArrayList();
list.add(123);
list.add("xrj");
list.add("xyy");
list.add("xrj");
System.out.println(list);
// 2.List 集合中的每个元素都有其对应的顺序索引,即支持索引
System.out.println(list.get(2));
}
}
List接口常用方法
package com.level7.List_;
import java.util.ArrayList;
import java.util.List;
public class ListMethod {
public static void main(String[] args) {
List list = new ArrayList();
list.add("张三丰");
list.add("贾宝玉");
// void add(int index, Object ele):在 index 位置插入 ele 元素
list.add(1, "xrj");
System.out.println(list);
// boolean addAll(int index, Collection eles):从 index 位置开始将 eles 中的所有元素添加进来
List list2 = new ArrayList();
list2.add("jack");
list2.add("tom");
list.addAll(2, list2);
System.out.println(list);
// Object get(int index):获取指定 index 位置的元素
System.out.println(list.get(3));
// int indexOf(Object obj):返回 obj 在集合中首次出现的位置
System.out.println(list.indexOf("tom"));
// int lastIndexOf(Object obj):返回 obj 在当前集合中末次出现的位置
list.add("xrj");
System.out.println(list);
System.out.println(list.lastIndexOf("xrj"));
// Object remove(int index):移除指定 index 位置的元素,并返回此元素
System.out.println(list.remove(0));
System.out.println(list);
// Object set(int index, Object ele):设置指定 index 位置的元素为 ele , 相当于是替换.
list.set(1, "玛丽");
System.out.println(list);
// List subList(int fromIndex, int toIndex):返回从 fromIndex 到 toIndex 位置的子集合
// fromIndex <= subList < toIndex
List returnlist = list.subList(1, 4);
System.out.println(returnlist);
}
}
二、ArrayList底层结构和源码分析
ArrayList注意事项
1、ArrayList
可以加入null
,并且可以放多个
2、ArrayList
是由数组来实现数据存储的
3、ArrayList
基本等同于Vector
,ArrayList
是线程不安全的,因为源码没有synchronized
关键字修饰,但是执行效率高。多线程情况下不建议使用ArrayList
ArrayList源码分析
1、ArrayList
中维护了一个Object
类型的数组transient Object[] elementData;
transient
表示瞬间、短暂,表示该属性不会被序列化
2、当创建ArrayList
对象时,如果使用的是无参构造器,则初始化elementData
的容量为0,第一次添加,则扩容elementData
为10,如需再次扩容,则扩容
elementData
为1.5倍
3、如果使用的是指定大小的构造器,则初始elementData
容量为指定大小,如需扩容,直接扩容为elementData
的1.5倍
package com.level7.List_;
import java.util.ArrayList;
public class ArrayListSource {
public static void main(String[] args) {
ArrayList list = new ArrayList();
// ArrayList 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);
}
}
使用无参构造器创建ArrayList
1、执行ArrayList list = new ArrayList();
将elementData
赋一个空数组
2、执行list.add(i);
由于是无参构造,所以第一次扩容容量为10
2.1 add时,先判断当前容量是否可用(初始容量为0)。先确定是否需要扩容,然后在赋值
2.2 计算当前所需最小容量
2.3 传入的Object[] elementData
就是elementData
,minCapacity
是1,elementData
初始的时候就是空,因此执行if中的语句,DEFAULT_CAPACITY
是10,因此返回10
2.4 现在所需最小容量为10,但是elementData
的容量为0,所以需要扩容,进入grow
方法进行扩容。modCount++
记录集合被修改的次数,即add的次数
2.5 oldCapacity
现在是0,newCapacity
是oldCapacity
的1.5倍,第一次扩容,所以newCapacity
也是0,然后0-10
小于0,执行第一个if,将newCapacity
置为10,最后执行elementData = Arrays.copyOf(elementData, newCapacity);
使用copyOf
将原数组拷贝到新数组,并将新数组扩容。
2.6 然后就一直返回,将当前add的值加入list中
3、当前容量为10,如果需要存放11个数据时,会继续扩容,这时就是扩容到原来的1.5倍即15个大小
使用有参构造器创建ArrayList
1、执行ArrayList list = new ArrayList(8);
为list创建一个大小为8的数组,后边容量不够就扩容为原始容量1.5倍,和上边流程一致
三、Vector
Vector介绍
1、Vector
底层也是一个数组 protected Object[] elementData;
2、Vector
是线程同步的,即线程安全,它的方法都带有synchronized
关键字
3、Vector
的无参构造默认初始化数组大小为10,如果需要扩容,每次扩容两倍
4、Vector
的有参构造,初始化时容量为指定大小,需要扩容,每次扩容两倍
Vector底层分析
package com.level7.List_;
import java.util.Vector;
public class Vector_ {
public static void main(String[] args) {
// Vector vector = new Vector();
Vector vector = new Vector(8);
for (int i = 0; i < 20; i++) {
vector.add(i);
}
vector.add(100);
System.out.println("vector=" + vector);
}
}
Vector无参构造
1、执行Vector vector = new Vector();
语句进入无参构造方法,调用有参构造方法,初始容量为10
初始指定initialCapacity
为10,capacityIncrement
为0
2、执行vector.add(i)
,先判断是否需要扩容,然后赋值,modCount
表示add多少次
3、如果当前所需最小容量大于数组容量,那么就扩容
4、如果需要扩容,就进入grow
方法。oldCapacity
是原数组大小,如果capacityIncrement
大于0,那么我们的容量就增加capacityIncrement
,但是capacityIncrementment
默认为0,所以newCapacity
就是oldCapacity
的两倍,然后使用copyOf
方法扩容
5、依次返回,将add的值加进去
Vector有参构造
1、执行Vector vector = new Vector(8);
,进入该方法,数组大小初始化为指定大小,如果需要继续扩容和无参构造一样
ArrayList和Vector对比
底层结构 | 出现版本 | 线程安全 | 扩容倍数 | |
---|---|---|---|---|
ArrayList | transient Object[] elementData | JDK1.2 | 不安全,效率高 | 如果是有参构造,每次扩容1.5倍。如果是无参,初始化大小为0,第一次扩容为10,然后每次扩容1.5倍。 |
Vector | protected Object[] elementData | JDK1.0 | 安全,效率不高 | 如果是无参,初始化大小为10,每次扩容2倍。如果指定大小,每次扩容2倍。 |
四、LinkedList
LinkedList介绍
1、LinkedList底层实现了双向链表和双端队列特点
2、可以添加任意元素(元素可以重复),包括null
3、线程不安全,没有实现同步
1、LinkedList
底层维护了一个双向链表
2、LinkedList
中维护了两个属性first
和last
分别指向首节点和尾结点
3、每个节点(Node对象),里边又维护了prev
,next
、item
三个属性,其中通过prev
指向前一个,通过next
指向后一个节点
模拟双向链表
package com.level7.List_;
public class LinkedList01 {
public static void main(String[] args) {
Node jack = new Node("jack");
Node tom = new Node("tom");
Node xrj = new Node("xrj");
jack.next = tom;
tom.next = xrj;
tom.pre = jack;
xrj.pre = tom;
Node first = jack;
Node last = xrj;
// 在tom和xrj之间增加一个xyy
Node xyy = new Node("xyy");
xyy.pre = xrj.pre;
xyy.next = tom.next;
tom.next = xyy;
xrj.pre = xyy;
while (first != null) {
System.out.println(first);
first = first.next;
}
while (last != null) {
System.out.println(last);
last = last.pre;
}
}
}
class Node {
public Object item;
public Node next;
public Node pre;
public Node(Object item) {
this.item = item;
}
@Override
public String toString() {
return "item = " + item;
}
}
LinkedList底层操作机制
package com.level7.List_;
import java.util.LinkedList;
public class LinkedListSource {
public static void main(String[] args) {
LinkedList list = new LinkedList();
list.add(10);
list.add(15);
list.add(20);
list.add(1, 50);
System.out.println(list);
// 删除节点
list.remove(); // 默认删除第一个节点
System.out.println(list);
// 修改某个节点
Object origin = list.set(1, 100); // 返回的是修改前的元素
System.out.println(origin);
System.out.println(list);
// 得到节点对象
Object o = list.get(2);
System.out.println(o);
}
}
插入元素
1、LinkedList list = new LinkedList();
调用该语句后,执行构造器
2、list.add(10);
调用add方法后。会进入linkLast(e)
这个方法
3、在该方法中,首先让 l 指向当前链表的尾指针即last节点,然后构建新节点,他的pre指针就是 l ,next指针为空,item就是当前add的值。然后让last指针指向新的节点,如果 l 为空,表示当前链表为空,那么这个新节点就是链表第一个节点,就让first指针也指向这个节点。如果不是第一次add,则 l 指向上一个节点,一定不为空,然后 l.next = newNode;
让上一个节点的next指针指向这个新节点
4、这是new Node<>(l, e, null)
构造器
指定位置插入元素
1、list.add(1, 50);
在第一个位置插入50这个元素,进入public void add(int index, E element)
这个方法,首先判断当前插入的位置index是否合法,即index >= 0 && index <= size;
如果不合法会抛出下标越界异常。然后判断如果index == size
说明在末尾插入,否则执行else的语句
2、进入node方法,首先找到插入的位置x是一个Node类型
3、进入linkBefore
方法,succ
就是需要插入的位置,然后将元素插入进去
删除节点
1、执行list.remove();
默认删除第一个节点。执行该语句后进入remove
方法
2、判断first节点是否为空,如果为空就抛出异常
3、进入private E unlinkFirst(Node<E> f)
,f为first节点,next为它的下一个节点,将要删除的这个f的item和next置为空,然后让first节点指向它的下一个节点,首节点就被删除了。如果next节点也为空,说明链表为空,那么让last也为空,否则就是让当前头结点的pre指针为空,然后返回删除的元素。
ArrayList和LinkedList的比较
底层结构 | 增删的效率 | 改查效率 | |
---|---|---|---|
ArrayList | 可变数组 | 较低,数组扩容 | 较高 |
LinkedList | 双向链表 | 较高,通过链表追加 | 较低 |