概述Collection接口是集合类的根接口,Java中没有提供这个接口的直接的实现类。但是却让其被继承产生了两个接口,就是Set和List。Set中不能包含重复的元素。List是一个有序的集合,可以包含重复的元素,提供了按索引访问的方式。1.Set(无序、不可重复)Set集合类似于一个罐子,“丢进"Set集合里的多个对象之间没有明显的顺序。Set继承自Collection接口,不能包含有重复元素。Set判断两个对象相同不是使用”==“运算符,而是根据equals方法。也就是说,我们在加入一个新元素的时候,如果这个新元素对象和Set中已有对象进行equals比较都返回false,则Set就会接受这个新元素对象,否则拒绝。如果希望遍历Set集合中的元素只能调用其iterator方法,通过返回的Iterator对象来完成。1.1 HashSetHashSet是底层通过HashMap实现,为快速查找设计的Set。存入HashSet的对象必须定hashCode(),HashSet使用HASH算法来存储集合中的元素,因此具有良好的存取和查找性能。当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据该HashCode值决定该对象在HashSet中的存储位置。值得注意的是,HashSet集合判断两个元素相等的标准是两个对象通过equals()方法比较相等,并且两个对象的hashCode()方法的返回值相等1.2 TreeSetTreeSet是依靠TreeMap来实现的。 TreeSet集合底层数据结构是红黑树(自平衡二叉查找树)。TreeSet的本质是一个”有序的,并且没有重复元素”的集合,而且支持自定义排序。2.List(有序、可重复) List集合代表一个元素有序、可重复的集合,集合中每个元素都有其对应的顺序索引。List集合允许加入重复元素,因为它可以通过索引来访问指定位置的集合元素。List集合默认按元素的添加顺序设置元素的索引2.1 LinkedList概括的说,LinkedList 是线程不安全的,允许元素为null的双向链表。 因其底层数据结构是链表,所以可想而知,它的增删只需要移动指针即可,故时间效率较高。不需要批量扩容,也不需要预留空间,所以空间效率比ArrayList高。缺点就是需要随机访问元素时,时间效率很低,虽然底层在根据下标查询Node的时候,会根据index判断目标Node在前半段还是后半段,然后决定是顺序还是逆序查询,以提升时间效率。不过随着n的增大,总体时间效率依然很低。构造方法//集合元素数量transient int size = 0;//链表头节点transient Node<E> first;//链表尾节点transient Node<E> last;//啥都不干public LinkedList() {}public LinkedList(Collection<? extends E> c) { this(); addAll(c);}对比JDK1.6构造方法private transient Entry<E> header = new Entry<E>(null, null, null);private transient int size = 0;public LinkedList() { header.next = header.previous = header;}public LinkedList(Collection<? extends E> c) { this(); addAll(c);}可以看出在1.7之前LinkedList是双向循环链表,在这之后,因为不再使用header节点,所以默认构造方法什么也不做,first和last会被默认初始化为null。节点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; }}增加1 addAll//addAll ,在尾部批量增加public boolean addAll(Collection<? extends E> c) { return addAll(size, c);//以size为插入下标,插入集合c中所有元素}//以index为插入下标,插入集合c中所有元素public boolean addAll(int index, Collection<? extends E> c) { checkPositionIndex(index);//检查越界 [0,size] 闭区间 Object[] a = c.toArray();//拿到目标集合数组 int numNew = a.length;//新增元素的数量 if (numNew == 0)//如果新增元素数量为0,则不增加,并返回false return false; Node<E> pred, succ; //index节点的前置节点,后置节点 if (index == size) { //在链表尾部追加数据 succ = null; //size节点(队尾)的后置节点一定是null pred = last;//前置节点是队尾 } else { succ = node(index);//取出index节点,作为后置节点 pred = succ.prev; //前置节点是,index节点的前一个节点 } for (Object o : a) {//遍历要添加的节点。 @SuppressWarnings(“unchecked”) E e = (E) o; Node<E> newNode = new Node<>(pred, e, null);//以前置节点 和 元素值e,构建new一个新节点, if (pred == null) //如果前置节点是空,说明是头结点 first = newNode; else//否则 前置节点的后置节点设置为新节点 pred.next = newNode; pred = newNode;//步进,当前的节点为前置节点了,为下次添加节点做准备 } if (succ == null) {//循环结束后,判断,如果后置节点是null。 说明此时是在队尾append的。 last = pred; //则设置尾节点 } else { pred.next = succ; // 否则是在队中插入的节点 ,更新前置节点 后置节点 succ.prev = pred; //更新后置节点的前置节点 } size += numNew; // 修改数量size modCount++; //修改modCount return true;}//根据index 查询出Node,Node<E> node(int index) { // assert isElementIndex(index);//通过下标获取某个node 的时候,(增、查 ),会根据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; }}private void checkPositionIndex(int index) { if (!isPositionIndex(index)) throw new IndexOutOfBoundsException(outOfBoundsMsg(index));}private boolean isPositionIndex(int index) { return index >= 0 && index <= size; //插入时的检查,下标可以是size [0,size]}小结:为什么添加新节点只是让前一个节点的next指向新节点?链表批量增加,是靠for循环遍历原数组,依次执行插入节点操作。通过下标获取某个node 的时候,并不是只能从头到尾查询,因为同时存储了头节点和尾节点,会根据index处于前半段还是后半段进行一个折半查找,以提升查询效率2 插入单个节点add//在尾部插入一个节点: addpublic boolean add(E e) { linkLast(e); return true;} //在指定下标,index处,插入一个节点 public void add(int index, E element) { checkPositionIndex(index);//检查下标是否越界[0,size] if (index == size)//在尾节点后插入 linkLast(element); else//在中间插入 linkBefore(element, node(index)); }//生成新节点 并插入到 链表尾部, 更新 last/first 节点。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++;//修改size modCount++;//修改modCount} //在succ节点前,插入一个新节点e void linkBefore(E e, Node<E> succ) { // assert succ != null; //保存后置节点的前置节点 final Node<E> pred = succ.prev; //以前置和后置节点和元素值e 构建一个新节点 final Node<E> newNode = new Node<>(pred, e, succ); //新节点new是原节点succ的前置节点 succ.prev = newNode; if (pred == null)//如果之前的前置节点是空,说明succ是原头结点。所以新节点是现在的头结点 first = newNode; else//否则构建前置节点的后置节点为new pred.next = newNode; size++;//修改数量 modCount++;//修改modCount } 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++; }删除 //删:remove目标节点 public E remove(int index) { checkElementIndex(index);//检查是否越界 下标[0,size) return unlink(node(index));//从链表上删除某节点 } //删: remove元素值 public boolean remove(Object o) { if (o == null) { for (Node<E> x = first; x != null; x = x.next) { if (x.item == null) { unlink(x); return true; } } } else { for (Node<E> x = first; x != null; x = x.next) { if (o.equals(x.item)) { unlink(x); return true; } } } return false; } //从链表上删除x节点 E unlink(Node<E> x) { // assert x != null; 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++; //修改modCount return element; //返回取出的元素值} private void checkElementIndex(int index) { if (!isElementIndex(index)) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } //下标[0,size) private boolean isElementIndex(int index) { return index >= 0 && index < size; } public E removeFirst() { final Node<E> f = first; if (f == null) throw new NoSuchElementException(); return unlinkFirst(f); } private E unlinkFirst(Node<E> f) { // assert f == first && f != null; final E element = f.item; final Node<E> next = f.next; f.item = null; f.next = null; // help GC 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) { // assert l == last && l != null; final E element = l.item; final Node<E> prev = l.prev; l.item = null; l.prev = null; // help GC last = prev; if (prev == null) first = null; else prev.next = null; size–; modCount++; return element; }修改public E set(int index, E element) { checkElementIndex(index); //检查越界[0,size) Node<E> x = node(index);//取出对应的Node E oldVal = x.item;//保存旧值 供返回 x.item = element;//用新值覆盖旧值 return oldVal;//返回旧值}改也是先根据index找到Node,然后替换值,改不修改modCount查找//根据index查询节点public E get(int index) { checkElementIndex(index);//判断是否越界 [0,size) return node(index).item; //调用node()方法 取出 Node节点,}//根据节点对象,查询下标 public int indexOf(Object o) { int index = 0; if (o == null) {//如果目标对象是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; }从尾至头遍历链表,找到目标元素值为o的节点 public int lastIndexOf(Object o) { int index = size; if (o == null) { for (Node<E> x = last; x != null; x = x.prev) { index–; if (x.item == null) return index; } } else { for (Node<E> x = last; x != null; x = x.prev) { index–; if (o.equals(x.item)) return index; } } return -1; }toArray() public Object[] toArray() { //new 一个新数组 然后遍历链表,将每个元素存在数组里,返回 Object[] result = new Object[size]; int i = 0; for (Node<E> x = first; x != null; x = x.next) result[i++] = x.item; return result; }队列操作 //返回头结点,但不删除 public E peek() { final Node<E> f = first; return (f == null) ? null : f.item; } //返回头结点,但不删除 public E element() { return getFirst(); } //返回头结点并移除 public E poll() { final Node<E> f = first; return (f == null) ? null : unlinkFirst(f); } //删除头结点并返回 public E remove() { return removeFirst(); } //添加指定元素在集合末尾 public boolean offer(E e) { return add(e); }双端队列操作 //在集合头部插入元素 public boolean offerFirst(E e) { addFirst(e); return true; } //在集合尾部插入元素 public boolean offerLast(E e) { addLast(e); return true; } //得到集合第一个元素 public E peekFirst() { final Node<E> f = first; return (f == null) ? null : f.item; } //得到集合最后一个元素但不删除 public E peekLast() { final Node<E> l = last; return (l == null) ? null : l.item; } //得到并移除第一个元素 public E pollFirst() { final Node<E> f = first; return (f == null) ? null : unlinkFirst(f); } //得到并移除最后一个元素 public E pollLast() { final Node<E> l = last; return (l == null) ? null : unlinkLast(l); } //在集合头部插入元素 public void push(E e) { addFirst(e); } //得到并删除第一个元素 ,如果为空抛出异常 public E pop() { return removeFirst(); }迭代器操作public Iterator<E> iterator() { return listIterator(); }public ListIterator<E> listIterator() { return listIterator(0); } public ListIterator<E> listIterator(int index) { checkPositionIndex(index); return new ListItr(index); }从上面可以看到三者的关系是iterator()——>listIterator(0)——>listIterator(int index)。最终都会调用listIterator(int index)方法,其中参数表示迭代器开始的位置。ListIterator是一个可以指定任意位置开始迭代,并且有两个遍历方法。下面直接看ListItr的实现: private class ListItr implements ListIterator<E> { private Node<E> lastReturned; private Node<E> next; private int nextIndex; private int expectedModCount = modCount;//保存当前modCount,确保fail-fast机制 ListItr(int index) { // assert isPositionIndex(index); next = (index == size) ? null : node(index);//得到当前索引指向的next节点 nextIndex = index; } public boolean hasNext() { return nextIndex < size; } //获取下一个节点 public E next() { checkForComodification(); if (!hasNext()) throw new NoSuchElementException(); lastReturned = next; next = next.next; nextIndex++; return lastReturned.item; } public boolean hasPrevious() { return nextIndex > 0; } //获取前一个节点,将next节点向前移 public E previous() { checkForComodification(); if (!hasPrevious()) throw new NoSuchElementException(); lastReturned = next = (next == null) ? last : next.prev; nextIndex–; return lastReturned.item; } public int nextIndex() { return nextIndex; } public int previousIndex() { return nextIndex - 1; } public void remove() { checkForComodification(); if (lastReturned == null) throw new IllegalStateException(); Node<E> lastNext = lastReturned.next; unlink(lastReturned); if (next == lastReturned) next = lastNext; else nextIndex–; lastReturned = null; expectedModCount++; } public void set(E e) { if (lastReturned == null) throw new IllegalStateException(); checkForComodification(); lastReturned.item = e; } public void add(E e) { checkForComodification(); lastReturned = null; if (next == null) linkLast(e); else linkBefore(e, next); nextIndex++; expectedModCount++; }}在ListIterator的构造器中,得到了当前位置的节点,就是变量next。next()方法返回当前节点的值并将next指向其后继节点,previous()方法返回当前节点的前一个节点的值并将next节点指向其前驱节点。由于Node是一个双端节点,所以这儿用了一个节点就可以实现从前向后迭代和从后向前迭代。另外在ListIterator初始时,exceptedModCount保存了当前的modCount,如果在迭代期间,有操作改变了链表的底层结构,那么再操作迭代器的方法时将会抛出ConcurrentModificationException。其他public boolean contains(Object o) { return indexOf(o) != -1; } //返回一个浅拷贝LinkedList对象 public Object clone() { LinkedList<E> clone = superClone(); // Put clone into “virgin” state clone.first = clone.last = null; clone.size = 0; clone.modCount = 0; // Initialize clone with our elements for (Node<E> x = first; x != null; x = x.next) clone.add(x.item); return clone; } private LinkedList<E> superClone() { try { return (LinkedList<E>) super.clone(); } catch (CloneNotSupportedException e) { throw new InternalError(e); } } public void clear() { for (Node<E> x = first; x != null; ) { Node<E> next = x.next; x.item = null; x.next = null; x.prev = null; x = next; } first = last = null; size = 0; modCount++; }2.2 ArrayListArrayList 是一个动态数组,它是线程不安全的,允许元素为null。因其底层数据结构是数组,所以可想而知,它是占据一块连续的内存空间(容量就是数组的length),所以它也有数组的缺点,空间效率不高。由于数组的内存连续,可以根据下标以O(1)的时间读写(改查)元素,因此时间效率很高。当集合中的元素超出这个容量,便会进行扩容操作。扩容操作也是ArrayList 的一个性能消耗比较大的地方,所以若我们可以提前预知数据的规模,应该通过public ArrayList(int initialCapacity) {}构造方法,指定集合的大小,去构建ArrayList实例,以减少扩容次数,提高效率。或者在需要扩容的时候,手动调用public void ensureCapacity(int minCapacity) {}方法扩容。构造方法//默认构造函数里的空数组 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //存储集合元素的底层实现:真正存放元素的数组 transient Object[] elementData; //当前元素数量 private int size; //默认构造方法 public ArrayList() { //默认构造方法只是简单的将 空数组赋值给了elementData this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } //空数组 private static final Object[] EMPTY_ELEMENTDATA = {}; //带初始容量的构造方法 public ArrayList(int initialCapacity) { //如果初始容量大于0,则新建一个长度为initialCapacity的Object数组. //注意这里并没有修改size(对比第三个构造函数) if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) {//如果容量为0,直接将EMPTY_ELEMENTDATA赋值给elementData this.elementData = EMPTY_ELEMENTDATA; } else {//容量小于0,直接抛出异常 throw new IllegalArgumentException(“Illegal Capacity: “+ initialCapacity); } } //利用别的集合类来构建ArrayList的构造函数 public ArrayList(Collection<? extends E> c) { //直接利用Collection.toArray()方法得到一个对象数组,并赋值给elementData elementData = c.toArray(); //因为size代表的是集合元素数量,所以通过别的集合来构造ArrayList时,要给size赋值 if ((size = elementData.length) != 0) { // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class)//这里是当c.toArray出错,没有返回Object[]时,利用Arrays.copyOf 来复制集合c中的元素到elementData数组中 elementData = Arrays.copyOf(elementData, size, Object[].class); } else { //如果集合c元素数量为0,则将空数组EMPTY_ELEMENTDATA赋值给elementData this.elementData = EMPTY_ELEMENTDATA; } } public Object[] toArray() { return Arrays.copyOf(elementData, size); }常用API1 增加每次 add之前,都会判断add后的容量,是否需要扩容。public boolean add(E e) { ensureCapacityInternal(size + 1); elementData[size++] = e;//在数组末尾追加一个元素,并修改size return true;}private static final int DEFAULT_CAPACITY = 10;//默认扩容容量 10 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);}//需要扩容的话,默认扩容一半private void grow(int minCapacity) { int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1);//默认扩容一半 if (newCapacity - minCapacity < 0)//如果还不够,那么就用 能容纳的最小的数量。(add后的容量) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) //若newCapacity 大于最大存储容量,则进行大容量分配 newCapacity = hugeCapacity(minCapacity); elementData = Arrays.copyOf(elementData, newCapacity);//拷贝,扩容,构建一个新数组,}//大容量分配,最大分配 Integer.MAX_VALUEprivate static int hugeCapacity(int minCapacity) { if (minCapacity < 0) throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;}public boolean addAll(Collection<? extends E> c) { Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); //确认是否需要扩容 System.arraycopy(a, 0, elementData, size, numNew);// 复制数组完成复制 size += numNew; return numNew != 0;}public boolean addAll(int index, Collection<? extends E> c) { rangeCheckForAdd(index);//越界判断 Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew);//确认是否需要扩容 int numMoved = size - index; if (numMoved > 0) System.arraycopy(elementData, index, elementData, index + numNew, numMoved);//移动(复制)数组 System.arraycopy(a, 0, elementData, index, numNew);//复制数组完成批量赋值 size += numNew; return numNew != 0;} add、addAll 先判断是否越界,是否需要扩容,如果扩容, 就复制数组,然后设置对应下标元素值。 值得注意的是: 如果需要扩容的话,默认扩容一半。如果扩容一半不够,就用目标的size作为扩容后的容量。在扩容成功后,会修改modCount。扩容操作会导致数组复制,批量删除会导致找出两个集合的交集,以及数组复制操作,因此,增、删都相对低效。 而 改、查都是很高效的操作。Vector的内部也是数组做的,区别在于Vector在API上都加了synchronized所以它是线程安全的,以及Vector扩容时,是翻倍size,而ArrayList是扩容50%。2 删除public E remove(int index) { rangeCheck(index);//判断是否越界 modCount++;//修改modeCount 因为结构改变了 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;} //根据下标从数组取值 并强转 E elementData(int index) { return (E) elementData[index]; }//删除该元素在数组中第一次出现的位置上的数据。 如果有该元素返回true,如果false。public boolean remove(Object o) { if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index);//根据index删除元素 return true; } } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } return false;}//不会越界 不用判断 ,也不需要取出该元素。private void fastRemove(int index) { modCount++;//修改modCount int numMoved = size - index - 1;//计算要移动的元素数量 if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved);//以复制覆盖元素 完成删除 elementData[–size] = null;//置空 不再强引用}//批量删除public boolean removeAll(Collection<?> c) { Objects.requireNonNull(c);//判空 return batchRemove(c, false);}//批量移动private boolean batchRemove(Collection<?> c, boolean complement) { final Object[] elementData = this.elementData; int r = 0, w = 0;//w 代表批量删除后 数组还剩多少元素 boolean modified = false; try { for (; r < size; r++) if (c.contains(elementData[r]) == complement) // 如果c里不包含当前下标元素, elementData[w++] = elementData[r];//则保留 } finally { if (r != size) { //出现异常会导致 r !=size , 则将出现异常处后面的数据全部复制覆盖到数组里。 System.arraycopy(elementData, r, elementData, w, size - r); w += size - r;//修改 w数量 } if (w != size) {//置空数组后面的元素 for (int i = w; i < size; i++) elementData[i] = null; modCount += size - w;//修改modCount size = w;// 修改size modified = true; } } return modified;}可以看出,当用来作为删除元素的集合里的元素多余被删除集合时,也没事,只会删除它们共同拥有的元素。小结:删除操作一定会修改modCount,且可能涉及到数组的复制,相对低效。3 修改不会修改modCount,相对增删是高效的操作。public E set(int index, E element) { rangeCheck(index);//越界检查 E oldValue = elementData(index); //取出元素 elementData[index] = element;//覆盖元素 return oldValue;//返回元素}4 查询不会修改modCount,相对增删是高效的操作。public E get(int index) { rangeCheck(index);//越界检查 return elementData(index); //下标取数据}E elementData(int index) { return (E) elementData[index];}5 清空 clear会修改modCount。public void clear() { modCount++;//修改modCount for (int i = 0; i < size; i++) //将所有元素置null elementData[i] = null; size = 0; //修改size }6 包含 contain//普通的for循环寻找值,只不过会根据目标对象是否为null分别循环查找。public boolean contains(Object o) { return indexOf(o) >= 0;}//普通的for循环寻找值,只不过会根据目标对象是否为null分别循环查找。public int indexOf(Object o) { if (o == null) { for (int i = 0; i < size; i++) if (elementData[i]==null) return i; } else { for (int i = 0; i < size; i++) if (o.equals(elementData[i])) return i; } return -1;}7 迭代器 Iterator.public Iterator<E> iterator() { return new Itr();}private class Itr implements Iterator<E> { int cursor; // 要返回的下一个元素的索引,默认是0 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)//再次判断是否越界,在 我们这里的操作时,有异步线程修改了List throw new ConcurrentModificationException(); cursor = i + 1;//游标+1 return (E) elementData[lastRet = i];//返回元素 ,并设置上一次返回的元素的下标 } public void remove() {//remove 掉 上一次next的元素 if (lastRet < 0)//先判断是否next过 throw new IllegalStateException(); checkForComodification();//判断是否修改过 try { ArrayList.this.remove(lastRet);//删除元素 remove方法内会修改 modCount 所以后面要更新Iterator里的这个标志值 cursor = lastRet; //要删除的游标 lastRet = -1; //不能重复删除 所以修改删除的标志位 expectedModCount = modCount;//更新 判断集合是否修改的标志, } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } }//判断是否修改过了List的结构,如果有修改,抛出异常 final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }}总结 ArrayList和Vectorvector是线程同步的,所以它也是线程安全的,而arraylist是线程异步的,是不安全的。如果不考虑到线程的安全因素,一般用arraylist效率比较高。如果集合中的元素的数目大于目前集合数组的长度时,vector增长率为目前数组长度的100%,而arraylist增长率为目前数组长度的50%。如果在集合中使用数据量比较大的数据,用vector有一定的优势。如果查找一个指定位置的数据,vector和arraylist使用的时间是相同的,如果频繁的访问数据,这个时候使用vector和arraylist都可以。而如果移动一个指定位置会导致后面的元素都发生移动,这个时候就应该考虑到使用linklist,因为它移动一个指定位置的数据时其它元素不移动。ArrayList 和Vector是采用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,都允许直接序号索引元素,但是插入数据要涉及到数组元素移动等内存操作,所以索引数据快,插入数据慢,Vector由于使用了synchronized方法(线程安全)所以性能上比ArrayList要差,LinkedList使用双向链表实现存储,按序号索引数据需要进行向前或向后遍历,但是插入数据时只需要记录本项的前后项即可,所以插入数度较快。 ArrayList和LinkedListArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。对于随机访问get和set,ArrayList优于LinkedList,因为LinkedList要移动指针。对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。 这一点要看实际情况的。若只对单条数据插入或删除,ArrayList的速度反而优于LinkedList。但若是批量随机的插入删除数据,LinkedList的速度大大优于ArrayList. 因为ArrayList每插入一条数据,要移动插入点及之后的所有数据。
...