ArrayList详解(源码解析)

#一个 BUG 假如我们有1个Object[]数组,并不代表着我们可以将Object对象存进去,这取决于数组中元素实际的类型。 当我么调用Object[] objectArray = stringList.toArray();的时候,objectArray 并不一定能够放置Object对象。这就是源码中的注释:c.toArray might (incorrectly) not return Object[] (see 6260652)。为了考虑这种情况,所以源码中进行了if判断,来防止错误的数组对象导致异常。Arrays.copyOf(elementData, size, Object[].class);这个方法就是用来创建1个Object[]数组,这样数组中就可以存放任意对象了。祥见[!http://www.cnblogs.com/lsf90/p/5366325.html]

#ArrayList--> ##特点:

  •  This class is roughly equivalent to <tt>Vector</tt>, except that it is unsynchronized.(这个类和Vector基本差不多,除了它是线程不安全的)。
    
  •  <p>The <tt>size</tt>, <tt>isEmpty</tt>, <tt>get</tt>, <tt>set</tt>,<tt>iterator</tt>, and <tt>listIterator</tt> operations run in constant time.(size,isEmpty,get,set,itrator,listiterator操作都是O(1)); The <tt>add</tt> operation runs in <i>amortized constant time</i>,that is, adding n elements requires O(n) time.(add操作w以分摊常量时间来运行,也就是O(n))All of the other operations run in linear time (roughly speaking).  The constant factor is low compared to that for the <tt>LinkedList</tt> implementation.(大体上来讲,所有其他操作都以线性时间来运行,和LinkedList实现相比这个常数因子更低。)这里解释下O(n)=a*n+b;b小.
    
  • A structural modification is any operation that adds or deletes one or more elements, or explicitly resizes the backing array; merely setting the value of an element is not a structural modification.(结构型的改变了array的结构的操作,比如,add,delete,数组的大小等。set不算这样的操作。注意这样的操作要用sychronized来保证线程安全。)
    
  • fail-fast:(快速失败机制),这个类的两个迭代器Iterator和ListIterator是快速失败的机制。在Iterator创建后,如果任何时候发生对List的机构性改变,除了用Iterator自己的remove和add方法,都有ConcurrentModification异常!
    

##三种构造器:

  • public ArrayList(int initialCapacity)
  • public ArrayList()
  • public ArrayList(Collection<? extends E> c)
public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

这里看下这个构造函数,就关系到本文我们开头说的那个BUG(see 6260652),看下他是怎么解决的! 我们仿照来验证下:

@Test
    public void test() {
        Collection<String> collection = Arrays.asList("1", "2", "3");
        System.out.println(collection.getClass());
        Object[] objects = new MyArrayList<String>(collection).getElementData();
        System.out.println(objects.getClass());
    }

    class MyArrayList<E> {
        private Object[] elementData;

        public MyArrayList(Collection<? extends E> c) {
            elementData = c.toArray();
        }

        public Object[] getElementData() {
            return elementData;
        }

        public void setElementData(Object[] elementData) {
            this.elementData = elementData;
        }
    }
class java.util.Arrays$ArrayList
class [Ljava.lang.String;

果然如注释所说,没有返回Object[]数组,故我们知道这就是大名鼎鼎的上转型,比如很多情况下虽然是用接口来声明的但是并不是正真的接口在实现,故而开始所说的那个BUG现在也解释通了。那么我们来看看作者怎么解决的呢?

if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);```
可已看出经行了类型判定,不是Object[].class则把所有元素用Arrays.copyOf()用Object[].class类型从新赋值一遍!由此,我们也明白了,如果我们定义一个父类Base.class,如果下用了一种它的子类Sub1.class,而此时想用另一种子类Sub2.class的数据也装进区,我们可以用Arrays.copyOf()用Base[].class这样两种子类和自己就都能装进去了!
#遍历的效率:

   1.  Iterator方式

public void IteratorMethod(List<String> list) { long start, end = 0; Object temp; ListIterator it = list.listIterator(); start = System.nanoTime(); for (; it.hasNext(); ) temp = it.next(); end = System.nanoTime(); System.out.println("IteratorMethod" + (end - start)); }


2.  RandomAccess接口方式:

public void RandomAccessMethod(List<String> list) { long start, end = 0; Object temp; start = System.nanoTime(); for (int i = 0; i < list.size(); i++) temp = list.get(i); end = System.nanoTime(); System.out.println("RandomAccessMethod" + (end - start)); }


3.  forEach方式:

public void ForEachMethod(List<String> list) { long start, end = 0; Object temp; start = System.nanoTime(); for (Object obj : list) temp = obj; end = System.nanoTime(); System.out.println("ForEachMethod" + (end - start)); }

测试

@Test public void ArrayListCompareAccess() { List<String> list = new ArrayList<>(); for (int i = 0; i < 100000; i++) list.add(new StringBuilder("元素").append(i).toString()); IteratorMethod(list); RandomAccessMethod(list); ForEachMethod(list); }


结果:

IteratorMethod15266552 RandomAccessMethod10056916 ForEachMethod12024777

##FastFail(快速失败机制)
ail-fast机制,是一种错误检测机制。它只能被用来检测错误,因为JDK并不保证fail-fast机制一定会发生.若在多线程环境下使用fail-fast机制的集合,建议使用“java.util.concurrent包下的类”去取代“java.util包下的类”。

###Example

public class FastFailDemo { List<String> list = new ArrayList<>(); @Test public void test1() throws InterruptedException { Thread task1 = new MyThread1(); Thread task2 = new MyThread2(); task1.start(); task2.start(); Thread.sleep(1000); } public void printList() { ListIterator it = list.listIterator(); while (it.hasNext()) System.out.print(it.next()+"\t"); System.out.println(); }

class MyThread1 extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            printList();
            list.add(new StringBuilder("element:").append(i).toString());
        }
    }
}

class MyThread2 extends Thread {
    @Override
    public void run() {
        for (int i = 5; i < 10; i++) {
            printList();
            list.add(new StringBuilder("element:").append(i).toString());
        }
    }
}

}

null element:5 null null element:5 element:1 null element:5 element:1 element:2 null element:5 element:1 element:2 element:3 Exception in thread "Thread-1" java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) at java.util.ArrayList$Itr.next(ArrayList.java:851) at collection.FastFailDemo.printList(FastFailDemo.java:39) at collection.FastFailDemo$MyThread2.run(FastFailDemo.java:57)

可以看出果然发生了ConcurrentModificationExceptio 异常。分析如前面所述。核心的就是ArrayList不是线程安全的,在用Iterator的时候如果发生 structural modification则抛出此异常。其实不止在多线程,你在单线程里用list.add,list.remove你试试也会报这个错误!但是注意的是如果在这个过程中我不用Iterator区遍历是不会出现这个错误的。所有的操作多线程还是都能完成.
这里留一个问题,在下用线程池好几次都没报错误,把循环次数调大也没用,可见线程池有自己的体系,具体留着研究(?????????这个为TODO,做到线程池里,回来给链接)
用 List<String> list = Collections.synchronizedList(new ArrayList<>());这个也一样有错!!!
 List<String> list = new CopyOnWriteArrayList<>();这个没问题。总结下就是因为Abstract里的Iterator需要检查modCount值。

final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }

而ArrayList里那些add,remove,clear操作修改了modCount,所以报这个异常了,而CopyOnWriteArrayList没有继承AbstractList,直接实现了List,定义了自己的Iterator,

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; } public boolean hasPrevious() { return cursor > 0; } public E next() { if (! hasNext()) throw new NoSuchElementException(); return (E) snapshot[cursor++]; } public E previous() { if (! hasPrevious()) throw new NoSuchElementException(); return (E) snapshot[--cursor]; } public int nextIndex() { return cursor; } public int previousIndex() { return cursor-1; } public void remove() { throw new UnsupportedOperationException(); } public void set(E e) { throw new UnsupportedOperationException(); } public void add(E e) { throw new UnsupportedOperationException(); }

转载于:https://my.oschina.net/xd03122049/blog/862139

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值