Java集合类--LinkedList

转自:http://www.cnblogs.com/huangfox/archive/2010/10/11/1847863.html

一、 LinkedList
 

3.1      创建:LinkedList()

LinkedList底层的数据结构是一个双向链表。既然是双向链表,那么必定存在一种数据结构——我们可以称之为节点,节点实例保存业务数据,前一个节点的位置信息和后一个节点位置信息,如下图所示:

图——双线链表及节点示意图

 

首先来了解节点类:

private static class Entry{

    Eelement;

    Entry next;

    Entry previous;

 

    Entry(Eelement, Entrynext, Entryprevious) {

        this.element = element;

        this.next = next;

        this.previous = previous;

    }

    }

节点类很简单,element存放业务数据,previous与next分别存放前后节点的信息(在数据结构中我们通常称之为前后节点的指针)。

声明LinkedList对象时,创建一个Entry对象,不过全部为空。

private transient Entry header = new Entry(nullnullnull);

private transient int size = 0;

在执行构造函数的时候,将header实例的previous和next全部指向header实例。

public LinkedList(){

       header.next = header.previous = header;

    }

执行完构造函数后,header实例自身形成一个闭环,如下图所示:

图——初始化LinkedList

 

3.2      添加数据:add()

从源代码中我们可知——给LinkedList添加数据是从双向链表的头开始的,代码如下所示:

public boolean add(E e) {

    addBefore(e, header);

       return true;

    }

private Entry addBefore(Ee, Entry entry) {

    Entry newEntry = new Entry(e, entry, entry.previous);

    newEntry.previous.next =newEntry;

    newEntry.next.previous =newEntry;

    size++;

    modCount++;

    return newEntry;

    }

下面分解“添加第一个数据”的步骤:

第一步:初始化后LinkedList实例的情况:

图——初始化后

 

第二步:初始化一个预添加的Entry实例(newEntry)。

Entry newEntry = newEntry(e, entry, entry.previous);

图——创建新节点实例

 

第三步:调整新加入节点和头结点(header)的前后指针。

newEntry.previous.next = newEntry;

newEntry.previous即header,newEntry.previous.next即header的next指向newEntry实例。在上图中应该是“4号线”指向newEntry。

newEntry.next.previous = newEntry;

newEntry.next即header,newEntry.next.previous即header的previous指向newEntry实例。在上图中应该是“3号线”指向newEntry。

调整后如下图所示:

图——加入第一个节点后LinkedList示意图

 

下面分解“添加第二个数据”的步骤:

第一步:新建节点。

图——添加第二个节点

 

第二步:调整新节点和头结点的前后指针信息。

图——调整前后指针信息

 

添加后续数据情况和上述一致,LinkedList实例是没有容量限制的。

3.3      删除数据:remove()remove(int)remove(Object)

LinkedList的数据删除操作虽然有多种(这里认为只包括remove()、remove(int)、remove(Object) ),但是真正执行删除操作的代码如下所示:

private E remove(Entry 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;

    }

Remove方法接收的参数是一个节点实例,即预被删除的节点。只不过remove()是删除的第一个节点(头结点后的第一个节点)——

public EremoveFirst() {

    return remove(header.next);

    }

Remove(int)是删除指定位置的节点——

private Entry entry(int index) {

       if (index < 0 || index >= size)

           throw new IndexOutOfBoundsException("Index: "+index+

                                               ", Size: "+size);

       Entry e = header;

       if(index < (size >> 1)) {//判断是从头开始遍历还是从尾开始遍历 

            for (int i = 0; i<= index; i++)

                e = e.next;

        } else {

            for (int i = size;i > index; i--)

                e = e.previous;

        }

       return e;

    }

Remove(Object)是删除指定内容的节点——

public boolean remove(Object o) {

       if (o==null) {

           for (Entry e = header.next; e != header; e = e.next) {

                if (e.element==null) {

                    remove(e);

                    return true;

                }

           }

       } else {

           for (Entry e = header.next; e != header; e = e.next) {

                if (o.equals(e.element)) {

                    remove(e);

                    return true;

                }

           }

       }

       return false;

    }

在回到我们的remove(Entry e)方法:

由于删除了某一节点因此调整相应节点的前后指针信息,如下:

e.previous.next = e.next;//预删除节点的前一节点的后指针指向预删除节点的后一个节点。 

e.next.previous = e.previous;//预删除节点的后一节点的前指针指向预删除节点的前一个节点。 

清空预删除节点:

e.next = e.previous = null;

e.element = null;

交给gc完成资源回收,删除操作结束。

与ArrayList比较而言,LinkedList的删除动作不需要“移动”很多数据,从而效率更高。

3.4      获取数据:get(int)

Get(int)方法的实现在remove(int)中已经涉及过了。首先判断位置信息是否合法(大于等于0,小于当前LinkedList实例的Size),然后遍历到具体位置,获得节点的业务数据(element)并返回。

注意:为了提高效率,需要根据获取的位置判断是从头还是从尾开始遍历。

private Entry entry(int index) {

       if (index < 0 || index >= size)

           throw new IndexOutOfBoundsException("Index: "+index+

                                               ", Size: "+size);

       Entry e = header;

       if(index < (size >> 1)) {//判断是从头开始遍历还是从尾开始遍历 

            for (int i = 0; i<= index; i++)

                e = e.next;

        } else {

            for (int i = size;i > index; i--)

                e = e.previous;

        }

       return e;

    }

注意细节:位运算与直接做除法的区别。

3.5      遍历数据:Iterator()

对ArrayList的iterator有所了解后,在此重点关注以下几点:

当调用iterator方法是,每次都创建一个ListItr实例,它拥有一个属性cursor,起到游标的作用。

Iterator实例的hasNext方法:

public boolean hasNext() {

     return cursor != size();

}

当游标跑到最后时返回false,说明遍历完成。

Iterator实例的next方法:

public E next() {

           checkForComodification();

        try {

       Enext = get(cursor);

       lastRet= cursor++;

       return next;

        } catch (IndexOutOfBoundsException e) {

       checkForComodification();

       throw newNoSuchElementException();

        }

    }

通过get方法返回当前游标位置的节点内容,并将游标后移一个位置。

LinkedList在遍历的过程中,如果有添加、删除等动作发生,会导致ConcurrentModificationException异常,和ArrayList类似。

3.6      判断存在或获取位置 

获取位置的IndexOf方法在remove(Object)中已经涉及,而判断存在的contains(Object)方法则主要是调用IndexOf方法,根据IndexOf返回的位置和-1进行比较进而判断指定数据是否存在。

3.7      注意 

LinkedList是无容量限制的;

LinkedList是非线程安全的;

LinkedList是基于双向链表实现的,当数据顺序无关的情况下,选择ArrayList还是LinkedList要从各动作的执行效率综合考虑。

### JavaLinkedList 集合类 #### 什么是 LinkedList 集合 `LinkedList` 是一种双向链表的数据结构,在 Java 编程中属于 `List` 接口的一种实现形式[^1]。这种数据结构允许元素之间通过指针相互链接,从而形成一个线性的序列。 #### LinkedList 的特性 - **内部实现**:基于双向链表来存储元素,这意味着每个节点不仅保存着自身的数据项还维护着前后相邻结点之间的连接关系。 - **访问速度**:由于不是连续内存分配的方式,因此随机存取效率较低;但在频繁插入删除操作时表现优异因为不需要移动大量现有元素的位置即可完成相应动作[^2]。 #### 如何创建并初始化 LinkedList 对象 为了定义一个具体的实例化对象,可以通过如下方式引入必要的包并且声明带有特定参数化的变量名: ```java import java.util.LinkedList; // 创建 LinkedList 对象,并指定泛型为 String 类型 LinkedList<String> list = new LinkedList<>(); ``` 上述代码片段展示了如何导入所需的库文件以及怎样构建一个新的空列表用于后续的操作处理过程之中[^4]。 #### 常见方法概述 以下是几个常用的 API 方法供开发者调用来管理该类型的容器内的成员: - 添加元素至队列末端或者头部位置; - 移除首尾两端处的目标实体; - 获取第一个或最后一个条目而不将其移出队列之外; - 判断某个给定的对象是否存在于此集合之内等等[^5]。 #### 示例代码展示基本功能的应用场景 下面给出一段简单的例子演示了部分核心函数的具体应用情形: ```java public class Main { public static void main(String[] args) { // 初始化 LinkedList 并加入一些初始值 LinkedList<Integer> numbers = new LinkedList<>(); // 向末尾追加数值 numbers.add(1); numbers.addLast(3); // 插入到开头位置 numbers.addFirst(0); System.out.println("当前内容:" + numbers); // 删除最前面的一个数 Integer removedElement = numbers.remove(); System.out.println("被删掉的是:" + removedElement); // 查看但不取出最后面的数字 int lastNumber = numbers.peekLast(); System.out.println("最后一项是:" + lastNumber); } } ``` 这段程序先建立了一个整数类型的动态数组,接着执行了一系列增减查的动作最终打印出了预期的结果集。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值