本文最后更新于 136 天前,其中的信息可能已经有所发展或是发生改变。
ArrayList 是一个用数组实现的集合,支持随机访问,元素有序且可以重复:
ArrayList
是一种变长的集合类,基于定长数组实现
ArrayList
允许空值和重复元素,当往 ArrayList 中添加的元素数量大于其底层数组容量时,其会通过扩容机制重新生成一个更大的数组
ArrayList
底层基于数组实现,所以其可以保证在 O(1)
复杂度下完成随机查找操作
ArrayList
是非线程安全类,并发环境下,多个线程同时操作 ArrayList,会引发不可预知的异常或错误。
| public class ArrayList<E> extends AbstractList<E> |
| implements List<E>, RandomAccess, Cloneable, java.io.Serializable |
-
实现 RandomAccess 接口:
- 这是一个标记接口,一般此标记接口用于
List
实现,以表明它们支持快速(通常是恒定时间)的随机访问
-
实现 Cloneable 接口:
- Cloneable 和 RandomAccess 接口一样也是一个标记接口,接口内无任何方法体和常量的声明
- 如果想克隆对象,必须要 实现 Cloneable 接口,表明该类是可以被克隆的
-
实现 Serializable 接口:
-
实现 List 接口:
- 这个接口是 List 类集合的上层接口,定义了实现该接口的类都必须要实现的一组方法
| |
| private static final int DEFAULT_CAPACITY = 10; |
| |
| |
| private static final Object[] EMPTY_ELEMENTDATA = {}; |
| |
| |
| private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; |
| |
| |
| |
| |
| transient Object[] elementData; |
| |
| |
| private int size; |
| public ArrayList() { |
| this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; |
| } |
此无参构造函数将创建一个 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 声明的数组,注意此时初始容量是0,而不是大家以为的 10。
注意:根据默认构造函数创建的集合,ArrayList list = new ArrayList();
此时集合长度是 0
| public ArrayList(int initialCapacity) { |
| if (initialCapacity > 0) { |
| this.elementData = new Object[initialCapacity]; |
| } else if (initialCapacity == 0) { |
| this.elementData = EMPTY_ELEMENTDATA; |
| } else { |
| throw new IllegalArgumentException("Illegal Capacity: "+ |
| initialCapacity); |
| } |
| } |
初始化集合大小创建 ArrayList 集合。当大于0时,给定多少那就创建多大的数组
当等于0时,创建一个空数组;当小于0时,抛出异常
| public ArrayList(Collection<? extends E> c) { |
| elementData = c.toArray(); |
| if ((size = elementData.length) != 0) { |
| |
| if (elementData.getClass() != Object[].class) |
| elementData = Arrays.copyOf(elementData, size, Object[].class); |
| } else { |
| |
| this.elementData = EMPTY_ELEMENTDATA; |
| } |
| } |
将已有的集合复制到 ArrayList 集合中
源码:
| public boolean add(E e) { |
| ensureCapacityInternal(size + 1); |
| elementData[size++] = e; |
| return true; |
| } |
如上所示,在通过调用 add 方法添加元素之前,我们要首先调用 ensureCapacityInternal 方法来确定集合的大小,如果集合满了,则要进行扩容操作:
| private void ensureCapacityInternal(int minCapacity) { |
| |
| ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); |
| } |
| |
| private static int calculateCapacity(Object[] elementData, int minCapacity) { |
| if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { |
| return Math.max(DEFAULT_CAPACITY, minCapacity); |
| } |
| return minCapacity; |
| } |
| |
| private void ensureExplicitCapacity(int minCapacity) { |
| modCount++; |
| |
| |
| if (minCapacity - elementData.length > 0) |
| grow(minCapacity); |
| } |
在 ensureExplicitCapacity 方法中,首先对修改次数 modCount 加一
这里的 modCount 给 ArrayList 的迭代器使用的,在并发操作被修改时,提供快速失败行为(保证modCount在迭代期间不变,否则抛出ConcurrentModificationException异常,可以查看源码865行),接着判断minCapacity是否大于当前ArrayList内部数组长度,大于的话调用grow方法对内部数组elementData扩容,grow方法代码如下:
| private void grow(int minCapacity) { |
| int oldCapacity = elementData.length; |
| int newCapacity = oldCapacity + (oldCapacity >> 1); |
| if (newCapacity - minCapacity < 0) |
| newCapacity = minCapacity; |
| |
| if (newCapacity - MAX_ARRAY_SIZE > 0) |
| newCapacity = hugeCapacity(minCapacity); |
| |
| elementData = Arrays.copyOf(elementData, newCapacity); |
| } |
| |
| private static int hugeCapacity(int minCapacity) { |
| if (minCapacity < 0) |
| throw new OutOfMemoryError(); |
| return (minCapacity > MAX_ARRAY_SIZE) ? |
| Integer.MAX_VALUE : |
| MAX_ARRAY_SIZE; |
| } |
扩容的核心方法就是调用前面我们讲过的Arrays.copyOf 方法,创建一个更大的数组,然后将原数组元素拷贝过去即可
对于 ArrayList 集合添加元素,总结一下:
- 当通过 ArrayList() 构造一个空集合,初始长度是为0的,第 1 次添加元素,会创建一个长度为10的数组,并将该元素赋值到数组的第一个位置。
- 第 2 次添加元素,集合不为空,而且由于集合的长度size+1是小于数组的长度10,所以直接添加元素到数组的第二个位置,不用扩容。
- 第 11 次添加元素,此时 size+1 = 11,而数组长度是10,这时候创建一个长度为10+10*0.5 = 15 的数组(扩容1.5倍),然后将原数组元素引用拷贝到新数组。并将第 11 次添加的元素赋值到新数组下标为10的位置。
- 第 Integer.MAX_VALUE - 8 = 2147483639,然后 2147483639%1.5=1431655759(这个数是要进行扩容) 次
- 添加元素,为了防止溢出,此时会直接创建一个 1431655759+1 大小的数组,这样一直,每次添加一个元素,都只扩大一个范围。第 Integer.MAX_VALUE - 7 次添加元素时,创建一个大小为 Integer.MAX_VALUE 的数组,在进行元素添加。
- 第 Integer.MAX_VALUE + 1 次添加元素时,抛出 OutOfMemoryError 异常。
注意:可以向集合中添加 null ,因为数组可以有 null 值存在:
| Object[] obj = {null,1}; |
| |
| ArrayList list = new ArrayList(); |
| list.add(null); |
| list.add(1); |
| System.out.println(list.size()); |
| public E remove(int index) { |
| rangeCheck(index); |
| |
| modCount++; |
| E oldValue = elementData(index); |
| |
| int numMoved = size - index - 1; |
| if (numMoved > 0) |
| |
| System.arraycopy(elementData, index+1, elementData, index, |
| numMoved); |
| elementData[--size] = null; |
| |
| return oldValue; |
| } |
remove(int index) 方法表示删除索引index处的元素
首先通过 rangeCheck(index) 方法判断给定索引的范围,超过集合大小则抛出异常
接着通过 System.arraycopy 方法对数组进行自身拷贝
| |
| |
| |
| |
| |
| |
| |
| |
| public static native void arraycopy(Object src, int srcPos, |
| Object dest, int destPos, |
| int length); |
通过调用 set(int index, E element) 方法在指定索引 index 处的元素替换为 element。并返回原数组的元素。
| public E set(int index, E element) { |
| rangeCheck(index); |
| |
| E oldValue = elementData(index); |
| elementData[index] = element; |
| return oldValue; |
| } |
通过调用 rangeCheck(index) 来检查索引合法性
| private void rangeCheck(int index) { |
| if (index >= size) |
| throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); |
| } |
当索引为负数时,会抛出 java.lang.ArrayIndexOutOfBoundsException 异常。
当索引大于集合长度时,会抛出 IndexOutOfBoundsException 异常。
| public E get(int index) { |
| rangeCheck(index); |
| |
| return elementData(index); |
| } |
同理,首先还是判断给定索引的合理性,然后直接返回处于该下标位置的数组元素。
前面我们介绍查找元素时,知道可以通过get(int index)方法,根据索引查找元素,那么遍历同理:
| ArrayList list = new ArrayList(); |
| list.add("a"); |
| list.add("b"); |
| list.add("c"); |
| for(int i = 0 ; i < list.size() ; i ++){ |
| System.out.print(list.get(i) + " "); |
| } |
| ArrayList<String> list = new ArrayList<>(); |
| list.add("a"); |
| list.add("b"); |
| list.add("c"); |
| Iterator<String> it = list.iterator(); |
| while(it.hasNext()){ |
| String str = it.next(); |
| System.out.print(str + " "); |
| } |
在介绍 ArrayList 时,我们知道该类实现了 List 接口,而 List 接口又继承了 Collection 接口,Collection 接口又继承了 Iterable 接口,该接口有个 Iterator iterator() 方法,能获取 Iterator 对象,能用该对象进行集合遍历
ArrayList 类中的该方法实现:
| public Iterator<E> iterator() { |
| return new Itr(); |
| } |
该方法是返回一个 Itr 对象,这个类是 ArrayList 的内部类。
| private class Itr implements Iterator<E> { |
| int cursor; |
| int lastRet = -1; |
| int expectedModCount = modCount; |
| |
| |
| public boolean hasNext() { |
| return cursor != size; |
| } |
| |
| @SuppressWarnings("unchecked") |
| public E next() { |
| checkForComodification(); |
| int i = cursor; |
| if (i >= size) |
| throw new NoSuchElementException(); |
| Object[] elementData = ArrayList.this.elementData; |
| if (i >= elementData.length) |
| throw new ConcurrentModificationException(); |
| cursor = i + 1; |
| return (E) elementData[lastRet = i]; |
| } |
| |
| public void remove() { |
| if (lastRet < 0) |
| throw new IllegalStateException(); |
| checkForComodification(); |
| |
| try { |
| ArrayList.this.remove(lastRet); |
| cursor = lastRet; |
| lastRet = -1; |
| expectedModCount = modCount; |
| } catch (IndexOutOfBoundsException ex) { |
| throw new ConcurrentModificationException(); |
| } |
| } |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public void forEachRemaining(Consumer<? super E> consumer) { |
| Objects.requireNonNull(consumer); |
| final int size = ArrayList.this.size; |
| int i = cursor; |
| if (i >= size) { |
| return; |
| } |
| final Object[] elementData = ArrayList.this.elementData; |
| if (i >= elementData.length) { |
| throw new ConcurrentModificationException(); |
| } |
| while (i != size && modCount == expectedModCount) { |
| consumer.accept((E) elementData[i++]); |
| } |
| |
| cursor = i; |
| lastRet = i - 1; |
| checkForComodification(); |
| } |
| |
| |
| |
| final void checkForComodification() { |
| if (modCount != expectedModCount) |
| throw new ConcurrentModificationException(); |
| } |
| } |
注意在进行 next() 方法调用的时候,会进行 checkForComodification() 调用,该方法表示迭代器进行元素迭代时,如果同时进行增加和删除操作,会抛出 ConcurrentModificationException 异常。
例如:
| ArrayList<String> list = new ArrayList<>(); |
| list.add("a"); |
| list.add("b"); |
| list.add("c"); |
| Iterator<String> it = list.iterator(); |
| while(it.hasNext()){ |
| String str = it.next(); |
| System.out.print(str + " "); |
| list.remove(str); |
| |
| list.set(0, str); |
| } |
解决办法是不调用 ArrayList.remove() 方法,转而调用迭代器的 remove() 方法:
| Iterator<String> it = list.iterator(); |
| while(it.hasNext()){ |
| String str = it.next(); |
| System.out.print(str + " "); |
| it.remove(); |
| } |
注意:迭代器只能向后遍历,不能向前遍历,能够删除元素,但是不能新增元素。
| ArrayList<String> list = new ArrayList<>(); |
| list.add("a"); |
| list.add("b"); |
| list.add("c"); |
| for(String str : list){ |
| System.out.print(str + " "); |
| } |
这种语法可以看成是 JDK 的一种语法糖,通过反编译 class 文件,我们可以看到生成的 java 文件,其具体实现还是通过调用 Iterator 迭代器进行遍历的。如下:
| ArrayList list = new ArrayList(); |
| list.add("a"); |
| list.add("b"); |
| list.add("c"); |
| String str; |
| for (Iterator iterator1 = list.iterator(); iterator1.hasNext(); System.out.print((new StringBuilder(String.valueOf(str))).append(" ").toString())) |
| str = (String)iterator1.next(); |
总结:
- ArrayList可以存放null。
- ArrayList 本质上就是一个elementData数组。
- ArrayList 区别于数组的地方在于能够自动扩展大小,其中关键的方法就是gorw()方法。
- ArrayList 由于本质是数组,所以它在数据的查询方面会很快,而在插入删除这些方面,性能下降很多,底层要移动很多数据才能达到应的效果。
| public class LinkedList<E> |
| extends AbstractSequentialList<E> |
| implements List<E>, Deque<E>, Cloneable, java.io.Serializable |
- 和 ArrayList 集合一样,LinkedList 集合也实现了Cloneable接口和Serializable接口,分别用来支持克隆以及支持序列化。
- List 接口也不用多说,定义了一套 List 集合类型的方法规范。
注意:相对于 ArrayList 集合,LinkedList 集合多实现了一个 Deque
接口,这是一个双向队列接口,双向队列就是两端都可以进行增加和删除操作。
| |
| transient int size = 0; |
| |
| |
| |
| |
| transient Node<E> first; |
| |
| |
| |
| |
| transient Node<E> last; |
注意:这里出现了一个 Node 类,这是 LinkedList 类中的一个内部类,其中每一个元素就代表一个 Node 类对象,LinkedList 集合就是由许多个 Node 对象类似于手拉着手构成。
| private static class Node<E> { |
| |
| E item; |
| Node<E> next; |
| Node<E> prev; |
| |
| |
| Node(Node<E> prev, E element, Node<E> next) { |
| this.item = element; |
| this.next = next; |
| this.prev = prev; |
| } |
| } |
无参构造:
有参构造:
| public LinkedList(Collection<? extends E> c) { |
| this(); |
| addAll(c); |
| } |
LinkedList 有两个构造函数,第一个是默认的空的构造函数,第二个是将已有元素的集合Collection 的实例添加到 LinkedList 中,调用的是 addAll() 方法
注意:LinkedList 是没有初始化链表大小的构造函数,因为链表不像数组,一个定义好的数组是必须要有确定的大小,然后去分配内存空间,而链表不一样,它没有确定的大小,通过指针的移动来指向下一个内存地址的分配。
将指定元素添加到链表头:
| |
| public void addFirst(E e) { |
| linkFirst(e); |
| } |
| |
| private void linkFirst(E e) { |
| final Node<E> f = first; |
| final Node<E> newNode = new Node<>(null, e, f); |
| first = newNode; |
| if (f == null) |
| last = newNode; |
| else |
| f.prev = newNode; |
| size++; |
| modCount++; |
| } |
将指定元素添加到链表尾:
将指定的元素插入此列表中的指定位置:
| |
| public void add(int index, E element) { |
| |
| checkPositionIndex(index); |
| |
| if (index == size) |
| linkLast(element); |
| else |
| linkBefore(element, node(index)); |
| } |
| |
| void linkLast(E e) { |
| final Node<E> l = last; |
| final Node<E> newNode = new Node<>(l, e, null); |
| last = newNode; |
| if (l == null) |
| first = newNode; |
| else |
| l.next = newNode; |
| size++; |
| modCount++; |
| } |
| |
| Node<E> node(int index) { |
| if (index < (size >> 1)) { |
| Node<E> x = first; |
| for (int i = 0; i < index; i++) |
| x = x.next; |
| return x; |
| } else { |
| Node<E> x = last; |
| for (int i = size - 1; i > index; i--) |
| x = x.prev; |
| return x; |
| } |
| } |
| |
| void linkBefore(E e, Node<E> succ) { |
| final Node<E> pred = succ.prev; |
| final Node<E> newNode = new Node<>(pred, e, succ); |
| succ.prev = newNode; |
| if (pred == null) |
| first = newNode; |
| else |
| pred.next = newNode; |
| size++; |
| modCount++; |
| } |
按照指定集合的迭代器返回的顺序,将指定集合中的所有元素追加到此列表的末尾
addAll有两个重载函数:
- addAll(Collection<? extends E>)型
- addAll(int, Collection<? extends E>)型
我们平时习惯调用的 addAll(Collection<? extends E>)
型会转化为 addAll(int, Collection<? extends E>)
型
addAll(c):
| |
| public boolean addAll(Collection<? extends E> c) { |
| return addAll(size, c); |
| } |
| |
| public boolean addAll(int index, Collection<? extends E> c) { |
| |
| checkPositionIndex(index); |
| |
| Object[] a = c.toArray(); |
| |
| int numNew = a.length; |
| if (numNew == 0) |
| |
| return false; |
| |
| Node<E> pred, succ; |
| |
| if (index == size) { |
| |
| |
| |
| |
| |
| succ = null; |
| pred = last; |
| } else { |
| |
| |
| succ = node(index); |
| pred = succ.prev; |
| } |
| |
| for (Object o : a) { |
| @SuppressWarnings("unchecked") E e = (E) o; |
| |
| Node<E> newNode = new Node<>(pred, e, null); |
| |
| if (pred == null) |
| first = newNode; |
| else |
| |
| pred.next = newNode; |
| |
| pred = newNode; |
| } |
| if (succ == null) { |
| |
| |
| |
| last = pred; |
| } else { |
| |
| pred.next = succ; |
| |
| succ.prev = pred; |
| } |
| |
| size += numNew; |
| modCount++; |
| return true; |
| } |
说明:参数中的index表示在索引下标为index的结点(实际上是第index + 1个结点)的前面插入。
在addAll函数中,addAll函数中还会调用到node函数,get函数也会调用到node函数,此函数是根据索引下标找到该结点并返回,具体代码如下:
| Node<E> node(int index) { |
| |
| if (index < (size >> 1)) { |
| Node<E> x = first; |
| for (int i = 0; i < index; i++) |
| x = x.next; |
| return x; |
| } else { |
| Node<E> x = last; |
| for (int i = size - 1; i > index; i--) |
| x = x.prev; |
| return x; |
| } |
| } |
说明:在根据索引查找结点时,会有一个小优化,结点在前半段则从头开始遍历,在后半段则从尾开始遍历,这样就保证了只需要遍历最多一半结点就可以找到指定索引的结点。
通过调用 set(int index, E element) 方法,用指定的元素替换此列表中指定位置的元素
| public E set(int index, E element) { |
| |
| checkElementIndex(index); |
| Node<E> x = node(index); |
| E oldVal = x.item; |
| x.item = element; |
| return oldVal; |
| } |
这里主要是通过 node(index) 方法获取指定索引位置的节点,然后修改此节点位置的元素即可。
返回此列表中的第一个元素:
| public E getFirst() { |
| final Node<E> f = first; |
| if (f == null) |
| throw new NoSuchElementException(); |
| return f.item; |
| } |
返回此列表中的最后一个元素:
| public E getLast() { |
| final Node<E> l = last; |
| if (l == null) |
| throw new NoSuchElementException(); |
| return l.item; |
| } |
返回指定索引处的元素:
| public E get(int index) { |
| checkElementIndex(index); |
| return node(index).item; |
| } |
这里分为两部分查找:
| |
| |
| |
| Node<E> node(int index) { |
| |
| |
| |
| if (index < (size >> 1)) { |
| Node<E> x = first; |
| for (int i = 0; i < index; i++) |
| x = x.next; |
| return x; |
| } else { |
| Node<E> x = last; |
| for (int i = size - 1; i > index; i--) |
| x = x.prev; |
| return x; |
| } |
| } |
即查找的过程:
- 先从第头节点向后遍历到链表中部查找
- 再从尾部节点遍历向前遍历到中部查找
返回此列表中指定元素第一次出现的索引,如果此列表不包含元素,则返回-1
| |
| public int indexOf(Object o) { |
| int index = 0; |
| if (o == null) { |
| for (Node<E> x = first; x != null; x = x.next) { |
| if (x.item == null) |
| return index; |
| index++; |
| } |
| } else { |
| for (Node<E> x = first; x != null; x = x.next) { |
| if (o.equals(x.item)) |
| return index; |
| index++; |
| } |
| } |
| return -1; |
| } |
从此列表中移除并返回第一个元素:
| |
| public E remove() { |
| return removeFirst(); |
| } |
| |
| |
| public E removeFirst() { |
| final Node<E> f = first; |
| if (f == null) |
| throw new NoSuchElementException(); |
| return unlinkFirst(f); |
| } |
| |
| private E unlinkFirst(Node<E> f) { |
| |
| final E element = f.item; |
| final Node<E> next = f.next; |
| f.item = null; |
| f.next = null; |
| first = next; |
| if (next == null) |
| last = null; |
| else |
| next.prev = null; |
| size--; |
| modCount++; |
| return element; |
| } |
从该列表中删除并返回最后一个元素:
| |
| public E removeLast() { |
| final Node<E> l = last; |
| if (l == null) |
| throw new NoSuchElementException(); |
| return unlinkLast(l); |
| } |
| |
| private E unlinkLast(Node<E> l) { |
| |
| final E element = l.item; |
| final Node<E> prev = l.prev; |
| l.item = null; |
| l.prev = null; |
| last = prev; |
| if (prev == null) |
| first = null; |
| else |
| prev.next = null; |
| size--; |
| modCount++; |
| return element; |
| } |
删除此列表中指定位置的元素:
| |
| public E remove(int index) { |
| |
| checkElementIndex(index); |
| return unlink(node(index)); |
| } |
| E unlink(Node<E> x) { |
| |
| final E element = x.item; |
| final Node<E> next = x.next; |
| final Node<E> prev = x.prev; |
| |
| if (prev == null) { |
| first = next; |
| } else { |
| prev.next = next; |
| x.prev = null; |
| } |
| |
| if (next == null) { |
| last = prev; |
| } else { |
| next.prev = prev; |
| x.next = null; |
| } |
| |
| x.item = null; |
| size--; |
| modCount++; |
| return element; |
| } |
| LinkedList<String> linkedList = new LinkedList<>(); |
| linkedList.add("L"); |
| linkedList.add("Y"); |
| linkedList.add("S"); |
| |
| for(int i = 0 ; i < linkedList.size(); i ++){ |
| System.out.print(linkedList.get(i) + " "); |
| } |
注意:
- 利用 LinkedList 的 get(int index) 方法,遍历出所有的元素
- 但是 get(int index) 方法每次都要遍历该索引之前的所有元素,效率极低
在LinkedList中除了有一个Node的内部类外,应该还能看到另外两个内部类,那就是ListItr,还有一个是DescendingIterator。
ListItr内部类:
| private class ListItr implements ListIterator<E> |
看一下他的继承结构,发现只继承了一个ListIterator,到ListIterator中一看:
其中不止有向后迭代的方法,还有向前迭代的方法,可以让linkedList不光能像后迭代,也能向前迭代
看一下ListItr中的方法,可以发现,在迭代的过程中,还能移除、修改、添加值得操作
其中 DescendingIterator
内部类:
| |
| |
| |
| private class DescendingIterator implements Iterator<E> { |
| private final ListItr itr = new ListItr(size()); |
| public boolean hasNext() { |
| return itr.hasPrevious(); |
| } |
| public E next() { |
| return itr.previous(); |
| } |
| public void remove() { |
| itr.remove(); |
| } |
| } |
DescendingIterator 通过调用 ListItr,作用是封装一下Itr中几个方法,让使用者以正常的思维去写代码
例如,在从后往前遍历的时候,也是跟从前往后遍历一样,使用next等操作,而不用使用特殊的previous
| LinkedList<String> linkedList = new LinkedList<>(); |
| linkedList.add("A"); |
| linkedList.add("B"); |
| linkedList.add("C"); |
| linkedList.add("D"); |
| |
| Iterator<String> listIt = linkedList.listIterator(); |
| while(listIt.hasNext()){ |
| System.out.print(listIt.next()+" "); |
| } |
| |
| |
| Iterator<String> it = linkedList.descendingIterator(); |
| while(it.hasNext()){ |
| System.out.print(it.next() + " "); |
| } |
| LinkedList<Integer> linkedList = new LinkedList<>(); |
| for (int i = 0; i < 10000; i ++) { |
| linkedList.add(i); |
| } |
| |
| long beginTimeFor = System.currentTimeMillis(); |
| for (int i = 0; i < 10000; i ++) { |
| System.out.println(linkedList.get(i)); |
| } |
| long endTimeFor = System.currentTimeMillis(); |
| System.out.println("使用普通for循环遍历10000个元素需要的时间:" + (endTimeFor - beginTimeFor)); |
| |
| long beginTimeIte = System.currentTimeMillis(); |
| Iterator<Integer> it = linkedList.listIterator(); |
| while (it.hasNext()) { |
| System.out.print(it.next() + " "); |
| } |
| long endTimeIte = System.currentTimeMillis(); |
| System.out.println("使用迭代器遍历10000个元素需要的时间:" + (endTimeIte - beginTimeIte)); |
一万个元素两者之间都相差一倍多的时间,如果是十万,百万个元素,那么两者之间相差的速度会越来越大,因此推荐使用迭代器遍历 LinkedList。
结合总结下,linklist迭代器比for快。因为迭代器会记录下一个数据的指针。而for循环,get什么的,则会触发O(n)时间的遍历。为什么arraylist不会呢,因为人家可以通过头指针+偏移量O(1)查找到数据。参考文献https://blog.51cto.com/u_16237826/8917700