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 ...
随机推荐
- JavaScript 基础(六) 数组方法 闭包
在一个对象中绑定函数,称为这个对象的方法.在JavaScript 中,对象的定义是这样的: var guagua = { name:'瓜瓜', birth:1990 }; 但是,如果我们给瓜瓜绑定一个 ...
- 全局变量重复定义,fatal error LNK1169: 找到一个或多个多重定义的符号
1.在GlobeValue.h中定义了一个变量: char gl_UID[256]; 2.在b.cpp和e.cpp中分别引用GlobeValue.h,并且使用gl_UID的全局变量, 结果出现:fat ...
- ABAP术语-Interface Parameter
Interface Parameter 原文:http://www.cnblogs.com/qiangsheng/archive/2008/02/26/1081800.html Parameter t ...
- 关于 laravel 集合的使用
常用的有 count() count方法返回集合中所有项的数目: $collection = collect([1, 2, 3, 4]); $collection->count(); forPa ...
- JDK1.8的安装
[环境准备] OS版本:Windows10企业版.64位操作系统: JDK版本:jdk-8u131-windows-x64.exe [彻底卸载已安装的JDK] 01:卸载或删除JDK服务.有三种方式: ...
- frame3.5安装出错
一般是因为禁用了microsoft update,可以在服务里禁用改为手动,之后启动,然后就可以安装
- zookeeper相关知识与集群搭建
Zookeeper Zookeeper相关概念 Zookeeper概述 Zookeeper是一个分布式协调服务的开源框架,主要用来解决分布式集群中应用系统的一致性问题. Zookeeper本质上是一个 ...
- PHP一些常用魔术方法
魔术方法 调用方法 作用__set 有两个 ...
- 嵌入式框架Zorb Framework搭建五:事件的实现
我是卓波,我是一名嵌入式工程师,我万万没想到我会在这里跟大家吹牛皮. 嵌入式框架Zorb Framework搭建过程 嵌入式框架Zorb Framework搭建一:嵌入式环境搭建.调试输出和建立时间系 ...
- python2.7练习小例子(五)
5):题目:输入三个整数x,y,z,请把这三个数由小到大输出. 程序分析:我们想办法把最小的数放到x上,先将x与y进行比较,如果x>y则将x与y的值进行交换,然后再用x与z进行比 ...