seaairland

           

          LinkedList源碼分析

           LinkedList類似C語言的雙向鏈表,但是java中沒有指針如何實現呢,看完LinkedList
            你將對java中的引用類型有更深入的理解。LindedList的聲明如下:
            public class LinkedList extends AbstractSequentialList implements List, Cloneable, java.io.Serializable
            有關AbstractSequentialList:http://blog.csdn.net/treeroot/archive/2004/09/18/108696.aspx
            有關List: http://blog.csdn.net/treeroot/archive/2004/09/14/104638.aspx
            有關Cloneable:http://blog.csdn.net/treeroot/archive/2004/09/07/96936.aspx
            下面分析一下這個鏈表的實現,這里只重點分析某些方法。
            private transient Entry header = new Entry(null, null, null);
            private transient int size = 0;
            public LinkedList() {
            header.next = header.previous = header;
            }
            header相當于C中的頭指針,而且這個頭指針不是鏈表的元素,有關Entry將在下面介紹。
            public LinkedList(Collection c) {
            this();
            addAll(c);
            }
            public Object getFirst() {
            if (size==0)
            throw new NoSuchElementException();
             return header.next.element;
            }
            頭指針的下一個元素就是第一個元素
            public Object getLast() {
            if (size==0)
            throw new NoSuchElementException();
            return header.previous.element;
            }
            頭指針的前一個當然就是最后一個了
            public Object removeFirst() {
            Object first = header.next.element;
            remove(header.next);
            return first;
            }
            public Object removeLast() {
            Object last = header.previous.element;
            remove(header.previous);
            return last;
            }
            public void addFirst(Object o) {
            addBefore(o, header.next);
            }
            public void addLast(Object o) {
            addBefore(o, header);
            }
            這個方法在鏈表末尾插入新的元素,功能和add方法一樣,這個方法完全是為了對稱性(因為有addFirst)
            public boolean contains(Object o) {
            return indexOf(o) != -1;
            }
            public int size() {
            return size;
            }
            public boolean add(Object o) {
            addBefore(o, header);
            return true;
            }
            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;
            }
            用過C的人應該感到很熟悉了,這里就是通過指針后移來查找相同的元素,注意這里最多只刪除一個
            元素,符合List接口中的說明。
            public boolean addAll(Collection c) {
            return addAll(size, c);
            }
            public boolean addAll(int index, Collection c) {
            int numNew = c.size();
            if (numNew==0)
            return false;
            modCount++;
            Entry successor = (index==size ? header : entry(index));
            Entry predecessor = successor.previous;
            Iterator it = c.iterator();
            for (int i=0; i
                 Entry e = new Entry(it.next(), successor, predecessor);
            predecessor.next = e;
            predecessor = e;
            }
            successor.previous = predecessor;
            size += numNew;
            return true;
            }
            這里又看到熟悉的面孔了,在數據結構里面的鏈表中插入元素不就是這樣嗎?
            successor代表后一個指針,就是說插入的數據在他的前面,predecessor代表前一個指針,也就是要插入數據之前的指針。如果對數據結構比較了解的話,應該比較容易理解,這里我可以把代碼改一下,但是不能算是優化:
            for (int i=0; i
                 Entry e = new Entry(it.next(), null, predecessor);
            predecessor.next = e;
            predecessor = e;
            }
            predecessor.next = successor; 
            successor.previous = predecessor;
            這樣修改和原來是一樣的,如果Entry有一個這樣的構造函數Entry(Object element,Entry previous)那就
            好了,那就可以用修改后的代碼優化了(并沒有多大的價值)。如果可以看明白為什么可以這樣修改,那就完全理解了,如果對這種數據結構不熟悉的話,可以畫一個鏈表,然后按代碼執行修改你的鏈表,這個方法很有效的。
            public void clear() {
            modCount++;
            header.next = header.previous = header;
            size = 0;
            }
            public Object get(int index) {
            return entry(index).element;
            }
            public Object set(int index, Object element) {
            Entry e = entry(index);
            Object oldVal = e.element;
            e.element = element;
            return oldVal;
            }
            public void add(int index, Object element) {
            addBefore(element, (index==size ? header : entry(index)));
            }
            public Object remove(int index) {
            Entry e = entry(index);
            remove(e);
            return e.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;
            }
            這個方法返回一個Entry,這里注意注意當index為0時返回的是head.next,不可能返回head。因為index>=0而且
             所以循環語句至少要執行一次。這里指針移動進行了優化,因為是一個雙向鏈表,可以朝不同方向移動,size>>2相當于size=size/2
            public int indexOf(Object o) {
            int index = 0;
            if (o==null) {
            for (Entry e = header.next; e != header; e = e.next) {
            if (e.element==null)
            return index;
            index++;
            }
            } else {
            for (Entry e = header.next; e != header; e = e.next) {
            if (o.equals(e.element))
            return index;
            index++;
            }
            }
            return -1;
            }
            這里唯一注意的就是index++的位置
            public int lastIndexOf(Object o) {
            int index = size;
            if (o==null) {
            for (Entry e = header.previous; e != header; e = e.previous) {
            index--;
            if (e.element==null)
            return index;
            }
            } else {
            for (Entry e = header.previous; e != header; e = e.previous) {
            index--;
            if (o.equals(e.element))
            return index;
            }
            }
            return -1;
            }
            注意index--的位置
            public ListIterator listIterator(int index) {
            return new ListItr(index);
            }
            以下是一個私有內部類
            private class ListItr implements ListIterator {
            private Entry lastReturned = header;
            private Entry next;
            //調用next()方法的節點
            private int nextIndex;
            private int expectedModCount = modCount;
            ListItr(int index) {
            if (index < 0 || index > size)
            throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
            if (index < (size >> 1)) {
            next = header.next;
            for (nextIndex=0; nextIndex
                     next = next.next;
            } else {
            next = header;
            for (nextIndex=size; nextIndex>index; nextIndex--)
            next = next.previous;
            }
            }
            public boolean hasNext() {
            return nextIndex != size;
            }
            public Object next() {
            checkForComodification();
            if (nextIndex == size)
            throw new NoSuchElementException();
             lastReturned = next;
            next = next.next;
            nextIndex++;
            return lastReturned.element;
            }
            public boolean hasPrevious() {
            return nextIndex != 0;
            }
            public Object previous() {
            if (nextIndex == 0)
            throw new NoSuchElementException();
              lastReturned = next = next.previous;
            nextIndex--;
            checkForComodification();
            return lastReturned.element;
            }
            public int nextIndex() {
            return nextIndex;
            }
            public int previousIndex() {
            return nextIndex-1;
            }
            public void remove() {
            checkForComodification();
            try {
            LinkedList.this.remove(lastReturned);
            } catch (NoSuchElementException e) {
            throw new IllegalStateException();
            }
            if (next==lastReturned)  //這里表示刪除的是調用previous()返回的元素。
            next = lastReturned.next; //next被刪除,所以next要后移,索引不變。
            else
            nextIndex--;      //刪除的是next.previous,所以索引要減1。     
            lastReturned = header;  //這里很重要:1.釋放資源。2.不允許連續調用remove。
            expectedModCount++;
            }
            public void set(Object o) {
            if (lastReturned == header)
            throw new IllegalStateException();
            checkForComodification();
            lastReturned.element = o;
            }
            public void add(Object o) {
            checkForComodification();
            lastReturned = header;
            addBefore(o, next);
            nextIndex++;
            expectedModCount++;
            }
            final void checkForComodification() {
            if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
            }
            }
            以下是Entry的定義:
            private static class Entry {
            Object element;
            Entry next;
            Entry previous;
            Entry(Object element, Entry next, Entry previous) {
            this.element = element;
            this.next = next;
            this.previous = previous;
            }
            }
            很簡單,就是一個雙向鏈表的接點,只有一個構造函數而已,沒有其他方法。這里的next和previous不就是一個引用嗎?其實不就是一個C里面的指針嗎!不過C語言不會處理空指針,直接讓操作系統處理了,Java確實拋出一個系統異常NullPointerException,根本不給他破壞系統的機會。
            private Entry addBefore(Object o, Entry e) {
            Entry newEntry = new Entry(o, e, e.previous);
            newEntry.previous.next = newEntry;
            newEntry.next.previous = newEntry;
            size++;
            modCount++;
            return newEntry;
            }
            private void remove(Entry e) {
            if (e == header)
            throw new NoSuchElementException();
             e.previous.next = e.next;
            e.next.previous = e.previous;
            size--;
            modCount++;
            }
            public Object clone() {
            LinkedList clone = null;
            try {
            clone = (LinkedList)super.clone();
            } catch (CloneNotSupportedException e) {
            throw new InternalError();
            }
            // Put clone into "virgin" state
            clone.header = new Entry(null, null, null);
            clone.header.next = clone.header.previous = clone.header;
            clone.size = 0;
            clone.modCount = 0;
            // Initialize clone with our elements
            for (Entry e = header.next; e != header; e = e.next)
            clone.add(e.element);
             return clone;
            }
            public Object[] toArray() {
            Object[] result = new Object[size];
            int i = 0;
            for (Entry e = header.next; e != header; e = e.next)
            result[i++] = e.element;
            return result;
            }
            }
            public Object[] toArray(Object a[]) {
            if (a.length < size)
            a = (Object[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
            int i = 0;
            for (Entry e = header.next; e != header; e = e.next)
            a[i++] = e.element;
             if (a.length > size)
            a[size] = null;
             return a;
            }
            private static final long serialVersionUID = 876323262645176354L;
            private synchronized void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
            // Write out any hidden serialization magic
            s.defaultWriteObject();
            // Write out size
            s.writeInt(size);
            // Write out all elements in the proper order.
            for (Entry e = header.next; e != header; e = e.next)
            s.writeObject(e.element);
            }
            private synchronized void readObject(java.io.ObjectInputStream s) 
             throws java.io.IOException,ClassNotFoundException {
            // Read in any hidden serialization magic
            s.defaultReadObject();
            // Read in size
            int size = s.readInt();
            // Initialize header
            header = new Entry(null, null, null);
            header.next = header.previous = header;
            // Read in all elements in the proper order.
            for (int i=0; i
                 add(s.readObject());
            }
            這里和ArrayList有一個區別就是size被聲明為 transient的,因為這里調用的是add方法,這樣
            size會自動增加,而在ArrayList是直接給數組賦值(效率更高)。
            這里比較一下ArrayList和LinkedList:
            1.ArrayList是基于數組,LinkedList基于鏈表實現。
            2.對于隨機訪問get和set,ArrayList覺得優于LinkedList,因為LinkedList要移動指針。
            3.對于新增和刪除操作add和remove,LinedList比較占優勢,因為ArrayList要移動數據。
            4.查找操作indexOf,lastIndexOf,contains等,兩者差不多。
            這里只是理論上分析,事實上也不一定,比如ArrayList在末尾插入和刪除數據就不設計到數據移動,不過還是
            有這么個建議:隨機訪問比較多的話一定要用ArrayList而不是LinkedList,如果需要頻繁的插入和刪除應該
            考慮用LinkedList來提高性能。
          引自:http://www.wangchao.net.cn/bbsshowlist.jsp?area_id=02&board_id=01&parent_id=43264

          posted on 2006-03-29 21:01 chenhui 閱讀(936) 評論(0)  編輯  收藏 所屬分類: 好文收集

          導航

          統計

          常用鏈接

          留言簿(1)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          介紹 IOC

          友情鏈接

          最新隨筆

          搜索

          積分與排名

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 黄陵县| 靖西县| 雷波县| 甘洛县| 连城县| 交口县| 大英县| 望奎县| 汤阴县| 泸西县| 喀什市| 九台市| 全南县| 邓州市| 龙州县| 南木林县| 江源县| 乌拉特前旗| 吐鲁番市| 大同市| 杨浦区| 南木林县| 高要市| 德安县| 沙河市| 靖西县| 娄烦县| 渑池县| 禄劝| 唐河县| 泸水县| 张北县| 开封市| 腾冲县| 武川县| 浙江省| 德安县| 台州市| 垦利县| 定陶县| 平阴县|