List在数据结构中表现为是线性表的方式,其元素以线性方式存储,集合中允许存放重复的对象,List接口主要的实现类有
ArrayList
ArrayList其实就是一组长度可变的数组,当实例化了一个ArrayList,该数据也被实例化了,当向集合中添加对象时,数组的大小也随着改变,
这样它所带来的有优点是快速的随机访问,即使访问每个元素所带来的性能问题也是很小的,但缺点就是想其中添加或删除对象速度慢,当你创建的数组是不确定其
容量,所以当我们改变这个数组时就必须在内存中做很多的处理,如你想要数组中任意两个元素中间添加对象,那么在内存中数组要移动所有后面的对象。
LinkedList
LinkedList是通过节点的连接实现链表的数据结构,向linkedList中插入或删除元素的速度是特别快,而随机访问的速度相对较慢,这个是由
于链表本身的性质造成的,在链表中,每个节点都包含了前一个节点的引用,后一个节点的引用和节点存储值,当一个新节点插入式,只需要修改其中相关的前后关
系节点引用即可,删除节点也是一样。操作对象只需要改变节点的链接,新节点可以存放在内存的任何位置,但也就是因为如此LinkedList虽然存在
get()方法,但是这个方法通过遍历节点来定位所以速度很慢。LinkedList还单独具
addFrist(),addLast(),getFrist(),getLast(),removeFirst(),removeLast()方法,这
些方法使得LinkedList可以作为堆栈,队列,和双队列来使用。
说白了,ArrayList和LinkedList就是数据结构中的顺序存储表和链式存储表。
ArrayList构造原理
上面已经清楚ArrayList和LinkedList就是数据结构的顺序表和链表(不清楚的翻翻数据结构的书),下面简单分析一下它们的实现方式。
下表是摘自sum提供的ArrayList的api文档
ArrayList()
构造一个初始容量为 10 的空列表。
ArrayList(Collection<? extends E> c)
构造一个包含指定 collection 的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的。
ArrayList(int initialCapacity)
构造一个具有指定初始容量的空列表。
第一个构造函数是没有默认构建了一个初始容量10的空列表,第二个构造函数是制定collection元素的列表,第三个构造函数是由用户指定构造的列表
初始化容量多少,如果使用第一个构造函数则表示默认调用该参数为initialCapacity=10来构造一个列表对象。
ArrayList源码稍微进行分析
public
class
ArrayList
<
E
>
extends
AbstractList
<
E
>
implements
List
<
E
>
, RandomAccess, Cloneable, java.io.Serializable
{
private
static
final
long
serialVersionUID
=
8683452581122892189L
;
private
transient
Object[] elementData;
private
int
size;
public
ArrayList(
int
initialCapacity) {
super
();
if
(initialCapacity
<
0
)
throw
new
IllegalArgumentException(
"
Illegal Capacity:
"
+
initialCapacity);
this
.elementData
=
new
Object[initialCapacity];
}
public
ArrayList() {
this
(
10
);
}
public
ArrayList(Collection
<?
extends
E
>
c) {
elementData
=
c.toArray();
size
=
elementData.length;
//
c.toArray might (incorrectly) not return Object[] (see 6260652)
if
(elementData.getClass()
!=
Object[].
class
)
elementData
=
Arrays.copyOf(elementData, size, Object[].
class
);
}
public
int
size() {
return
size;
}
}
可以发现ArrayList中包含两个主要的属性
private transient Object[] elementData;
private int size;
其中elementData[]是列表的实现的核心数组,我们使用该数组来存放集合中的数据,而我们的构造函数所传递的initialCapacity参数是构建该数组的长度。
在看看size的实现形式,它的作用是返回size的属性值的大小,我们再看看另外一个构造函数public
ArrayList(Collection<? extends E>
c),该构造函数的作用是把另外一个容器对象中的元素放入当点的List对象中。首先是通过调用另外一个容器对象c的size()来设置当前List对象
的size属性的长度大小。接下来就似乎对elementData[]数组进行初始化,最后通过Arrays.copyOf(U[] original,
int newLength, Class<? extends T[]>
newType)方法把当前容器中的对象都存放进新的数组elementData,主要就完成了一个列表的创建。
ArrayList容量扩充
还有一个问题就是我们所建立的ArrayList是使用数组来实现的,但数组的长度一旦被初始化就不能改变,而我们在给此列表对象添加元素时却没有受到长
度的限制,所以,ArrayList的elementData属性一定是存在一个动态扩充容量的机制,下面把相关的部分源码贴出来再做研究
public
boolean
add(E e) {
ensureCapacity(size
+
1
);
//
Increments modCount!!
elementData[size
++
]
=
e;
return
true
;
}
protected
transient
int
modCount
=
0
;
/**
* Increases the capacity of this <tt>ArrayList</tt> instance, if
* necessary, to ensure that it can hold at least the number of elements
* specified by the minimum capacity argument.
*
*
@param
minCapacity the desired minimum capacity
*/
public
void
ensureCapacity(
int
minCapacity) {
modCount
++
;
int
oldCapacity
=
elementData.length;
if
(minCapacity
>
oldCapacity) {
Object oldData[]
=
elementData;
int
newCapacity
=
(oldCapacity
*
3
)
/
2
+
1
;
if
(newCapacity
<
minCapacity)
newCapacity
=
minCapacity;
//
minCapacity is usually close to size, so this is a win:
elementData
=
Arrays.copyOf(elementData, newCapacity);
}
}
看看public boolean add(E e)方法,可以发现在添加一个元素到容器中的时候,我们会先通过ensureCapacity(size + 1)判断该数组是否需要扩充。
public void ensureCapacity(int
minCapacity)这个方法是用来判断当前的数组是否需要扩充,并且该扩充多少。modCount++;
表示当前的对象对elementData数组进行了多少次扩充,清空和移除等操作,相当于是一个对当前List对象的一个操作记录数。
int oldCapacity = elementData.length; 初始化oldCapacity,表示为当前elementData数组的长度。
if (minCapacity > oldCapacity) 判断minCapacity和oldCapacity谁大,来决定是否需要扩充。
int newCapacity = (oldCapacity * 3)/2 + 1; 扩充的策列是判断(oldCapacity * 3)/2 +
1和minCapacity两者之间谁更大,取更大的数作为扩充后数组的initialCapacity值,然后使用数组拷贝的方式,把以前的数据转移到
新的数组对象中
如果minCapacity 小于 oldCapacity 就不需要再扩充。
ArrayList删除元素
public
E remove(
int
index) {
RangeCheck(index);
modCount
++
;
E oldValue
=
(E) elementData[index];
int
numMoved
=
size
-
index
-
1
;
if
(numMoved
>
0
)
System.arraycopy(elementData, index
+
1
, elementData, index,
numMoved);
elementData[
--
size]
=
null
;
//
Let gc do its work
return
oldValue;
}
private
void
RangeCheck(
int
index) {
if
(index
>=
size)
throw
new
IndexOutOfBoundsException(
"
Index:
"
+
index
+
"
, Size:
"
+
size);
}
在看看ArrayList移除元素是怎么实现的,首先判断需要删除的index是否在elementData数组的下标内,如不存在则抛出IndexOutOfBoundsException。
modCount++; 与扩充元素一个,删除元素也记下来操作数。
E oldValue = (E) elementData[index]; 获取需要删除元素的对象。
int numMoved = size - index - 1; 获取需要被删除元素的下标,删除该元素后,数组需要在此元素下标后的所有对象进行内存的移动。
System.arraycopy(elementData, index+1, elementData, index,numMoved);对numMoved后面的所有对象通过copy的方式进行内存的移动重新构建数组。
说完ArrayList的实现,再说说linkedList
构建双链表(LinkedList)
LinkedList是类似于C语言的双链表,双链表比单链表多了一个域,这个双链表就有了三个域,一个域来用存储数据元素,一个用来指向后续节点,另一个是指向结点的直接前驱节点。
public
class
LinkedList
<
E
>
extends
AbstractSequentialList
<
E
>
implements
List
<
E
>
, Deque
<
E
>
, Cloneable, java.io.Serializable
{
private
transient
Entry
<
E
>
header
=
new
Entry
<
E
>
(
null
,
null
,
null
);
private
transient
int
size
=
0
;
public
LinkedList() {
header.next
=
header.previous
=
header;
}
private
static
class
Entry
<
E
>
{
E element;
Entry
<
E
>
next;
Entry
<
E
>
previous;
Entry(E element, Entry
<
E
>
next, Entry
<
E
>
previous) {
this
.element
=
element;
this
.next
=
next;
this
.previous
=
previous;
}
}
}
在Entry类中,定义了三个属性,分别为E element 表示数据与,Entry<E> next为后续指针域,Entry<E> previous为前驱指针域。
在LinkedList中定义了一个重要的属性header,头结点,不会纳入链表的总元素,该节点的previous是指向最后节点,next是指向第一节点。
构造函数LinkedList()
构造一个空列表。将header的前驱指针域和后续指针域都指向了自己,看到这里可以发现,next和previous就是一个引用,其实也相等于C里面
的指针,不过C不会处理空指针,直接放操作系统处理了,java就直接抛出NullPointerException,根本不让它破坏系统的机会。
LinkedList元素变动
上面说到了LinkedList的新增和删除的效率比ArrayList的高,实际上在 链表操作这些方法时,只需要改变2个节点各自的前驱指针和后续指针域,而ArrayList是需要移动很多的元素。
public
boolean
add(E e) {
addBefore(e, header);
return
true
;
}
private
Entry
<
E
>
addBefore(E e, Entry
<
E
>
entry) {
Entry
<
E
>
newEntry
=
new
Entry
<
E
>
(e, entry, entry.previous);
newEntry.previous.next
=
newEntry;
newEntry.next.previous
=
newEntry;
size
++
;
modCount
++
;
return
newEntry;
}
private
E remove(Entry
<
E
>
e) {
if
(e
==
header)
throw
new
NoSuchElementException();
E result
=
e.element;
e.previous.next
=
e.next;
e.next.previous
=
e.previous;
e.next
=
e.previous
=
null
;
e.element
=
null
;
size
--
;
modCount
++
;
return
result;
}
上列的代码是对一个链表的遍历,其中包含了一个算法,如果给的索引号小于总节点数的一半,则在链表的前半部分第一个节点完进行遍历,如果给的索引号大于总节点数的一半,则从最后一个节点往前进行遍历直到索引号。
最后总结
一下ArrayList和LinkedList的各自特点
1.ArrayList是基于线性表的顺序存储表,LinkedList是基本线性表的链表存储表。
2.对于新增和删除元素,LinkedList比较占有优势,只需要变前后2个节点,而ArrayList要移动数据。
3.对于随机访问来说,ArrayList比较占有优势,可以根据索引号快速访问,而LinkedList则需要遍历集合的元素来定位。
4.而对于迭代操作(iterate)和查找操作(indexOf),两者是差不多。
不过上面都是基于理论,具体问题还是要根据事实进行分析,如ArrayList删除的元素刚好是队列的最后一个元素,那么是无需要移动数据的,大体我们可
以认为需要随机访问较多的那么比较适合用ArrayList,如果是插入和删除(如消息队列)较多的那么就需要考虑LinkedList。
分享到:
相关推荐
javalist数据结构_Java数据结构-------List 三种List:ArrayList,Vector,LinkedList 类继承关系图 ArrayList和Vector通过数组实现,⼏乎使⽤了相同的算法;区别是ArrayList不是线程安全的,Vector绝⼤多数⽅法做了...
此java类实现了对数据表的分类递归树的实现,为本人倾力之作,后期,会发布js版,敬请期待!
Java基础复习笔记04数据结构-线性表。
介绍数据结构列表(List)的概念、特点、优缺点、适用场景和Java示例代码
第四个模块——Create()的功能是:创建新的数据记录。 第五个模块——Add()的功能是:增加新的数据记录,并返回选单。 第六个模块——Find()的功能是:按要求查询相关的信息,如果找到了,则显示该信息...
Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲区、写入数据到文件、关闭输入流、关闭套接字关闭输出流、输出错误信息等Java编程小技巧。 Java数组倒置...
因此,允许您更改基本的数据结构而不必改变其它代码。框架接口层次结构如下图所示。 有的人可能会认为 Map 会继承 Collection。 在数学中,映射只是对(pair)的集合。但是,在“集合框架”中,接口 Map 和 ...
计算机后端-Java-Java核心基础-第25章 集合02 03. 复习:List接口.avi
计算机后端-Java-Java核心基础-第24章 集合01 18. List遍历及方法总结.avi
常见数据结构的Java实现 例子1 import java.util.*; public class Example13_1 { public static void main(String args[]) { List list=new LinkedList(); list.add("is"); list.add("a"); int number=list....
计算机后端-Java-Java核心基础-第24章 集合01 13. List接口常用实现类的对比.avi
计算机后端-Java-Java核心基础-第24章 集合01 17. List接口中的常用方法测试.avi
文档中详细介绍了JAVA数组,LIST,MAP,SET等的使用和细节。
java导出excel 集合导出Excel 导出Excel源码 模板导出Excel
CoreJava DAY02 数据类型和控制结构 6 CoreJava DAY03 数组 11 CoreJava DAY04 15 CoreJava DAY05 面向对象 17 CoreJava DAY06 类的加载过程、实例化、继承、多态 20 CoreJava DAY07修饰符 26 CoreJava DAY08 常用类...
java后台从数据库读取 数据,封装到list集合,控制层转化为XML格式数据
java代码-使用java解决list(Map)排序的问题源代码 ——学习参考资料:仅用于个人学习使用!
关于Java中List对象的分页思想-按10个或者n个数对list进行分组
数据结构与算法 设计模式 软件设计 等文档 ,可以访问我们的官网查看更多内容 [人在地上跑 牛在天上飞](#人在地上跑 牛在天上飞) Java Java-Coding Java-IO Java-IO-练习 [Read or List All Files in a Folder](Java...
java 数据结构 数组 向量 字符串