java复习——ArrayList,Vector,LinkedList,Arrays

Array

array:数组,容量固定,无法改变。
创建数组(一定要声明长度):int[] i=new int[4];或者int[] i={1,2,3};//{}里元素的个数就是数组的长度
二维数组可以这样声明int[][] arr=new int[3][];但是不允许int[][] arr=new int[][3];对二维复合数据类型的数组,必须首先为最高维分配引用空间,然后再顺次为低维分配空间。而且,必须为每个数组元素单独分配空间。
byte/short/int类型数组元素默认值为0,long类型数组元素默认值为0l,float类型数组元素默认值为0.0f,double类型数组元素默认值为0.0d,boolean类型数组元素默认值为false。
使用数组内置的length属性获得数组的大小。试用Arrays的静态函数对数组进行操作。例如Arrays.fill(array,1)将array数组元素全部填充为1。使用Arrays.asList(array)将数组array转为List。
利用下面这个函数实现数组复制。src:源数组,index:源数组开始复制的元素下标,dest:目标数组,destIndex:复制元素存放在目标数组起始位置,length:复制的长度
System.arraycopy(src, int index, dest, int destIndex, int length):
数组的下标从0开始,获得数字某一位置的元素可以通过其下标获得。为什么可以通过数组的下标获得对应的对象:
数组在内存是连续存储的,数组名是这段内存的起始地址,而数组的下标,可以理解成一个偏移量。而对于数组array[i]就相对于array便宜了i个单位。通过array+i就可以得到array[i]的内存地址,则可以读出该地址上的对象。

Vector

vector:矢量队列,容量可以自动增长。实现了List, RandomAccess, Cloneable, java.io.Serializable接口,因此它有List的增删改查等功能。RandomAccess接口的随机访问功能,即我们可以通过元素的序号快速的获取元素。Cloneable实现了clone函数,使得vector能被克隆。vector的add,remove等操作都用了 synchronized修饰,因此vector是线程安全的。
vector部分代码:
public class Vector<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    //该数组保存Vector中的数据
    protected Object[] elementData;

    //数据的数量
    protected int elementCount;

    //容量的增长系数
    protected int capacityIncrement;

    
    private static final long serialVersionUID = -2767605614048989439L;

    //指定初始容量大小和增长系数的构造函数
    public Vector(int initialCapacity, int capacityIncrement) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
        this.elementData = new Object[initialCapacity];
        this.capacityIncrement = capacityIncrement;
    }

    //指定容量大小的构造函数
    public Vector(int initialCapacity) {
        this(initialCapacity, 0);
    }

    //默认构造函数,初始容量为10
    public Vector() {
        this(10);
    }
      //指定集合内容的构造函数
    public Vector(Collection<? extends E> c) {
        elementData = c.toArray();
        elementCount = elementData.length;
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
  }
}
vector自动增长容量是怎么实现的呢?
public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);//当前实际容量+1是否超过最大容量
        elementData[elementCount++] = e;
        return true;
    }
private void ensureCapacityHelper(int minCapacity) {
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)//如果实际容量+1大于数组的长度
            grow(minCapacity);//增加容量
    }
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
	    //如果当前数组的长度不足,新建数组,如果容量增长系数大于0,则新数组长度为旧数组长度+容量增长系数,否则新数组的长度=旧数组长度*2;
        int newCapacity = oldCapacity + ((capacityIncrement > 0) ?capacityIncrement : oldCapacity);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

vector在指定位置新增元素

public void add(int index, E element) {
        insertElementAt(element, index);
    }

public synchronized void insertElementAt(E obj, int index) {
        modCount++;
        if (index > elementCount) {
            throw new ArrayIndexOutOfBoundsException(index + " > " + elementCount);
        }
        ensureCapacityHelper(elementCount + 1);//扩容
	    //扩容后把插入点后面的元素同一往后移一位
        System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
        elementData[index] = obj;
        elementCount++;
    }

vector删除元素,会将删除点后的元素往前移动一位。
public synchronized E remove(int index) {
        modCount++;
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);
        E oldValue = elementData(index);

        int numMoved = elementCount - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index, numMoved);//找到删除的元素并删除后,将删除点后面的元素往前移动一位
        elementData[--elementCount] = null; // Let gc do its work

        return oldValue;
    }
public synchronized void removeElementAt(int index) {
        modCount++;
        if (index >= elementCount) {
            throw new ArrayIndexOutOfBoundsException(index + " >= " +
                                                     elementCount);
        }
        else if (index < 0) {
            throw new ArrayIndexOutOfBoundsException(index);
        }
        int j = elementCount - index - 1;
        if (j > 0) {
            System.arraycopy(elementData, index + 1, elementData, index, j);//找到删除的元素并删除后,将删除点后面的元素往前移动一位
        }
        elementCount--;
        elementData[elementCount] = null; /* to let gc do its work */
    }


参考资料: Java 集合深入理解:古老的 Vector

LinkedList

LinkedList:基于双向链表的动态数组,非线程安全。实现了List和Deque。所以LinkedList实现了List的操作,数据的增删改查。

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
    transient int size = 0;//容量

    //首节点
    transient Node<E> first;

    //尾节点
    transient Node<E> last;

    //默认构造函数
    public LinkedList() {
    }

    //指定集合初始元素的构造函数
    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    }
}

LinkedList在指定位置新增元素源码:
    public void add(int index, E element) {
        checkPositionIndex(index);//检查位置是否超出范围

        if (index == size)//插入位置在链表尾
            linkLast(element);
        else
            linkBefore(element, node(index));//在指定节点前插入新元素
    }
    private void checkPositionIndex(int index) {//检查插入位置是否超出范围,超出了就抛出异常
        if (!isPositionIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    private boolean isPositionIndex(int index) {
        return index >= 0 && index <= size;//插入位置不能小于0也不能超过链表的长度
    }
    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++;
    }
    void linkBefore(E e, Node<E> succ) {//在指定节点前插入节点
        // assert succ != null;
        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++;
    }

参考资料: Java之LinkedList源码解读(JDK 1.8)

ArrayList

ArrayList:动态数组,容量可以动态增长。非线程安全。
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    private static final long serialVersionUID = 8683452581122892189L;

    //默认初始容量
    private static final int DEFAULT_CAPACITY = 10;

    //用于空实例共享空数组实例
    private static final Object[] EMPTY_ELEMENTDATA = {};

    //默认空数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    //存放元素的数组
    transient Object[] elementData; // non-private to simplify nested class access

    //容量
    private int size;

    //指定初始容量的构造函数
    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);
        }
    }

    //默认构造函数
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    //指定集合内容的构造函数
    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;
        }
    }
}

ArrayList在指定位置中插入某一对象,源码:

	public void add(int index, E element) {// 在指定位置增加元素
		rangeCheckForAdd(index);
		//
		ensureCapacityInternal(size + 1); // Increments modCount!!
		System.arraycopy(elementData, index, elementData, index + 1, size - index);// 将插入点后面的元素往后移一位
		elementData[index] = element;// 插入新元素
		size++;
	}

	private void rangeCheckForAdd(int index) {// 要插入的位置是否合法(溢出)
		if (index > size || index < 0)
			throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
	}

	// 检查数组的大小是否需要扩容
	private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);/使用默认值和参数中较大的值作为容量的预设值
        }

        ensureExplicitCapacity(minCapacity);//检查是否需要扩容
    }

	private void ensureExplicitCapacity(int minCapacity) {
		modCount++;
		// overflow-conscious code
		if (minCapacity - elementData.length > 0)
			grow(minCapacity);
	}

	private void grow(int minCapacity) {// 扩容
		// overflow-conscious code
		int oldCapacity = elementData.length;
		int newCapacity = oldCapacity + (oldCapacity >> 1);// 新长度=旧长度+旧长度/2
		if (newCapacity - minCapacity < 0)// 取参数中的较大值
			newCapacity = minCapacity;
		if (newCapacity - MAX_ARRAY_SIZE > 0)
			newCapacity = hugeCapacity(minCapacity);
		// minCapacity is usually close to size, so this is a win:
		elementData = Arrays.copyOf(elementData, newCapacity);// 将原来的数组内容复制到新数组中
	}

	private static int hugeCapacity(int minCapacity) {
		if (minCapacity < 0) // overflow
			throw new OutOfMemoryError();
		return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
	}


ArrayList的删除指定位置的元素

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; // clear to let GC do its work

        return oldValue;
    }
private void rangeCheck(int index) {//检查删除的位置是否溢出
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

ArrayList,Vector,LinkedList,Array之间的区别

ArrayList:数组队列,容量可以动态增长(效率较低),扩容需要把旧数组里的内容复制到新数组中。
Vector:线程安全,性能差于ArrayList。
LinkedList:基于链表,允许null元素,没有同步方法,非线程安全。不支持随机访问。
array:数组,容量固定,无法改变。
ArrayList与Vector区别:Vector是线程安全的,ArrayList是非线程安全的。从源码中我们能看到Vector扩容是新容量=原容量*2,而ArrayList的则是新容量=原容量*1.5。Vector可以设置增长系数,而ArrayList不行。
ArrayList与LinkedList区别ArrayList适用于随机访问较多的情况下,但是对于随机插入元素则效率较低。如果ArrayList在非数组尾部插入元素,首先要判断插入的位置是否溢出,然后判断是否需要扩容(如果需要扩容,还需要把旧数组复制到新数组中)。然后再把插入点的元素后移一位。最后才插入元素。而LinkedList只需要记录插入点的前后项即可。而对于获得某位置上的数据,ArrayList可以通过下标获得,而LinkedList则需要进行向前或者向后的遍历才行。

在使用ArrayList等集合类时,如果提前知道长度,应该在初始化时设置长度,防止新增元素后的扩容,提高代码效率或者使用array代替ArrayList。如果在代码中列表常见操作是随机插入或删除,则使用LinkedList代替ArrayList,如果常见操作是随机访问,则使用ArrayList。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值