linkedlist是有序的吗 取数据有序么

package cn.itcast_04;
import java.util.LinkedHashS
* LinkedHashSet:底层数据结构由哈希表和链表组成。
* 哈希表保证元素的唯一性。
* 链表保证元素有素。(存储和取出是一致)
public class LinkedHashSetDemo {
public static void main(String[] args) {
// 创建集合对象
LinkedHashSet&String& hs = new LinkedHashSet&String&();
// 创建并添加元素
hs.add(&hello&);
hs.add(&world&);
hs.add(&java&);
hs.add(&world&);
hs.add(&java&);
for (String s : hs) {
System.out.println(s);
阅读(...) 评论()java linked list里的元素顺序反过来_百度知道
java linked list里的元素顺序反过来
用一个void的函数把一个linkedlist里的元素顺序颠倒过来,自己一个一个的追踪没问题,但是一运行就有nullpointerexception...
用一个void的函数把一个linked list里的元素顺序颠倒过来,自己一个一个的追踪没问题,但是一运行就有nullpointerexception
答题抽奖
首次认真答题后
即可获得3次抽奖机会,100%中奖。
sunnyliqian1知道合伙人
来自电脑网络类芝麻团
sunnyliqian1
采纳数:27
获赞数:75
擅长:暂未定制
参与团队:
&&&&&&&&定义一个LinkedList&Integer& templist = new LinkedList&&();来存储list里面的值,通过迭代list,将值插入在templist的头上,那么templist就是list的反转了,最后将templist赋值给list就行了!如下代码:public&void&reverse()&{LinkedList&Integer&&list&=&new&LinkedList&&();LinkedList&Integer&&templist&=&new&LinkedList&&();int&i&=&0;while&(i&&&6)&{list.add(i);i++;}Iterator&Integer&&it&=&list.iterator();int&m;while&(it.hasNext()&&&&i&&=&0)&{m&=&it.next();templist.addFirst(m);i--;}list&=&System.out.println(list);}运行结果为:5 4 3 2 1 0&&&&从API中可以看到List等Collection的实现并没有同步化,如果在多线程应用程序中出现同时访问,而且出现修改操作的时候都要求外部操作同步化;调用Iterator操作获得的Iterator对象在多线程修改Set的时候也自动失效,并抛出java.util.ConcurrentModificationException。这种实现机制是fail-fast,对外部的修改并不能提供任何保证。&&&&Iterator是工作在一个独立的线程中,并且拥有一个 mutex锁,就是说Iterator在工作的时候,是不允许被迭代的对象被改变的。Iterator被创建的时候,建立了一个内存索引表(单链表),这个索引表指向原来的对象,当原来的对象数量改变的时候,这个索引表的内容没有同步改变,所以当索引指针往下移动的时候,便找不到要迭代的对象,于是产生错误。&&&&List、Set等是动态的,可变对象数量的数据结构,但是Iterator则是单向不可变,只能顺序读取,不能逆序操作的数据结构,当 Iterator指向的原始数据发生变化时,Iterator自己就迷失了方向。&&&&所以如果像下面这么写就会抛出异常java.util.ConcurrentModificationException:public&void&reverse()&{LinkedList&Integer&&list&=&new&LinkedList&&();int&i&=&0;while&(i&&&6)&{list.add(i);i++;}Iterator&Integer&&it&=&list.iterator();int&m;while&(it.hasNext()&&&&i&&=&0)&{m&=&it.next();list.add(m);list.remove(0);i--;}System.out.println(list);}
t_1985知道合伙人
采纳数:36
获赞数:135
我没有运行,但是我觉得你第5行已经把temp(此时等于head)的next置为null了,第9行还要进行curr.getNext()(注意curr的些前一直没变,也就是还等于head, 而head的next是null, ), 于是curr的值变成了null,而你在第10行还写 n = curr.getNext(), curr本身是null, 再通过curr引用来进行函数调用就会出现空指针异常;另外,你的程序逻辑上有问题,while的判断应用是while(n != null)
这个我一直没有弄懂,比如我一开始写ListNode &E& temp=head,我是创造了一个新的链表元素指向head的下一个元素呢,还是temp就是head,之后的运算相当于全部在head及原链表上进行?如果是在原链表上运行的话那我这整个逻辑应该是错的
是在原链表上进行的操作。ListNode&E& temp=head,这里temp只是在原链表上增加的新的引用,该引用指向表头的地址。后面的curr什么的也是一样。如果要创建新的链表,要用new关键词对每个节点开辟新的内存空间,然后把原来链表里的内容复制过去。
本回答被提问者采纳
venia01知道合伙人
来自电脑网络类芝麻团
采纳数:62
获赞数:294
参与团队:
你这段代码逻辑完全看不懂。你再改改吧,提下问题:ListNode&E& n=curr.getNext(); 这里你赋了值,但是没有检查,如果链表为空或者只有1个元素,在后面n.getNext()会直接抛异常。第一次循环,temp=curr=head, temp.setNext(null), n.setNext(temp), temp=n, 这里的逻辑你再好好看看,理一理。你的null应该是n=curr.getNext()抛的,原因是因为temp.setNext(null)这一句
天方之夜谭知道合伙人
来自电脑网络类芝麻团
天方之夜谭
采纳数:117
获赞数:509
参与团队:
while的条件应该是n!=null,而不是n.getNext()!=null,空指针错误就是因为你的n==null造成的
之前就试过了,n!=null也是nullpointer的错误
其他1条回答
为你推荐:
其他类似问题
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。为什么多线程向LinkedList加数据和取数据会有差别
[问题点数:40分,结帖人Fly_fans]
本版专家分:274
结帖率 100%
CSDN今日推荐
本版专家分:274
本版专家分:274
匿名用户不能发表回复!|
其他相关推荐List&(链表|线性表)
& & &特点: 接口,可存放重复元素,元素存取是有序的,允许在指定位置插入元素,并通过索引来访问元素
1、创建一个用指定可视行数初始化的新滚动列表。默认情况下,不允许进行多项选择。
& & &注意,这是 List(rows, false) 的一种便捷方法。还要注意,列表中的可视行数一旦创建就不能更改。
public List(int&rows)------------------row-----要显示的项数
&2、创建一个初始化为显示指定行数的新滚动列表。
& & & 注意,如果指定了零行,则会按默认的四行创建列表。还要注意,列表中的可视行数一旦创建就不能更改。
& & & 如果 multipleMode 的值为 true,则用户可从列表中选择多项。如果为 false,则一次只能选择一项。
public List(int&rows,boolean&multipleMode)-------rows - 要显示的项数。
-------multipleMode - 如果为 true,则允许进行多项选择;否则,一次只能选择一项。
&综上所述:如果抛出为throws-----GraphicsEnvironment.isHeadless(),则返回 true。
& & & &特点: 不可以存放重复元素,元素存取是无序的
Collection(接口)
& & & 1、特点:描述的是集合共有的功能(描述所有接口的共性)
& & & 2、Collection接口有两个子接口:
& & & & & & &List(链表|线性表)
& & & & & & &Set(集)
Collection
我们需要保存若干个对象的时候使用集合。
如果我们需要保留存储顺序, 并且保留重复元素, 使用List.
如果查询较多, 那么使用ArrayList
如果存取较多, 那么使用LinkedList
如果需要线程安全, 那么使用Vector
如果我们不需要保留存储顺序, 并且需要去掉重复元素, 使用Set.
如果我们需要将元素排序, 那么使用TreeSet
如果我们不需要排序, 使用HashSet, HashSet比
TreeSet效率高.
如果我们需要保留存储顺序, 又要过滤重复元素, 那么使用LinkedHashSet
Collection接口的共性方法
&&&&&& 1:add()&& 将指定对象存储到容器中
&&&&&&&&&&&&&&&&&&&&& add 方法的参数类型是Object 便于接收任意对象
&&&&&& 2:addAll() 将指定集合中的元素添加到调用该方法和集合中
&&&&&& 3:remove() 将指定的对象从集合中删除
&&&&&& 4:removeAll() 将指定集合中的元素删除
&&&&&& 5:clear() 清空集合中的所有元素
&&&&&& 6:isEmpty() 判断集合是否为空
&&&&&& 7:contains() 判断集合何中是否包含指定对象
&&&&&&&&&&&
&&&&&& 8:containsAll() 判断集合中是否包含指定集合
&&&&&&&&&&&&&&&&&&&&&&&&&&& 使用equals()判断两个对象是否相等&
获取: &&9:int size()&&& 返回集合容器的大小
转成数组10: toArray()&& 集合转换数组
实例练习:
1 //创建Person类
2 class Person {
private int
public Person() {
public Person(String name, int age) {
this.name =
this.age =
public int hashCode() {
return this.name.hashCode() +
public boolean equals(Object obj) {
if (!(obj instanceof Person)) {
return false;
Person p = (Person)
return this.name.equals(p.name) && this.age == p.
public String toString() {
return "Person :name=" + name + ", age=" +
37 public static void main(String[] args) {
Person p1 = new Person("李华", 19);
Person p2 = new Person("尹乐乐", 20);
Person p3 = new Person("洛洛", 18);
Collection list = new ArrayList();
list.add(p1);
list.add(p2);
list.add(p3);
// isEmpty() 判断集合是否为空
boolean empty = list.isEmpty();
System.out.println(empty);
// 返回集合容器的大小
int size = list.size();
System.out.println(size);
// contains()判断集合何中是否包含指定对象
boolean contains = list.contains(p1);
System.out.println(contains);
// remove(); 将指定的对象从集合中删除
list.remove(p1);
// clear() 清空集合中的所有元素
list.clear();
System.out.println(list);
1:Person类
&&& 1:姓名和年龄
&&& 2:重写hashCode和equals方法
& & & & & & 1、如果不重写,调用Object类的equals方法,判断内存地址,为false
& & & & & & 2、如果是Person类对象,并且姓名和年龄相同就返回true
& & & & & & 3、如果不重写,调用父类hashCode方法,如果equals方法相同,那么hashCode也要相同,需要重写hashCode方法
& & &3:重写toString方法
& & & & & &注:不重写,直接调用Object类的toString方法,打印该对象的内存地址
注:list、ArrayList、LinkedList、Vector的区别
&1、List:元素是有序的(怎么存的就怎么取出来,顺序不会乱),元素可以重复(角标1上有个3,角标2上也可以有个3)因为该集合体系有索引。
& & & & & &List:特有的方法,凡是可以操作角标的方法都是该体系特有的方法。
&2、ArrayList:1、底层的数据结构使用的是数组结构(数组长度是可变的百分之五十延长)。
& & & & & & & & & & 2、特点:是查询很快,但增删较慢,线程不同步(数组查询快的原因是:数组的内存空间地址是连续的)。
& & & & & & & & & & 3、ArrayList底层维护了一个Object[] 用于存储对象,默认数组的长度是10,当默认的或者指定的容量不够存储对象的时候,容量自动增长为原来的容量的1.5倍。
& & & & & & & & & & 4、单线程效率高。
实例: &去除ArrayList集合中重复元素
& & & & & & &1:存入字符串元素
& & & & & & &2:存入自定义对象元素(如Perosn对象)
&&&& 循环遍历该集合,每取出一个放置在新的集合中,放置之前先判断新的集合是否以包含了新的元素。
1 public class Demo2 {
public static void main(String[] args) {
ArrayList arr = new ArrayList();
Person p1 = new Person("jack", 20);
Person p2 = new Person("rose", 18);
Person p3 = new Person("rose", 18);
arr.add(p1);
arr.add(p2);
arr.add(p3);
<span style="color: #
System.out.println(arr);
<span style="color: #
ArrayList arr2 = new ArrayList();
<span style="color: #
for (int i = 0; i & arr.size(); i++) {
<span style="color: #
Object obj = arr.get(i);
<span style="color: #
Person p = (Person)
<span style="color: #
if (!(arr2.contains(p))) {
<span style="color: #
arr2.add(p);
<span style="color: #
<span style="color: #
<span style="color: #
System.out.println(arr2);
<span style="color: #
<span style="color: # }
<span style="color: #
<span style="color: # class Person {
<span style="color: #
<span style="color: #
private int
<span style="color: #
<span style="color: #
public Person() {
<span style="color: #
<span style="color: #
<span style="color: #
<span style="color: #
public Person(String name, int age) {
<span style="color: #
<span style="color: #
this.name =
<span style="color: #
this.age =
<span style="color: #
<span style="color: #
<span style="color: #
public String getName() {
<span style="color: #
<span style="color: #
<span style="color: #
<span style="color: #
public void setName(String name) {
<span style="color: #
this.name =
<span style="color: #
<span style="color: #
<span style="color: #
public int getAge() {
<span style="color: #
<span style="color: #
<span style="color: #
<span style="color: #
public void setAge(int age) {
<span style="color: #
this.age =
<span style="color: #
<span style="color: #
<span style="color: #
<span style="color: #
public int hashCode() {
<span style="color: #
return this.name.hashCode() + age * 37;
<span style="color: #
<span style="color: #
<span style="color: #
<span style="color: #
public boolean equals(Object obj) {
<span style="color: #
if (!(obj instanceof Person)) {
<span style="color: #
return false;
<span style="color: #
<span style="color: #
Person p = (Person)
<span style="color: #
<span style="color: #
return this.name.equals(p.name) && this.age == p.
<span style="color: #
<span style="color: #
<span style="color: #
<span style="color: #
public String toString() {
<span style="color: #
return "Person@name:" + this.name + " age:" + this.
<span style="color: #
<span style="color: #
<span style="color: # }
& 3、LinkedList:底层的数据结构是链表结构(特点是查询较慢,增删较快)。& & & & &
& & & & & 特有方法:addFirst(E e)、getFirst(E e) 、removeFirst(E e)&
& & & & &如果集合中没有元素,获取或者删除元素抛:NoSuchElementException
& 4、Vector:1、底层是数组数据结构 线程同步(数组长度是可变的百分之百延长),(无论查询还是增删都很慢,被ArrayList替代了)
& & & & & & & & & &2、多线程安全的,所以效率低。
& & & & & & & & & &3、特有的方法:
& & & & & & & & & & & void addElement(E obj)& 在集合末尾添加元素
& & & & & & & & & & & E elementAt( int index) 返回指定角标的元素
& & & & & & & & & & & Enumeration elements()& 返回集合中的所有元素,封装到Enumeration对象中测试此枚举是否包含更多的元素。
& & & & & & & & & & & E nextElement() & &如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素。
& & & & Enumeration 接口:&boolean hasMoreElements()&
注:1、可以使用该集合去模拟出队列(先进先出) 或者堆栈(后进先出) 数据结构,堆栈(后进先出)。
& & &2、有一批数据要存储,要求存储这批数据不能出现重复数据,ArrayList、LinkedList都没法满足需求。解决办法:使用 set集合。
& & & &1:栈 (1.6)
& & & & & & 先进后出
& & & & & & & push()
& & & & & & & &pop()
& & & &2:队列(双端队列1.5)
&&&&&&&&&&&&&&&&& 先进先出
&&&&&&&&&&&&&&&&& offer()
&&&&&&&&&&&&&&&&& poll()
& & & &3:返回逆序的迭代器对象 & descendingIterator()&& 返回逆序的迭代器对象
import java.util.I
import java.util.LinkedL
public class Demo3 {
public static void main(String[] args) {
LinkedList list = new LinkedList();
list.add("微信");
list.add("微博");
<span style="color: #
list.add("淘宝");
<span style="color: #
list.add("京东");
<span style="color: #
list.add("阿里巴巴");
<span style="color: #
Iterator it = list.iterator();
<span style="color: #
// Iterator it = list.descendingIterator();//逆序迭代
<span style="color: #
while (it.hasNext()) {
<span style="color: #
String next = (String) it.next();
<span style="color: #
System.out.println(next);
<span style="color: #
<span style="color: #
<span style="color: #
Views(...) Comments()弱小和无知不是生存的障碍,傲慢才是。
ArrayList和LinkedList剖析
java集合中最顶层的接口为Connection接口,其中有两个接口实现了Connection接口,分别为Set接口和List接口。Set接口表现为无序,不能重复;List接口表现为有序,可重复。其中ArrayList和LinkedList是List接口的实现类中最常用的两个。下面针对ArrayList和LinkedList这两个实现类做一些说明:
(1)ArrayList:ArrayList是一个泛型类,底层采用数组结构保存对象。数组结构的优点是便于对集合进行快速的随机访问,即如果需要经常根据索引位置访问集合中的对象,使用由ArrayList类实现的List集合的效率较好。数组结构的缺点是向指定索引位置插入对象和删除指定索引位置对象的速度较慢,并且插入或删除对象的索引位置越小效率越低,原因是当向指定的索引位置插入对象时,会同时将指定索引位置及之后的所有对象相应的向后移动一位。
(2)LinkedList:LinkedList是一个泛型类,底层是一个双向链表,所以它在执行插入和删除操作时比ArrayList更加的高效,但也因为链表的数据结构,所以在随机访问方面要比ArrayList差。另外,LinkedList还提供了一些可以使其作为栈、队列、双端队列的方法。
1.ArrayList内部实现:
将从两方面来剖析ArrayList,即存储结构-字段和功能实现-方法。
存储结构-字段
从结构实现来讲,ArrayList是数组实现的。首先从源码中看出
* Default initial capacity.
private static final int DEFAULT_CAPACITY = 10;
* Shared empty array instance used for empty instances.
private static final Object[] EMPTY_ELEMENTDATA = {};
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
transient Object[] elementD
* The size of the ArrayList (the number of elements it contains).
private int
ArrayList中维护了一个Object类型的数组elementData,即用来存放数据。注意的是这个数组是transient类型,这个留到下面再说。其次ArrayList默认大小为10。其中size为当前数组中元素的个数。其余的两个参数都用做初始化elementData数组。
功能实现-方法
ArrayList中功能主要包括增删改查,还有扩容操作。这里主要对扩容操作,add方法进行展开分析。
1.扩容操作
扩容就是重新计算容量,想ArrayList对象不停的添加元素,而ArrayList对象内部的数组无法装载更多的元素时,对象就需要扩大数组的长度,以便能装入更多的元素。java中的数组无法自动扩容,方法便是用一个新的数组代替已有的容量小的数组。ArrayList的扩容入口函数为ensureCapacity和ensureCapacityInternal,java8源码如下:
* 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.
minCapacity
the desired minimum capacity
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
: DEFAULT_CAPACITY;
if (minCapacity & minExpand) {
ensureExplicitCapacity(minCapacity);
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
ensureExplicitCapacity(minCapacity);
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length & 0)
grow(minCapacity);
java8中的代码相比java7有小幅度改变,首先入口函数都会调用到ensureExplicitCapacity()这个方法,modCount一般是在迭代器中出现,这里是记录list结构被改变的次数。可以看出只有当minCapacity大于当前数组长度的时候才会调用grow方法,接下来看grow方法:
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
* minCapacity the desired minimum capacity
private void grow(int minCapacity) {
int oldCapacity = elementData.
int newCapacity = oldCapacity + (oldCapacity && 1);
if (newCapacity - minCapacity & 0)
newCapacity = minC
if (newCapacity - MAX_ARRAY_SIZE & 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
这里便于java7有点区别了,java7中的capacity计算如下:
java7中使用的数学公式,而java8中则使用位移操作,向又移一位后与java7中数组大小一致,位移操作要比数学公式快不少。然后确定newCapacity后,调用Arrays.copy方法生成了新的Array数组。
add操作就是向ArrayList对象中插入一条新的数据,方法如下:
* Appends the specified element to the end of this list.
* e element to be appended to this list
* &tt&true&/tt& (as specified by {@link Collection#add})
public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] =
return true;
* Inserts the specified element at the specified position in this
* list. Shifts the element currently at that position (if any) and
* any subsequent elements to the right (adds one to their indices).
* index index at which the specified element is to be inserted
* element element to be inserted
* IndexOutOfBoundsException {@inheritDoc}
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1);
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] =
add有两个重载方法,第一个是默认向尾端插入数据,第二个可指定插入位置。两个方法都会首先调用ensureCapacityInternal,并且传入参数为size+1,这样就保证了ArrayList的数组永远不会超过界限。对于第二个方法,主要是调用了arrayCopy,其源码如下:
可看出这个是一个本地方法,看解释就很清楚了,arrayList就是把index上的数据往后移了一位。
linkedList内部实现:
将从两方面来剖析linkedList,即存储结构-字段和功能实现-方法。
存储结构-字段
从结构实现来讲,linkedList是双向链表实现的。首先从源码中看出
transient int size = 0;
* Pointer to first node.
* Invariant: (first == null && last == null) ||
(first.prev == null && first.item != null)
transient Node&E&
* Pointer to last node.
* Invariant: (first == null && last == null) ||
(last.next == null && last.item != null)
transient Node&E&
相比java7来说还是有一点变化的,其中size用来表示当前对象中的个数,first,last分别指向头结点和尾结点。注意,都被申明为了transient类型,这个在下文中讲解。Node内部类源码如下:
private static class Node&E& {
Node(Node&E& prev, E element, Node&E& next) {
this.item =
this.next =
this.prev =
一看就清楚了,item是当前值,next和prev分别指向下一个节点和前一个节点。
功能实现-方法
linkedList中功能主要包括增删改查。由于是链表的数据结构,所以没有扩容方法,这里主要对add方法remove方法进讲解。
1.add操作:
* Inserts the specified element at the specified position in this list.
* Shifts the element currently at that position (if any) and any
* subsequent elements to the right (adds one to their indices).
* index index at which the specified element is to be inserted
* element element to be inserted
* IndexOutOfBoundsException {@inheritDoc}
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
linkBefore(element, node(index));
可以看出其中有checkPositionIndex、linkLast、linkBefore这三个方法,其中checkPositionIndex方法只有在指定插入index位时才有,用于校验index是否合法(合法的判断标准为必须大于0小于或等于size)。如果index==size那么直接根据last指针插入到最后即可,下面具体看一下node方法和linkBefore方法:
* Returns the (non-null) Node at the specified element index.
Node&E& node(int index) {
if (index & (size && 1)) {
Node&E& x =
for (int i = 0; i & i++)
Node&E& x =
for (int i = size - 1; i & i--)
node方法就是根据index位置返回index位置上的结点。这里为了降低便利次数,如果index大于size的一般的话,那么就倒序开始遍历。
* Inserts element e before non-null Node succ.
void linkBefore(E e, Node&E& succ) {
final Node&E& pred = succ.
final Node&E& newNode = new Node&&(pred, e, succ);
succ.prev = newN
if (pred == null)
first = newN
pred.next = newN
modCount++;
确定index位的node结点指针后,仅仅需要改变一下链表指向即可。注意这里的modCount也是用于迭代器中的。
2.remove操作:
public boolean remove(Object o) {
if (o == null) {
for (Node&E& x = x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
for (Node&E& x = x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
return false;
可以看出remove其实只做了两件事,1、检查index是否合法;2、调用unlink方法,传入index位置的node节点;下面我们看看unlink方法:
* Unlinks non-null node x.
E unlink(Node&E& x) {
final E element = x.
final Node&E& next = x.
final Node&E& prev = x.
if (prev == null) {
prev.next =
x.prev = null;
if (next == null) {
next.prev =
x.next = null;
x.item = null;
modCount++;
其实就是标准的聊表节点删除操作,要注意的是需要判断边界情况,即头结点和尾结点的情况。
transient分析
注意到的是ArrayList和LinkedList中的一些变量被transient关键字修饰。如ArrayList中的elementData数组,LinkedList中指向头结点和尾结点的指针等。下面解释一下transient关键字的作用:
java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我不想用serialization机制来保存它。为了在一个特定对象的域上关闭serialization,可以在这个域前加上关键字transient。transient是一个关键字,用来表示一个于不是该对象串行化的一部分。当一个对象被串行化的时候,被transient关键字修饰的变量的值不包括在串行化的表示中,非transient型的变量是被包括进去的。
那么既然用于保存数据的变量都被transient修饰,ArrayList和LinkedList还能不能被序列化呢?
答案是可以的。对于ArrayList来说,如果不把elementData申明为transient类型,那么序列化的时候里面的数据都会被序列化,但是elementData这个数组很大程序是存在空值的情况(即size《length),这时如果序列化就会导致磁盘空间被浪费。为了解决这个问题,ArrayList将elementData申明为transient,自己重写了writeObject方法,保证只序列化elementData中有数据的那部分,ArrayList中的writeObject方法如下:
注意的是这里面也用到了modCount,如果不一致说明这段时间集合发生了改变,抛出异常。
LinkedList中的序列化和ArrayList中的序列化还不一样,LinkedList中不会存在说有多余的元素这种说法,但是由于LinkedList中申明为transient类型的数据都可以不用进行序列化,所以进行申明,比如分别指向头结点和尾结点的first和last指针。
迭代器分析:
ArrayList和LikedList都提供了iterator()方法来获得当前对象的迭代器。实现迭代器必须要定义一个内部类来实现Iterator接口,下面便看看ArrayList的迭代器实现。
首先该类中有三个变量,分别为cursor,lastRet,expectedModCount。cursor为下个返回元素的索引,lastRet为最后一个返回元素的索引,初始值为-1,代表没有作用。expectedModCount为期望的改变次数,初始值为modCount,一旦两个值不一至,就会抛出ConcurrentModificationException异常;
首先所有的方法都先会调用checkForComodification这个方法,这个方法主要是判断modCount和expectedModCount是否一致,不一致就抛出异常。这就使得在迭代过程中modCount的值不能更改,即不能在迭代期间往list中新增数据和删除(调用迭代器的删除是可以的,下文分析)。
迭代器中方法分析:
这两个方法非常清楚,首先hasNext方法一看就清楚,即判断元素是否迭代完。next返回的是cursor所指向的元素。接下来看看remove方法:
其实可以看出迭代器中的remove也是调用的是ArrayList中的remove方法,remove的索引就是lastRet,这里调用后lastRet被置为-1,所以remove方法在迭代器中只能调用一次。现在分析一下,为什么调用迭代器的remove方法就不会抛出ConcurrentModificationException异常。首先上文分析了为什么会抛出ConcurrentModificationException异常的原因,其实就是modCount和expectedModCount不相等,还有就是在方法调用过程中,发现cursor大于element.length时抛出。在remove方法中又重新将modCount的值赋给了expectedModCount,使得值再次相等,所以不会抛出异常。
优缺点对比:
1、ArrayList是基于数组实现的,所以搜索和读取数据很快;获取数据的时间复杂度是O(1),但是要删除和增加节点开销就很大,时间复杂度为O(N)。LinkedList是基于双链表实现的,所以插入和删除很快,时间复杂度为O(1);但是随机索引速度就比较慢,时间复杂度为O(N)。
2、LinkedList对内存需求更高,因为需要存储的是实际的数据和前后节点的位置;ArrayList的每个索引的位置是实际的数据,所以对内存需求相比要小。
参考文献:
java源码分析之LinkedList
java笔记三:List接口
LinkedList和ArrayList的区别
java源码分析之ArrayList
Java 中Vector、ArrayList和LinkedList 的区别
常见Map 及 ArrayList 是否有序总结
List接口的实现类---ArrayList、LinkedList、Vector之间的区别--------(java复习)
没有更多推荐了,

我要回帖

更多关于 linkedlist是不是有序的 的文章

 

随机推荐