ArrayList的详解
来源:互联网 发布:英雄联盟视频软件 编辑:程序博客网 时间:2024/06/08 15:00
前言:ArrayList是java常见的集合,相信大家已经在很多场合使用过,他的优点是查询速度快,插入删除速度慢,这里我们一起来详细看一下ArrayList的设计。
1 arrayList类关系
查看arrayList的类关系,就可以知道他继承了 AbstractList, AbstractList是一个抽象类,他继承了 AbstractCollection,并且实现了List
2 那么我们从入口开始说
2.1 这里我们new了一个ArrayList的对象,点进去查看他的构造方法可以发现
List<String> list = new ArrayList<>();
/** * Constructs an empty list with an initial capacity of ten. */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
他把一个空的对象数组赋给了当前数组,所以这个构造函数的作用就是产生了一个空的对象数组,这里就可以得知ArrayList是底层为数组的集合,他也具有数组的所有特性。
2.2 我们先从第一个add方法开始看,add方法是List接口就定义的,这里ArrayList做了实现。
/** * Appends the specified element to the end of this list. * * @param e element to be appended to this list * @return <tt>true</tt> (as specified by {@link Collection#add}) */ public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! 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++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
通过上图可以发现,SIZE是数组的元素的个数,通过构造方法我们可以发现该数组初始化长度是空的,放置不了对象,DEFAULT_CAPAITY是里面定义的一个初始化长度数组,为10,minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);通过这行代码可以发现,当前数组长度小于10的时候取10,大于10的时候取minCapacity,minCapacity也就是指当前数组长度+放入的元素个数,minCapacity - elementData.length > 0时,进行数组的扩容。
/** * Increases the capacity to ensure that it can hold at least the * number of elements specified by the minimum capacity argument. * * @param minCapacity the desired minimum capacity */ private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); 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); }
上面的方法主要是取最新的数组长度,分为三步
1 将当前的数组长度往右移1位,加上之前的长度得到最新数组长度
2 如果得到的数组长度仍然放不上要添加的元素,则取最小应该的长度做为最新长度
3 如果数组长度达到最长,则
private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
,然后调用Arrays.copyOf进行扩容,原理就是复制当前数组到新的数组中。
最后将元素放到数组中
以上就是将元素放入arrayList所做的动作。
2.3 我们来看一下indexOf的 api
/** * Returns the index of the first occurrence of the specified element * in this list, or -1 if this list does not contain the element. * More formally, returns the lowest index <tt>i</tt> such that * <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>, * or -1 if there is no such index. */ 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; }上诉的方法就很简单了 如果参数是null则 循环==null来判断,如果不为null 则调用equals的方法的比较,具体为啥这么区分大家可以自行百度一下equals的方法,
2.4 接下来我们看一下get()方法,大家都知道里面参数的 集合的下标
public E get(int index) { rangeCheck(index); return elementData(index); }
/** * Checks if the given index is in range. If not, throws an appropriate * runtime exception. This method does *not* check if the index is * negative: It is always used immediately prior to an array access, * which throws an ArrayIndexOutOfBoundsException if index is negative. */ private void rangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }大家可以看到 在从数组中拿元素的时候进行了一次安全检查 调用了rangeCheck的方法,如果超过了数组元素的长度,就报索引越界的错误,这就是为啥我们程序中经常遇到的错误了.,检查完毕,再从数组中拿数据
2.5 我门接着看remove的方法
/** * Removes the element at the specified position in this list. * Shifts any subsequent elements to the left (subtracts one from their * indices). * * @param index the index of the element to be removed * @return the element that was removed from the list * @throws IndexOutOfBoundsException {@inheritDoc} */ 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; }上诉方法 ,我们依然检查了索引越界的问题,接下来调用
E oldValue = elementData(index);
获取要删除的元素,int numMoved = size - index - 1;这里主要是计算一下要删除oldValue 这个元素的后面所有元素的个数是多少,比如 现在一个有0 1 2 3 4 一个5个元素,现在要删除2 元素,那么计算出来2后面有3,4一共俩个元素,目地是为了移动3 4 的下标,这也就是说明了为啥ArrayList的插入删除效率慢,因为他要维护后面所有元素的索引。
将后面的元素向前移动一位,将要删除的元素放到数组最后一位,并且将数组长度减少一位,并且赋值为null,让GC收集器去删除
2.6 其他的方法大家可以去看下ArrayList了 比较简单了。
但是ArrayList的设计为线程不安全的,这里引用别的博客的表述
概要介绍
首先说一下什么是线程不安全:线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。 如图,List接口下面有两个实现,一个是ArrayList,另外一个是vector。 从源码的角度来看,因为Vector的方法前加了,synchronized 关键字,也就是同步的意思,sun公司希望Vector是线程安全的,而希望arraylist是高效的,缺点就是另外的优点。 说下原理(百度的,很好理解): 一个 ArrayList ,在添加一个元素的时候,它可能会有两步来完成:
1. 在 Items[Size] 的位置存放此元素;
2. 增大 Size 的值。
在单线程运行的情况下,如果 Size = 0,添加一个元素后,此元素在位置 0,而且 Size=1;
而如果是在多线程情况下,比如有两个线程,线程 A 先将元素存放在位置 0。但是此时 CPU 调度线程A暂停,线程 B 得到运行的机会。线程B也向此 ArrayList 添加元素,因为此时 Size 仍然等于 0 (注意哦,我们假设的是添加一个元素是要两个步骤哦,而线程A仅仅完成了步骤1),所以线程B也将元素存放在位置0。然后线程A和线程B都继续运行,都增加 Size 的值。
那好,现在我们来看看 ArrayList 的情况,元素实际上只有一个,存放在位置 0,而 Size 却等于 2。这就是“线程不安全”了。
示例程序:细心的朋友会发现transient Object[] elementData;数组是被transient修饰的,目地是为了当数组长度为10时 元素为5时 进行序列化的时候只会序列化数组为5的对象,所以这里反序列化的时候是不安全的,没有意义的,所以声明为不参与序列化
阅读全文
0 0
- ArrayList的详解
- ArrayList详解
- ArrayList详解
- ArrayList详解
- ArrayList详解
- ArrayList详解
- ArrayList详解
- ArrayList详解
- ArrayList详解
- arraylist详解
- ArrayList详解
- 详解ArrayList的remove的方法
- Java ArrayList的实现原理详解
- Java 集合ArrayList与Vector的详解
- ArrayList的基本工作原理详解add
- ArrayList的基本工作原理详解remove
- Java ArrayList的实现原理详解
- LinkedList、ArrayList、 Vector的区别和详解
- linux 挂载tf卡
- redisson总结
- 在Linux中安装g++/gdb/vim配置
- eclipse中如何去除警告:Class is a raw type. References to generic type Class<T> should be parameterized
- Vim nerdcommenter 插件
- ArrayList的详解
- Java基础总结
- [ubuntu入门手册]
- linux Shell ----mkdir和touch命令详解
- 结构体
- vs2015安装中文语言包后无法检测到中文语言包
- greenDAO 官方替换数据库框架ObjectBox 学习 写记录1
- 周鸿祎与小蓝单车有何仇怨,称:这个CEO一直有人品问题
- 闲来麻将源码下载