5、删除元素

public boolean remove(Object o)

使用方法:

list.remove("hello")

源代码:


/**      * 删除list中的第一个o      * 1)获取锁、上锁      * 2)获取旧数组、旧数组的长度len      * 3)如果旧数组长度为0,返回false      * 4)如果旧数组有值,创建新数组,容量为len-1      * 5)从0开始遍历数组中除了最后一个元素的所有元素      * 5.1)将旧数组中将被删除元素之前的元素复制到新数组中,      * 5.2)将旧数组中将被删除元素之后的元素复制到新数组中      * 5.3)将新数组赋给全局array      * 6)如果是旧数组的最后一个元素要被删除,则      * 6.1)将旧数组中将被删除元素之前的元素复制到新数组中      * 6.2)将新数组赋给全局array      */     public boolean remove(Object o) {         final ReentrantLock lock = this.lock;         lock.lock();         try {             Object[] elements = getArray();//获取原数组             int len = elements.length;//获取原数组长度             if (len != 0) {//如果有数据                 // Copy while searching for element to remove                 // This wins in the normal case of element being present                 int newlen = len - 1;//新数组长度为原数组长度-1                 Object[] newElements = new Object[newlen];//创建新数组                  for (int i = 0; i < newlen; ++i) {//遍历新数组(不包含最后一个元素)                     if (eq(o, elements[i])) {                         // 将旧数组中将被删除元素之后的元素复制到新数组中                         for (int k = i + 1; k < len; ++k)                             newElements[k - 1] = elements[k];                         setArray(newElements);//将新数组赋给全局array                         return true;                     } else                         newElements[i] = elements[i];//将旧数组中将被删除元素之前的元素复制到新数组中                 }                  if (eq(o, elements[newlen])) {//将要删除的元素时旧数组中的最后一个元素                     setArray(newElements);                     return true;                 }             }             return false;         } finally {             lock.unlock();         }     }


判断两个对象是否相等:

/**      * 判断o1与o2是否相等      */     private static boolean eq(Object o1, Object o2) {         return (o1 == null ? o2 == null : o1.equals(o2));     }

注意点:


  • 需要加锁
  • ArrayList的remove使用了System.arraycopy(这是一个native方法),而这里没使用,所以理论上这里的remove的性能要比ArrayList的remove要低


6、遍历所有元素

iterator() hasNext() next()

使用方法:

讲解用的:

Iterator<String> itr = list.iterator();         while(itr.hasNext()){             System.out.println(itr.next());         }

实际中使用的:

for(String str : list){             System.out.println(str);         }

源代码:

public Iterator<E> iterator() {         return new COWIterator<E>(getArray(), 0);     }


private static class COWIterator<E> implements ListIterator<E> {         private final Object[] snapshot;//数组快照         private int cursor;//可看做数组索引          private COWIterator(Object[] elements, int initialCursor) {             cursor = initialCursor;             snapshot = elements;//将实际数组赋给数组快照         }                  public boolean hasNext() {             return cursor < snapshot.length;//0~snapshot.length-1         }                  public E next() {             if (!hasNext())                 throw new NoSuchElementException();             return (E) snapshot[cursor++];         }


说明:这一块儿代码非常简单,看看代码注释就好。

注意:

由于遍历的只是全局数组的一个副本,即使全局数组发生了增删改变化,副本也不会变化,所以不会发生并发异常。但是,可能在遍历的过程中读到一些刚刚被删除的对象。

注意点:


总结:


  • 线程安全,读操作时无锁的ArrayList
  • 底层数据结构是一个Object[],初始容量为0,之后每增加一个元素,容量+1,数组复制一遍
  • 增删改上锁、读不上锁
  • 遍历过程由于遍历的只是全局数组的一个副本,即使全局数组发生了增删改变化,副本也不会变化,所以不会发生并发异常
  • 读多写少且脏数据影响不大的并发情况下,选择CopyOnWriteArrayList

疑问:


  • 每增加一个新元素,都要进行一次数组的复制消耗,那为什么每次不将数组的元素设大(比如说像ArrayList那样,设置为原来的1.5倍+1),这样就会大大减少因为数组元素复制所带来的消耗?
  • get(int)操作会发生脏读,为什么?