IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> java集合详解之ArrayList -> 正文阅读

[游戏开发]java集合详解之ArrayList

1 特点

  • 有序的,可以存储重复值和null值。
  • 底层是数组实现的,线程不安全。通过下标查询、速度快,非末尾增删的时候需要重新移动数据,所以增删性能低。
  • 调用构造函数new ArrayList()时,最开始是一个空数组,在第一次add的时候会创建一个初始容量为10的数组。也可以自定义初始化容量new ArrayList(int initialCapacity)。
  • 以自身的1.5倍容量扩容,不可以设置容量增量。

2 源码分析

image-20201206112834609

2.1 接口的定义和属性

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    private static final long serialVersionUID = 8683452581122892189L;
    /**
     * 默认的初始容量,在不指定minCapacity变量的时候,它将使用DEFAULT_CAPACITY 作为数组的大小。但是需要注意的是它是在第一次添加元素的时候在使用的,而不是像指定了minCapacity在构造方法中那样在创建ArrayList 的过程中就创建了。
     */
    private static final int DEFAULT_CAPACITY = 10;
    /**
     * 指定参数初始容量,但是初始容量是0的时候
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};
    /**
     * 初始容量是0的数组。无参构造时候,elementData=DEFAULTCAPACITY_EMPTY_ELEMENTDATA 其实主要是为了区分elementData=它是无参构造的赋值,当有参构造方法但是参数是0的时候的赋值
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    /**
     * 实际存储元素的数组
     */
    transient Object[] elementData;
    /**
     * 实际存储的元素个数,而不是数组的大小
     */
    private int size;
}

2.2 构造方法

    //ArrayList构造方法
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    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(Collection<? extends E> c) {
        Object[] a = c.toArray();
        if ((size = a.length) != 0) {
            if (c.getClass() == ArrayList.class) {
                elementData = a;
            } else {
                elementData = Arrays.copyOf(a, size, Object[].class);
            }
        } else {
            // replace with empty array.
            elementData = EMPTY_ELEMENTDATA;
        }
    }
  • 当调用new ArrayList<>()时,将一个空数组 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 赋值给了elementData,这个时候集合的长度size为默认长度0,在第一次添加的时候就会把容量变为10;
  • 当调用new ArrayList<>(100)时,根据传入的长度,new一个Object[100]赋值给elementData,当然如果玩儿的话,传了一个0,那么将一个空数组 EMPTY_ELEMENTDATA 赋值给了elementData;
  • 当调用new ArrayList<>(new HashSet())时,可以传递任何实现了Collection接口的类,将传递的集合调用toArray()方法转为数组内赋值给elementData。

2.3?add(E element)

    //添加一个元素到列表的末尾
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);//在添加元素之前要保证数组大小可以容纳该元素 
        elementData[size++] = e;
        return true;
    }

    //如果是首次添加元素的话,szie=0 则 minCapacity=1
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    //计算容量大小
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        //如果无参数构造的话,elementData=DEFAULTCAPACITY_EMPTY_ELEMENTDATA,在第一次添加元素的时候将直接扩容到DEFAULT_CAPACITY,直接返回DEFAULT_CAPACITY默认容量10
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        //否则的话则是有参构造或者不是第一次添加元素,那么这里返回的就是size+1,也就是说扩容到所需的szie+1 即可
        return 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);
        //如果newCapacity 还是不足的话,直接使用minCapacity作为最小容量
        //当无参构造方法创建时第一次调用grow方法minCapacity=10,而newCapacity=0,所以无参构造方法默认的初始容量为10
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        //创建一个容量为newCapacity的数组,并把数组elementData元素复制到新的数组里
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    //容量最大值
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    //当超出最大值进行处理
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

2.4?add(int index, E element)

    //插入指定元素到指定位置,如果当前待插入的位置有元素,则需要右移当前元素和其后的元素
    public void add(int index, E element) {
        // 检查位置的合法性        
        rangeCheckForAdd(index);
        //跟add里面的一样,保证数组容量足够
        ensureCapacityInternal(size + 1);
        // 移动当前位置和其后置的元素
        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));
    }

2.5?set(int index, E element)

    public E set(int index, E element) {
        //数组下标是否越界
        rangeCheck(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

    //数组下标是否越界
    private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

2.6?get(int index)

    public E get(int index) {
        //数组下标是否越界
        rangeCheck(index);

        return elementData(index);
    }

2.7?remove(int index) 和 remove(Object o)

    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);
        //通过设置null值让GC起作用
        elementData[--size] = null;

        return oldValue;
    }


    //删除ArrayList中的值对象,其实和通过下标删除很相似
    //只是多了一个步骤,遍历底层数组elementData,通过equals()方法或 == (特殊情况下)来找到要删除的元素,获取其下标
    //最后调用remove(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;
    }

ArrayList 还提供了另外一个方法,就是removeIf(),它是一个函数式接口

List<String> cityList = new ArrayList<>(2);
cityList.removeIf((String name )->name.equalsIgnoreCase("Bangalore"));?

2.8?iterator()

//移除失败的场景
@Test
public void itratorTest(){
    List<String> cityList = new ArrayList<>(4);
    cityList.add("重庆");
    cityList.add("北京");
    cityList.add("上海");
    cityList.add("天津");
    Iterator<String> itr = cityList.iterator();
    while(itr.hasNext()){
        String city = itr.next();
        if(city.equals("北京")){
            cityList.remove(city);
        }
        System.out.println(city);
    }
}

输出结果
重庆
北京
java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)


//移除成功的场景
@Test
public void itratorTest2(){
    List<String> cityList = new ArrayList<>(4);
    cityList.add("重庆");
    cityList.add("北京");
    cityList.add("上海");
    cityList.add("天津");
    Iterator<String> itr = cityList.iterator();
    while(itr.hasNext()){
        String city = itr.next();
        if(city.equals("北京")){
            itr.remove();
        }
        System.out.println(city);
    }
}

2.9?其他方法

int size() : 获取集合长度,通过定义在ArrayList中的私有变量size得到

boolean isEmpty():是否为空,通过定义在ArrayList中的私有变量size得到

boolean contains(Object o):是否包含某个元素,通过遍历底层数组elementData,通过equals或==进行判断

int lastIndexOf(Object o):返回最后一个元素为o的下标

Object[] toArray():转换成一个新得数组返回

void clear():集合清空,通过遍历底层数组elementData,设置为null

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-04-24 09:46:08  更:2022-04-24 09:46:18 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 13:38:42-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码