為什麼使用迭代器iterator遍歷Linkedlist要比普通for快
阿新 • • 發佈:2019-01-23
</pre><p></p><pre name="code" class="java">
大家可以搜尋一下普通情況遍歷linkedlist應該是O(n)但是使用iterator就是常數,這讓我很好奇。於是我去查了原始碼。。
一路順著找找到了Collection,確實有一個iterator但是是個interface還沒有實現。
網上找list,有一個listiterator還是這樣。
只能去linked找了,找到了如下原始碼
private static final class LinkIterator<ET> implements ListIterator<ET> { 61 int pos, expectedModCount; 62 63 final LinkedList<ET> list; 64 65 Link<ET> link, lastLink; 66 67 LinkIterator(LinkedList<ET> object, int location) { 68 list = object; 69 expectedModCount = list.modCount; 70 if (location >= 0 && location <= list.size) { 71 // pos ends up as -1 if list is empty, it ranges from -1 to 72 // list.size - 1 73 // if link == voidLink then pos must == -1 74 link = list.voidLink; 75 if (location < list.size / 2) { 76 for (pos = -1; pos + 1 < location; pos++) { 77 link = link.next; 78 } 79 } else { 80 for (pos = list.size; pos >= location; pos--) { 81 link = link.previous; 82 } 83 } 84 } else { 85 throw new IndexOutOfBoundsException(); 86 } 87 } 88 89 public void add(ET object) { 90 if (expectedModCount == list.modCount) { 91 Link<ET> next = link.next; 92 Link<ET> newLink = new Link<ET>(object, link, next); 93 link.next = newLink; 94 next.previous = newLink; 95 link = newLink; 96 lastLink = null; 97 pos++; 98 expectedModCount++; 99 list.size++; 100 list.modCount++; 101 } else { 102 throw new ConcurrentModificationException(); 103 } 104 } 105 106 public boolean hasNext() { 107 return link.next != list.voidLink; 108 } 109 110 public boolean hasPrevious() { 111 return link != list.voidLink; 112 } 113 114 public ET next() { 115 if (expectedModCount == list.modCount) { 116 LinkedList.Link<ET> next = link.next; 117 if (next != list.voidLink) { 118 lastLink = link = next; 119 pos++; 120 return link.data; 121 } 122 throw new NoSuchElementException(); 123 } 124 throw new ConcurrentModificationException(); 125 } 126 127 public int nextIndex() { 128 return pos + 1; 129 } 130 131 public ET previous() { 132 if (expectedModCount == list.modCount) { 133 if (link != list.voidLink) { 134 lastLink = link; 135 link = link.previous; 136 pos--; 137 return lastLink.data; 138 } 139 throw new NoSuchElementException(); 140 } 141 throw new ConcurrentModificationException(); 142 } 143 144 public int previousIndex() { 145 return pos; 146 } 147 148 public void remove() { 149 if (expectedModCount == list.modCount) { 150 if (lastLink != null) { 151 Link<ET> next = lastLink.next; 152 Link<ET> previous = lastLink.previous; 153 next.previous = previous; 154 previous.next = next; 155 if (lastLink == link) { 156 pos--; 157 } 158 link = previous; 159 lastLink = null; 160 expectedModCount++; 161 list.size--; 162 list.modCount++; 163 } else { 164 throw new IllegalStateException(); 165 } 166 } else { 167 throw new ConcurrentModificationException(); 168 } 169 } 170 171 public void set(ET object) { 172 if (expectedModCount == list.modCount) { 173 if (lastLink != null) { 174 lastLink.data = object; 175 } else { 176 throw new IllegalStateException(); 177 } 178 } else { 179 throw new ConcurrentModificationException(); 180 } 181 } 182 } 183
我們仔細察看next方法
這裡裡面有一個類是叫link,程式碼如下public ET next() { 115 if (expectedModCount == list.modCount) { 116 LinkedList.Link<ET> next = link.next; 117 if (next != list.voidLink) { 118 lastLink = link = next; 119 pos++; 120 return link.data; 121 } 122 throw new NoSuchElementException(); 123 } 124 throw new ConcurrentModificationException(); 125 } 126
private static final class Link<ET> {
49 ET data;
50
51 Link<ET> previous, next;
52
53 Link(ET o, Link<ET> p, Link<ET> n) {
54 data = o;
55 previous = p;
56 next = n;
57 }
58 }
可見list就是一個雙向連結串列的link,沒有什麼特殊之處。到這裡我徹底懵逼了,為什麼呢為什麼呢,為什麼你遍歷就是常數呢?
我們仔細對比一下for迴圈
for(int i =0;i<list.size();i++){
list.get(i);
}
但是iterator和他對比起來少了一個list.get(i);其實就遍歷而言它們兩個差距並不大。但是其中呼叫了一次get(i).這個時間複雜度應該是O(n)所以巢狀一個for迴圈是O(n^2),但是在iterator中因為next的存在get當前項不需要時間所以迴圈下來應該是O(n),原來差距就在get和iterator這裡了