zoukankan      html  css  js  c++  java
  • JDK源代码学习系列04----ArrayList

                                                                             JDK源代码学习系列04----ArrayList

    1.ArrayList简单介绍

    ArrayList是基于Object[] 数组的,也就是我们常说的动态数组。

    它能非常方便的实现数组的添加删除等操作。

    public class ArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable</span>

    ArrayList支持泛型,它继承自AbstractList。实现了List、RandomAccess、Cloneable、java.io.Serializable接口。
    List接口定义了列表必须实现的方法。
    RandomAccess是一个标记接口,接口内未定义不论什么内容。
    实现了Cloneable接口的类,能够调用Object.clone方法返回该对象的浅拷贝。
    通过实现 java.io.Serializable 接口以实现序列化功能。

    2.ArrayList成员变量

    private transient Object[] elementData;//注意keyword transient
    private int size;//实际size,不是容量
    Javakeyword transient:是为了在序列化时保护对象的某些域不被序列化。在Java序列化Serializable具体解释中有所提及。

    关于transient的具体内容稍后补上。

    3.ArrayList构造函数

     public ArrayList(int initialCapacity) {//为ArrayList初始化容量
    	super();
            if (initialCapacity < 0)//若传入參数小于0。则报IllegalArgumentException的错误
                throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
    	this.elementData = new Object[initialCapacity];//初始elementData数组的大小为此传入的參数
        }
     
     public ArrayList() {//ArrayList初始容量,即默认容量为10
    	this(10);
        }
      
     public ArrayList(Collection<?

    extends E> c) {//把整个集合初始化给ArrayList.注意:该集合内的数据类型必须与ArrayList一致!!!! elementData = c.toArray(); size = elementData.length; // c.toArray might (incorrectly) not return Object[] (see 6260652)//!!这是jdk的一个bug,说是c.toArray()不一定返回的是Object类型 if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class);//调用的Arrays.copyOf() }

    4.ArrayList的成员函数

    a.void trimToSize() 

    public void trimToSize() {//节约内存
    	modCount++;//此变量记录ArrayList被改变的次数  。!!

    超级注意!

    !。 int oldCapacity = elementData.length; if (size < oldCapacity) {//!!size往往不等于elementData.length;elementData.length是数组的初始长度。size是实际内容的长度!! elementData = Arrays.copyOf(elementData, size); } }

    modCount变量是记录ArrayList被改变的次数。为什么须要这个变量呢?

    ArrayList不是线程安全(异步)的。

    这里会引出Fail-Fast机制: 

    ArrayList不是线程安全的,因此假设在使用迭代器的过程中有其它线程改动了map,那么将抛出ConcurrentModificationException,这就是所谓fail-fast策略。
    这一策略在源代码中的实现是通过modCount域。modCount顾名思义就是改动次数。对ArrayList 结构的改动(长度的变化。添加,删除;赋值不是结构变化)都将添加这个值,那么在迭代器初始化过程中会将这个值赋给迭代器的expectedModCount。

    在迭代过程中,推断modCount跟expectedModCount是否相等。假设不相等就表示已经有其它线程改动了ArrayList。
    ArrayList中的mouCount是在他的父类Abstract中申明的。

    protected transient int modCount = 0;
    b.void ensureCapacity(int minCapacity)

    public void ensureCapacity(int minCapacity) {
    	modCount++;
    	int oldCapacity = elementData.length;
    	if (minCapacity > oldCapacity) {
    	    Object oldData[] = elementData;
    	    int newCapacity = (oldCapacity * 3)/2 + 1;//若传入參数大于原容量,先扩为  1.5*原容量+1
        	    if (newCapacity < minCapacity)
    		newCapacity = minCapacity;//若扩为 1.5*原容量+1 后还是小于传入的參数,则把传入的參数作为新容量
                // minCapacity is usually close to size, so this is a win:
                elementData = Arrays.copyOf(elementData, newCapacity);
    	}
        }
    ArrayList须要扩容时至少都是扩为 1.5*原容量+1 ,若1.5*原容量+1 还是小于传入的參数,才把传入的參数作为新容量。
    c.boolean contains(Object o)

    public boolean contains(Object o) {
    	return indexOf(o) >= 0;
        }
    d.int indexOf(Object o)

     public int indexOf(Object o) {
    	if (o == null) {//定位是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;
        }
    e.Object[] toArray()   /   <T> T[] toArray(T[] a)

    调用的是Arrays.copyOf()

    public Object[] toArray() {
            return Arrays.copyOf(elementData, size);
        }
     public <T> T[] toArray(T[] a) {
            if (a.length < size)
                // Make a new array of a's runtime type, but my contents:
                return (T[]) Arrays.copyOf(elementData, size, a.getClass());//若传入的数组长度小于size(ArrayList实际长度),则返回一个长度为size新的数组
    	System.arraycopy(elementData, 0, a, 0, size);//若传入数组长度相等,则把elementData复制进传入数组
            if (a.length > size)//若传入的a的长度大于原本数组。则后面补null
                a[size] = null;
            return a;
        }
    f.E set(int index, E element)

    set是直接替换掉该位置元素。而add是插入该位置,其余元素后移。

    public E set(int index, E element) {
    	RangeCheck(index);//參数检查的方法~~一定要时刻注意參数检查哦~~
    	E oldValue = (E) elementData[index];
    	elementData[index] = element;
    	return oldValue;
        }
    g.boolean add(E e)   /   void add(int index, E element)

    public boolean add(E e) {
    	ensureCapacity(size + 1);  // add()时先扩容!
    	elementData[size++] = e;//!!写的非常好,size++的同一时候还完毕了在最后位置的赋值。之所以size++不会报边界溢出的错误是由于上面已经扩容了。
    	return true;
        }
    public void add(int index, E element) {
    	if (index > size || index < 0)throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
    	ensureCapacity(size+1);  // Increments modCount!!
    	System.arraycopy(elementData, index, elementData, index + 1,size - index);//!!把elementData的数据从index->末尾所有拷贝到从index+1開始。复制长度无size-index
    	elementData[index] = element;//相当于把传入的element插入到空出的位置,即原index
    	size++;
        }
    注意System.arracopy()方法!

    h.E remove(int index)    /    boolean remove(Object o)    /    void fastRemove(int index)

     public E remove(int index) {
    	RangeCheck(index);
    	modCount++;
    	E oldValue = (E) elementData[index];//得到须要返回的被remove掉的元素
    	int numMoved = size - index - 1;//复制时复制的长度。
    	if (numMoved > 0)
    	    System.arraycopy(elementData, index+1, elementData, index,numMoved);//把原数组从index+1-->末尾的数据拷贝到 index的位置,即相当于把原本index上的数据覆盖掉了。这样最后就空出了一个位置。
    	elementData[--size] = null; // 先把size减一,在把最后一赋值为null
    	return oldValue;
        }
    public boolean remove(Object o) {
    	if (o == null) {//!!推断是否为null ,养成编程好习惯
                for (int index = 0; index < size; index++)
    		if (elementData[index] == null) {
    		    fastRemove(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++; int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index,numMoved); elementData[--size] = null; // Let gc do its work }

    i.void clear()

     public void clear() {//把每一个都置为null,再设置size=0;
    	modCount++;
    	// Let gc do its work
    	for (int i = 0; i < size; i++)
    	    elementData[i] = null;
    
    	size = 0;
        }
    g.boolean addAll(int index, Collection<? extends E> c)

    public boolean addAll(int index, Collection<?

    extends E> c) { if (index > size || index < 0) throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); Object[] a = c.toArray(); int numNew = a.length; ensureCapacity(size + numNew); // 扩容时传入的參数为:如今的实际长度+传入的集合的长度 int numMoved = size - index; if (numMoved > 0) System.arraycopy(elementData, index, elementData, index + numNew,numMoved);//把原数组从index-->末尾拷贝到 index+numNew-->末尾。即中间空出numNum的长度来为传入的集合做准备。 System.arraycopy(a, 0, elementData, index, numNew); size += numNew; return numNew != 0; }

    5.总结

    a.ArrayList动态数组!

    b.ArrayList 非线程安全,即 是异步的。 单线程才用ArrayList。















查看全文
  • 相关阅读:
    [转]windows7远程桌面连接失败:发生身份验证错误。要求的函数不受支持
    SNMP协议学习笔记
    Sublime for MacOS 使用技巧
    Git常用操作
    罗技K380连接Win10(MacBookPro双系统)系统失败
    Git知识点汇总
    开发工作中提高效率的一些方式
    css
    IO多路复用
    进程
  • 原文地址:https://www.cnblogs.com/ldxsuanfa/p/10754974.html
  • Copyright © 2011-2022 走看看