java容器-ArrayList

java集合Collection

一些实用的基本的东西,不会去分析UML图。

下面列出一些主要包含关系,淡然集合还有queue队列但不在此处研究

List:可以有重复元素的集合、有序(存入和取出的顺序不一定相同)

  1. ArrayList:非线程安全、底层是数组的实现,所以方便查询,增删不方便
  2. LinkedList:非线程安全、底层是链表实现,所以增删方便,查询不方便
  3. Vector:几乎和ArrayList一样只不过是用来synchronized加锁,所以效率低一点,但是线程安全

Set:不可以有重复元素的集合、无序

  1. HashSet:线程不安全,存取速度快。底层是以哈希表实现的。
  2. TreeSet:线程不安全,查询速度快。底层是以红黑树实现的。
  3. LinkedHashSet:有序,线程不安全,内部是以LinkHashMap实现的。

ArrayList

基于jdk 1.8

常量

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;
    //最大的大小
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

构造函数

//带初始大小的构造函数
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) {
    	//将collection对象转换成数组,然后将数组的地址的赋给elementData。
        elementData = c.toArray();
        //更新size的值,同时判断size的大小,如果是size等于0,直接将空对象赋给elementData
        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;
        }
    }

add

public boolean add(E e) {
		//这个方法是确保有位子可以放数据
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //存放数据,并让size自增
        elementData[size++] = e;
        return true;
    }
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;
        //扩容大小为本身大小+本身大小/2,也就是1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //判断是否满足现在的需求,否就直接扩容到现在需要的大小
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //判断是否超出界限
        if (newCapacity - MAX_ARRAY_SIZE > 0)
        	//这个方法里面判断了是否大于int最大上限,如果是就取上限否则就是MAX_ARRAY_SIZE 
            newCapacity = hugeCapacity(minCapacity);
        //复制扩容
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
public void add(int index, E element) {
		//这个方法只是判断是否越界,越界就抛出异常
        rangeCheckForAdd(index);
		//和add方法一样
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //直接复制index后面的数据给elementData,并在index存入数据
        //相当于index后面的数据整体后移
        System.arraycopy(elementData, index, elementData, index + 1,
                size - index);
        elementData[index] = element;
        size++;
    }

get

这个太简单了只是验证超界然后从数组里取数据

 public E get(int index) {
        rangeCheck(index);
        return elementData(index);
    }

set

简单的判断越界,取原数据存新数据并返回原数据

public E set(int index, E element) {
        rangeCheck(index);
        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

remove

remove有两种一个根据位置 一种根据数据

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;
    }

在这里插入图片描述
根据对象删除分成两种情况一种是循环判断删除用于删除null,另外一个就是循环equals判断删除,真实的删除方法是fastRemove(int index);

public boolean remove(Object o) {
        if (o == 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; // clear to let GC do its work
    }

clear

循环清空数据,size变为0,注意容量不变

public void clear() {
        modCount++;
        for (int i = 0; i < size; i++)
            elementData[i] = null;
        size = 0;
    }

addAll

两个方法和add方法差不多只不过从添加一个数据变成了,添加多个

public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        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);  // Increments modCount

        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;
    }

contains

本质就是循环遍历数组看看元素是否存在

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

    
    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;
    }

trimToSize

public void trimToSize() {
        modCount++;
        if (size < elementData.length) {
            elementData = (size == 0)
                    ? EMPTY_ELEMENTDATA
                    //这个方法主要就是去除size后面的容量
                    : Arrays.copyOf(elementData, size);
        }
    }

到这里ArrayList算是差不多了解析了,还有一些内部类,自己的序列化方式等没解析,也不在这里讲了,有兴趣的小伙伴可以自己去看看。

知道上面一些基本就够用了

小结

  1. ArrayList基于数组方式实现,扩容上限int的最大值
  2. 添加元素时可能要扩容,删除元素时不会减少容量(若希望减少容量,trimToSize()),删除元素时,将删除掉的位置元素置为null,下次gc就会回收这些元素所占的内存空间。
  3. 线程不安全
  4. add(int index, E element):添加元素到数组中指定位置的时候,需要将该位置及其后边所有的元素都整块向后复制一位,所以效率比add低
  5. remove(Object o)需要遍历数组,效率比不需要遍历数组的remove(int index)低
  6. 每次扩容默认是1.5倍,如果还不够就是给定的长度
  7. 默认大小是10
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值