List 简介
List 是 java.util 包下面的类,是一个有序集合(有时被称为序列),List 可以包含重复的元素,其继承了 Collection 的操作。除此之外,List 本身也是一个接口,其还包括以下操作:
- 按位置访问:根据元素在序列中的位置索引访问元素。
- 查找:在序列中查找指定的对象,并返回其位置索引。
- 迭代:扩展了 Iterator 接口,以利用序列的顺序特性。
- List 子集合:在序列上执行任意范围的操作。
public interface List<E> extends Collection<E>
ArrayList
ArrayList 简介
ArrayList 基于数组实现,是一个动态的数组队列。但是它和 Java 中的数组又不一样,它的容量可以自动增长。
ArrayList 继承了 AbstractList,实现了 RandomAccess、Cloneable 和 Serializable 接口。
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
ArrayList 的容量为什么会自动增长?
ArrayList 的列表对象实质上是存储在一个引用型数组里的,有人认为该数组有“自动增长机制”可以自动改变 size 大小。准确地说,该数组是无法改变大小的,实际上它只是改变了该引用型数组的指向而已。下面,让我们来看看 java 是怎样实现 ArrayList 类的。
ArrayList 源代码
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
//创建 ArrayList 完成之后,默认的容量为 0。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// ArrayList 的扩容问题其实就是这个 Object 类型的数组的扩容问题
//创建一个容量为 x 的 ArrayList 对象,其实就是一个长度为 x 的 Object 数组
transient Object[] elementData;
private void grow(int minCapacity) {
// ArrayList的原始大小
int oldCapacity = elementData.length;
// 在原始大小的基础上计算扩充后的大小,扩充后的大小是元素大小的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//跟前面计算的扩充后长度 minCapacity 比较,取较大的那个为扩充后长度
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果扩充后长度大于最大长度
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 扩充
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
ArrayList 实质
ArrayList 底层采用 Object 类型的数组实现,当使用不带参数的构造方法生成 ArrayList 对象时,实际上会在底层生成一个的 Object 类型数组。首先,ArrayList 定义了一个私有的未被序列化的数组 elementData ,用来存储 ArrayList 的对象列表。
其次,以指定初始容量(Capacity)或把指定的 Collection 转换为引用型数组后实例化 elementData 数组;如果没有指定,则引用源码中的初始容量进行实例化。把私有数组预先实例化,然后通过 copyOf 方法覆盖原数组,是实现自动改变 ArrayList 的大小(size)的关键。
add() 与 addAll() 的区别
add 是将传入的参数作为当前 List 中的一个 Item 存储,即使你传入一个 List 也只会另当前的List增加1个元素。
addAll 是传入一个 List,将此 List 中的所有元素加入到当前List中,也就是当前 List 会增加的元素个数为传入的 List 的大小。
Collection demo1 = new ArrayList();
Collection demo2 = new ArrayList();
demo1.add(1);
demo1.add(2);
demo2.add("a");
demo2.add("b");
System.out.println("demo1:" + demo1);
System.out.println("demo1Size:" + demo1.size());
System.out.println("demo2:" + demo2);
System.out.println("demo2Size:" + demo2.size());
System.out.println();
demo1.add(demo2);
System.out.println("---------add--------");
System.out.println("demo1:" + demo1);
System.out.println("demo1Size:" + demo1.size());
System.out.println();
// 恢复原始的 demo1
demo1.remove(demo2);
demo1.addAll(demo2);
System.out.println("-------addAll------");
System.out.println("demo1:" + demo1);
System.out.println("demo1Size:" + demo1.size());
结果
demo1:[1, 2]
demo1Size:2
demo2:[a, b]
demo2Size:2
---------add--------
demo1:[1, 2, [a, b]]
demo1Size:3
-------addAll------
demo1:[1, 2, a, b]
demo1Size:4
创建几个 ArrayList
Person p1 = new Person();
p1.setName("Springer");
p1.setAge(18);
p1.setSex("male");
Person p2 = new Person();
p2.setName("Simple");
p2.setAge(38);
p2.setSex("female");
List<Person> demo1 = new ArrayList<Person>();
demo1.add(p1);
demo1.add(p2);
ArrayList<Integer> argList = new ArrayList<Integer>();
argList.add(1);
argList.add(2);
argList.add(3);
argList.add(4);
argList.add(5);
argList.add(6);
四种遍历方法:
1)通过迭代器遍历
Iterator<Person> it = demo1.iterator();
while(it.hasNext()){
System.out.print(it.next());
System.out.println();
}
2)通过增强 for 循环遍历
for(Person d : demo1){
System.out.println(d);
}
3)通过索引遍历
Iterator<Person> it = demo1.iterator();
for(int i = 0; i < demo1.size(); i++){
System.out.print(demo1.get(i));
System.out.println();
}
-
Lambda 表达式
demo1.forEach(System.out::println);
System.out.println();
结果
name:Springer,age:18,sex:male
name:Simple,age:38,sex:female
ArrayList 中几种常用的方法
1. 获取 List 中元素的个数
List<Person> demo1 = new ArrayList<Person>();
System.out.println("---------获取 List 中元素个数---------");
System.out.println(demo1.size());
---------获取 List 中元素个数---------
2
2.在指定位置插入元素
- argList.add(index, element)
index:插入位置的下标
element:插入的元素
System.out.println("---------添加前遍历---------");
for(int i = 0; i < argList.size(); i++){
System.out.print(argList.get(i) + " ");
}
argList.add(2, 7);
System.out.println("---------添加后遍历---------");
for(int i = 0; i < argList.size(); i++){
System.out.print(argList.get(i) + " ");
}
---------添加前遍历---------
1 2 3 4 5 6
---------添加后遍历---------
1 2 7 3 4 5 6
3.删除指定位置的元素
- argList.remove(index)
index:删除元素下标
argList.remove(2);
System.out.println("---------删除指定位置元素后遍历---------");
for(int i = 0; i < argList.size(); i++){
System.out.print(argList.get(i) + " ");
}
---------删除指定位置元素后遍历---------
1 2 3 4 5 6
4.删除指定元素
- argList.remove(Object)
Object:删除的元素
argList.remove((Object)5);
System.out.println("---------删除指定元素后遍历---------");
for(int i = 0; i < argList.size(); i++){
System.out.print(argList.get(i) + " ");
}
---------删除指定元素后遍历---------
1 2 3 4 6
5.判断元素是否存在
- argList.contains(Object)
Object:需要判断的元素
boolean obj3 = argList.contains(3);
boolean obj5 = argList.contains(5);
System.out.println("---------判断是否存在元素---------");
System.out.println("ArrayList contains 3 is: " + obj3);
System.out.println("ArrayList contains 5 is: " + obj5);
---------判断是否存在元素---------
ArrayList contains 3 is: true
ArrayList contains 5 is: false
6.清空集合
argList.clear()
System.out.println("---------清空前 argList 长度---------");
System.out.println("argList中元素个数:" + argList.size());
argList.clear();
System.out.println("---------清空后 argList 长度---------");
System.out.println("argList中元素个数:" + argList.size());
System.out.println("---------判断 argList 是否为空---------");
System.out.println("判断argList是否为空:" + argList.isEmpty());
---------清空前 argList 长度---------
argList中元素个数:5
---------清空后 argList 长度---------
argList中元素个数:0
---------判断 argList 是否为空---------
判断argList是否为空:true
LinkedList
LinkedList 简介
LinkedList 是一个继承于 AbstractSequentialList 的双向链表。它也可以被当作堆栈、队列或双端队列进行操作,其相对于 ArrayList 来说,是可以快速添加,删除元素,ArrayList 添加删除元素的话需移动数组元素,可能还需要考虑到扩容数组长度。
LinkedList 继承了 AbstractSequentialList,实现了 List、Deque、Cloneable 和Serializable 接口
public class LinkedList<E> extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
LinkedList 属性
LinkedList 本身的的属性比较少,主要有三个,一个是 size,代表当前有多少个节点;一个是 first,代表第一个节点;一个是 last,代表最后一个节点。
创建一个 LinkedList
LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("first");
linkedList.add("second");
linkedList.add("third");
System.out.println(linkedList);
[first, second, third]
LinkedList 中几种常用的方法
1. 在 LinkedList 中第一个节点添加元素
- addFirst(element)
element:需要添加的元素
linkedList.addFirst("addFirst");
System.out.println(linkedList);
[addFirst, first, second, third]
2. 在 LinkedList 中最后一个节点添加元素
- addLast(element)
element:需要添加的元素
linkedList.addLast("addLast");
System.out.println(linkedList);
[addFirst, first, second, third, addLast]
3. 在 LinkedList 中指定位置插入元素
- add(index,element)
index:插入位置的下标
element:需要添加的元素
linkedList.add(2, "addByIndex");
System.out.println(linkedList);
[addFirst, first, addByIndex, second, third, addLast]
LinkedList 和 ArrayList 的区别
ArrayList 与 LinkedList 都是 List 接口的实现类,因此都实现了 List 的所有未实现的方法,只是实现的方式有所不同,而 List 接口继承了 Collection 接口, Collection 接口又继承了 Iterable 接口,因此可以看出 List 同时拥有了 Collection 与 Iterable 接口的特性。
ArrayList 实现了 List 接口,它是以数组的方式来实现的,数组的特性是可以使用索引的方式来快速定位对象的位置, 因此对于快速的随机取得对象的需求,使用 ArrayList 实现执行效率上会比较好。
LinkedList 是采用链表的方式来实现 List 接口的,它本身有自己特定的方法,如: addFirst() , addLast() , getFirst(), removeFirst()等。 由于是采用链表实现的,因此在进行 insert 和 remove 动作时在效率上要比ArrayList要好得多!适合用来实现 Stack (堆栈)与 Queue (队列),前者先进后出,后者是先进先出。
关于 ArrayList 和 LinkedList 的效率问题
ArrayList 底层是用数组来保存对象的,这种方式将对象放在连续的位置中。
优点:可以通过数组下标快速的拿到值,大量修改时高效。
缺点:每一次添加和删除都需要将操作的元素后面的元素们全部移动,非常麻烦。
LinkedList 则是将对象放在独立的空间中,而且在每一个空间中存放下一个链接的索引。
优点:增加和删除非常快速,其底层是链表,插入元素只要打断这个节点插入元素即可。
缺点:要定位元素的位置时,要从头一个一个寻找,耗费资源。
package test;
import java.util.ArrayList;
import java.util.LinkedList;
public class TimeForList {
public static long getArrayListFindTime(){
ArrayList<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < 71000; i++) {
list.add(0,1);
}
long start = System.currentTimeMillis();
for (int i = 0; i < 71000; i++) {
list.get(i);
}
long end = System.currentTimeMillis();
return end-start;
}
public static long getLinkedListFindTime(){
LinkedList<Integer> list = new LinkedList<Integer>();
for (int i = 0; i < 71000; i++) {
list.add(0,1);
}
long start = System.currentTimeMillis();
for (int i = 0; i < 71000; i++) {
list.get(i);
}
long end = System.currentTimeMillis();
return end-start;
}
public static long getLinkedListAddTime(){
LinkedList<Integer> list = new LinkedList<Integer>();
long start = System.currentTimeMillis();
for (int i = 0; i < 71000; i++) {
list.add(0,1);
}
long end = System.currentTimeMillis();
return end-start;
}
public static long getArrayListAddTime(){
ArrayList<Integer> list = new ArrayList<Integer>();
long start = System.currentTimeMillis();
for (int i = 0; i < 71000; i++) {
list.add(0, 1);
}
long end = System.currentTimeMillis();
return end-start;
}
public static long getLinkedListAddLastTime(){
LinkedList<Integer> list = new LinkedList<Integer>();
long start = System.currentTimeMillis();
for (int i = 0; i < 71000; i++) {
list.add(i,1);
}
long end = System.currentTimeMillis();
return end-start;
}
public static long getArrayListAddLastTime(){
ArrayList<Integer> list = new ArrayList<Integer>();
long start = System.currentTimeMillis();
for (int i = 0; i < 71000; i++) {
list.add(i, 1);
}
long end = System.currentTimeMillis();
return end-start;
}
public static void main(String[] args) {
System.out.println("用LinkedList完成添加操作(开头添加)所需时间为:" + getLinkedListAddTime() + "ms");
System.out.println( "用Linked完成添加操作(末尾添加)所需时间为:" + getLinkedListAddLastTime() + "ms");
System.out.println("用ArrayList完成添加操作(开头添加)所需时间为:" + getArrayListAddTime() + "ms");
System.out.println( "用ArrayList完成添加操作(末尾添加)所需时间为:" + getArrayListAddLastTime() + "ms");
System.out.println("用LinkedList完成查询操作所需时间为:" + getLinkedListFindTime() + "ms");
System.out.println("用ArrayList完成查询操作所需时间为:" + getArrayListFindTime() + "ms");
}
}
结果
用 LinkedList 完成添加操作(开头添加)所需时间为:15ms
用 Linked 完成添加操作(末尾添加)所需时间为:17ms
用 ArrayList 完成添加操作(开头添加)所需时间为:941ms
用 ArrayList 完成添加操作(末尾添加)所需时间为:11ms
用 LinkedList 完成查询操作所需时间为:4457ms
用 ArrayList 完成查询操作所需时间为:2ms
由此可以直观的看出 LinkedList 和 ArrayList 的效率
点击下载文章内容原代码
提取密码:jnfj
本文内容部分取自百度内容,如有雷同部分请见谅。