Java集合(2)——深入理解ArrayList、Vector和LinkedList
回顾
Java集合主要分为两个体系结构,Collection和Map。这篇博客主要介绍Collection子接口List下的三个经常使用的实现类:ArrayList、Vector和LinkedList。
详细内容参见《Java基础——集合》
先看下关系图:
1、ArrayList
这是List最常用的实现类,想一想为什么他最常用?
Array,在java中意为“数组”。猜想ArrayList和数组应该关系很密切,其实ArrayList可以看作是一个可以改变大小的数组。
举个简单的例子吧,看下他的使用:
ArrayList<String> list1 = new ArrayList<>();
list1.add("a");
list1.add("b");
list1.add("c");
list1.set(2, "d");
Iterator<String> iter = list1.iterator();
while (iter.hasNext()) {
System.out.println(iter.next());
}
之后,我们就从源码看看他是如何设计的吧。
1)构造函数
提示:
默认ArrayList长度为10;
用于保存数据的elementData本身就是个Object[];
ArrayList提供了三种构造函数,具体逻辑可以看下面的代码。
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
//默认容量为10
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
//调用无参数构造函数时,给一个空数据
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//保存元素的数据
transient Object[] elementData;
//1、无参数构造函数,默认空数组
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//2、给定初始容量的构造函数
public ArrayList(int initialCapacity) {
//大于0时,创建一个Object数据,长度为传入的容量值
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
//等于0时,给一个空数组
this.elementData = EMPTY_ELEMENTDATA;
} else {
//其他抛出容量不合法异常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
//3、给定集合对象的构造函数
public ArrayList(Collection<? extends E> c) {
//放入数组
elementData = c.toArray();
//数组长度不等于0时,将进行复制
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
否则,返回空数组
this.elementData = EMPTY_ELEMENTDATA;
}
}
}
2)动态扩充
在添加元素时,会涉及到扩充的问题,其中核心方法是grow()。
提示:
“>>” 右移运算符:
在二进制中比较容易理解,这里不是重点,大致相当于值的二分之一。因此,有的书上会说ArrayList扩容一次会增加原来的一半,就是从这里看出来的。
private void grow(int minCapacity) {
//获取原来的容量,即原来数组的长度
int oldCapacity = elementData.length;
//新容量
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//复制数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
2、Vector
Vector和ArrayList几乎完全相同,直接看源码就知道了,瞬间有种剽窃的赶脚……
但其实不是的,Vector是比ArrayList出现的要早的,也正是因为这样,很多方法都比较陈旧了,并不推荐适用男。
这里贴了一小部分代码,和ArrayList几乎一样,就不占用空间了,有兴趣可以去java.util.Vector里面翻一翻。
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
protected Object[] elementData;
protected int elementCount;
public Vector() {
this(10);
}
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
当然Vector也有一些自己的特点,随便拿几个方法来看看。他的方法中几乎都用到了synchronized关键字,开销会比较大,而且运行会比较慢。
public synchronized void copyInto(Object[] anArray) {
System.arraycopy(elementData, 0, anArray, 0, elementCount);
}
public synchronized void setSize(int newSize) {
}
public synchronized int capacity() {
return elementData.length;
}
//数组个数
public synchronized int size() {
return elementCount;
}
//获取元素
public Enumeration<E> elements() {
return new Enumeration<E>() {
int count = 0;
public boolean hasMoreElements() {
return count < elementCount;
}
public E nextElement() {
synchronized (Vector.this) {
if (count < elementCount) {
return elementData(count++);
}
}
throw new NoSuchElementException("Vector Enumeration");
}
};
}
3、LinkedList
是一个双链表,在add和remove时比ArrayList性能好,但get和set时就特别慢了。
恶补下:
双向链表,链表的一种。每个数据结点中都有两个指针,分别指向直接前驱和直接后继。因此,我们可以方便的访问他的前驱结点和后继结点。
下图是一个双向链表的图,element为元素,pre指向直接前驱(前一个 元素),next指向直接后继(后一个元素)。而LinkedList还不只是双向链表,他是双向循环链表。也就是第一个pre指针指向最后一个节点,最后一个节点的next指针指向第一个节点,形成一个环路,而不是下图中的Null。
下面来看下代码
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable{
//元素个数
transient int size = 0;
//相当于pre指针
transient Node<E> first;
//相当于next指针
transient Node<E> last;
//Node内部类(双链表结构)
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;
}
}
......
}
Tips:
Deque,他是双端队列的接口,支持在两端插入和移除元素。
public interface Deque extends Queue {}
那么如何进行操作呢?这里以在中间位置添加元素为例。
代码实现如下
//在指定位置添加元素
public void add(int index, E element) {
//检查索引是否有效
checkPositionIndex(index);
//索引值等于集合大小,直接添加在末尾
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
//返回索引处的非空Node
Node<E> node(int index) {
// assert isElementIndex(index);
//如果在链表的前半段
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
//后半段
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
//中间插入元素(核心方法!!!!)
void linkBefore(E e, Node<E> succ) {
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
对于性能这块,在大数据量重复试验下得出的结论才比较有说服力,小编就随手贴上自己的测试结果,有兴趣看看就可以了。
测试代码差不多都是这样子的……
ArrayList<Integer> arrayList = new ArrayList<Integer>();
LinkedList<Integer> linkedList = new LinkedList<Integer>();
// add操作
int count = 100000;
System.out.println("ArrayList--add()-------------------");
long startTime = System.nanoTime();
for (int i = 0; i < count; i++) {
arrayList.add(i);
}
long endTime = System.nanoTime();
System.out.println(endTime - startTime + "--------");
System.out.println("LinkedList--add()-------------------");
startTime = System.nanoTime();
for (int i = 0; i < count; i++) {
linkedList.add(i);
}
endTime = System.nanoTime();
System.out.println(endTime - startTime + "--------");
循环1W次
循环10W次
循环100W次
等一下……
等一下……
再等一下……
出去接杯水……
结果还是这个样子,直接给个图吧,LinkedList还没get出来(⊙﹏⊙)b
嗯……这篇差不多了,over
Java集合(2)——深入理解ArrayList、Vector和LinkedList的更多相关文章
- 常用Java API: ArrayList(Vector) 和 LinkedList
摘要: 本文主要介绍ArrayList(Vector)和LinkedList的常用方法, 也就是动态数组和链表. ArrayList ArrayList 类可以实现可增长的对象数组. 构造方法 Arr ...
- Java集合系列(二):ArrayList、LinkedList、Vector的使用方法及区别
本篇博客主要讲解List接口的三个实现类ArrayList.LinkedList.Vector的使用方法以及三者之间的区别. 1. ArrayList使用 ArrayList是List接口最常用的实现 ...
- java集合【12】——— ArrayList,LinkedList,Vector的相同点与区别是什么?
目录 特性列举 底层存储结构不同 线程安全性不同 默认的大小不同 扩容机制 迭代器 增删改查的效率 总结一下 要想回答这个问题,可以先把各种都讲特性,然后再从底层存储结构,线程安全,默认大小,扩容机制 ...
- Java 集合系列03之 ArrayList详细介绍(源码解析)和使用示例
概要 上一章,我们学习了Collection的架构.这一章开始,我们对Collection的具体实现类进行讲解:首先,讲解List,而List中ArrayList又最为常用.因此,本章我们讲解Arra ...
- Java集合系列:-----------06List的总结(LinkedList,ArrayList等使用场景和性能分析)
现在,我们再回头看看总结一下List.内容包括:第1部分 List概括第2部分 List使用场景第3部分 LinkedList和ArrayList性能差异分析第4部分 Vector和ArrayList ...
- Java集合(3):Vector && Stack
一.Vector介绍 Vector可以实现可增长的动态对象数组.与数组一样,它包含可以使用整数索引进行访问的组件.不过,Vector的大小是可以增加或者减小的,以便适应创建Vector后进行添加或者删 ...
- Java集合(5):理解Collection
Collection是List.Set.Queue的共同接口.Collection主要方法有: int size():返回当前集合中元素的数量 boolean add(E e):添加对象到集合 boo ...
- 【Java集合】试读ArrayList源码
ArrayList简介 ArrayList 是一个数组队列,相当于 动态数组.与Java中的数组相比,它的容量能动态增长.它继承于AbstractList,实现了List, RandomAccess, ...
- Java 集合:HashSet 与 ArrayList
Set 集合是无序不可以重复的的.List 集合是有序可以重复的. Java 集合:HashSet 与 hashCode.equals 博客里面已经说到这个问题,但是解释的还是不够清楚. 看一个小例子 ...
- Java 集合系列03之 ArrayList详细介绍
ArrayList做为List接口中最常用的实现类,必须掌握. 一.ArrayList简介 与Java中的数组相比ArrayList的容量可以动态增加.它继承与AbstractList,实现了List ...
随机推荐
- rest_framework -- 认证组件
#####认证组件##### 一.认证是什么就不说了,某些网页必须是用户登陆之后,才能访问的,所以这时候就需要用上认证组件. 你不用rest_framework的认证组件也行,这种认证的话,完全可以自 ...
- NEC 框架规范 template media
<!DOCTYPE html><html><head><meta charset="utf-8"/><title>NEC ...
- [SCOI2009]windy数(数位DP)
题目描述 windy定义了一种windy数.不含前导零且相邻两个数字之差至少为2的正整数被称为windy数. windy想知道, 在A和B之间,包括A和B,总共有多少个windy数? 输入输出格式 输 ...
- SpringBoot配置redis和分布式session-redis
springboot项目 和传统项目 配置redis的区别,更加简单方便,在分布式系统中,解决sesssion共享问题,可以用spring session redis. 1.pom.xml <d ...
- 基于webSocket的聊天室
前言 不知大家在平时的需求中有没有遇到需要实时处理信息的情况,如站内信,订阅,聊天之类的.在这之前我们通常想到的方法一般都是采用轮训的方式每隔一定的时间向服务器发送请求从而获得最新的数据,但这样会浪费 ...
- [转]select top n 动态赋值
怎样实现 select top n 语句中 n 的动态赋值呢?怎样实现 select top n 语句中 n 的动态赋值,求教各位了. 要把这个n值传到存储过程中,再加入 select t ...
- PHP----composer安装和TP5验证码类
妈的,想用TP5做个项目,用到登录验证码了,结果煞笔TP5不内置了,需要用Composer,用吧,来下载 1.安装Composer 1.1 更新 sudo apt-get update 1.2 安装w ...
- scala成长之路(7)函数进阶——可能是史上最浅显易懂的闭包教程
由于scala中函数内部能定义函数,且函数能作为函数的返回值,那么问题来了,当返回的函数使用了外层函数的局部变量时,会发生什么呢?没错,就产生是闭包. 关于闭包的解释网上一大堆,但基本上都是照葫芦画瓢 ...
- 清华大学《C++语言程序设计基础》线上课程笔记01---基础概念与一些注意事项
使用除法的注意事项 double b = 4.0 * 1/239.0; 因为整数相除结果取整,如果参数写1/239,结果就都是0 浮点数注意事项 浮点数是近似存储,所以不能直接比较两个浮点数的大小, ...
- 勾股数--Python
勾股数:勾股数又名毕氏三元数 .勾股数就是可以构成一个直角三角形三边的一组正整数.勾股定理:直角三角形两条直角边a.b的平方和等于斜边c的平方(a²+b²=c²) 要求:输出1000以内的勾股数 fr ...