Java 集合学习--ArrayList
一、ArrayList 定义
ArrayList 是一个用数组实现的集合,支持随机访问,元素有序且可以重复。

①、实现 List 接口
List接口继承Collection接口,是List类的顶层接口,定义了大量方法,子类可进行个性化实现

②、实现RandomAccess接口
RandomAccess 接口是一个标记接口,类似我们熟悉的Serializable接口,表明支持随机访问,在工具类Collections中有发挥其作用。

③、实现 Cloneable 接口
能否调用Object.clone() 方法的关键,如果未实现,调用clone则抛出CloneNoSupportException异常。
④、实现 Serializable 接口
这个接口没什么好说的,能都进行序列化的关键。
二、字段属性
ArrayList类的主要属性如下:
//Arraylist默认初始大小
private static final int DEFAULT_CAPACITY = 10;
//空的数组实例
private static final Object[] EMPTY_ELEMENTDATA = {};
//这也是一个空的数组实例,与上面空数组的区别在于,当第一个元素添加的时候,需要膨胀多少
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//存储 ArrayList集合的元素的数组
transient Object[] elementData;
//ArrayList集合元素个数
private int size;
三、构造函数
1.无参构造函数,构造一个空的数组。

2.有参构造函数,参数指定数组的初始大小。构造给定的初始大小的数组。

3.有参构造函数,参数是一个集合,最终是将给定的集合复制到数组中。

四、主要方法
1.添加元素
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
在添加元素之前,需要调用ensureCapacityInternal()方法来确定数组的大小,保证数组有足够的大小来容量新增的元素。如果数组已经满了,则进行数组增大,主要是借助Arrays.copyOf()方法实现的。
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);
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);
}
①、当通过 ArrayList() 构造一个空集合,初始长度是为0的,第 1 次添加元素,会创建一个长度为10的数组,并将该元素赋值到数组的第一个位置。
②、第 2 次添加元素,集合不为空,而且由于集合的长度size+1是小于数组的长度10,所以直接添加元素到数组的第二个位置,不用扩容。
③、第 11 次添加元素,此时 size+1 = 11,而数组长度是10,这时候创建一个长度为10+10*0.5 = 15 的数组(扩容1.5倍),然后将原数组元素引用拷贝到新数组。并将第 11 次添加的元素赋值到新数组下标为10的位置。
数组的最大长度为 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
2.删除元素
①、通过数组索引删除元素
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;
}
首先判断索引的值是否大于等于size,超过集合的大小则抛出IndexOutOfBoundsException异常,再通过System.arraycopy()方法对数组进行复制操作,最终形成的数组是删除指定位置后的数组,达到了移动数组中元素位置的效果。
②、直接删除指定元素
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;
}
删除指定元素的逻辑非常简单,即首先遍历数组,找到指定元素在数组中的位置后,最终的逻辑和通过索引删除元素一致。
3.查找元素
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
查找元素是根据索引查找,即通过索引获取数组中的值。
4.集合遍历
①、普通 for 循环遍历,通过循环,在借助get方法即可遍历ArrayList集合。
②、通过terator遍历
首先获取迭代器,其返回的是ArrayList类中自定义的内部类Itr。
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
③、forEach遍历ArrayList本质即通过迭代器遍历。
5.toArray,ArrayList集合转换成数组
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
6.trimToSize()
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
这个方法主要是释放内存,将ArrayList集合中数组的大小调整为size的大小。
7.subList方法
这个方法往往引起我们犯错,subList返回的并不是一个ArrayList。经常我们容易使用subList后,再使用add方法,然后报异常。具体原因源码显示的很清楚。
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
其他方法的学习省略,感兴趣的可以参考源码。
五、总结
- ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存。
- ArrayList不是线程安全的,只能用在单线程环境下,多线程环境下可以考虑用Collections.synchronizedList(List l)函数返回一个线程安全的ArrayList类,也可以使用concurrent并发包下的CopyOnWriteArrayList类。
- ArrayList的实现中大量地调用了Arrays.copyof()和System.arraycopy()方法
- ArrayList基于数组实现,可以通过下标索引直接查找到指定位置的元素,因此查找效率高,但每次插入或删除元素,就要大量地移动元素,插入删除元素的效率低。
- ArrayList中允许元素为null,且可以重复。
Java 集合学习--ArrayList的更多相关文章
- 2019/3/4 java集合学习(二)
java集合学习(二) 在学完ArrayList 和 LinkedList之后,基本已经掌握了最基本的java常用数据结构,但是为了提高程序的效率,还有很多种特点各异的数据结构等着我们去运用,类如可以 ...
- 【源码阅读】Java集合之一 - ArrayList源码深度解读
Java 源码阅读的第一步是Collection框架源码,这也是面试基础中的基础: 针对Collection的源码阅读写一个系列的文章,从ArrayList开始第一篇. ---@pdai JDK版本 ...
- Java集合学习(9):集合对比
一.HashMap与HashTable的区别 HashMap和Hashtable的比较是Java面试中的常见问题,用来考验程序员是否能够正确使用集合类以及是否可以随机应变使用多种思路解决问题.Hash ...
- 从源码看Java集合之ArrayList
Java集合之ArrayList - 吃透增删查改 从源码看初始化以及增删查改,学习ArrayList. 先来看下ArrayList定义的几个属性: private static final int ...
- 转:深入Java集合学习系列:HashSet的实现原理
0.参考文献 深入Java集合学习系列:HashSet的实现原理 1.HashSet概述: HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持.它不保证set 的迭代顺序:特 ...
- 2019/3/2周末 java集合学习(一)
Java集合学习(一) ArraysList ArraysList集合就像C++中的vector容器,它可以不考虑其容器的长度,就像一个大染缸一 样,无穷无尽的丢进去也没问题.Java的数据结构和C有 ...
- java集合学习(2):Map和HashMap
Map接口 java.util 中的集合类包含 Java 中某些最常用的类.最常用的集合类是 List 和 Map. Map 是一种键-值对(key-value)集合,Map 集合中的每一个元素都包含 ...
- Java集合学习(7):ArrayList
一.概述 ArrayList可以理解为动态数组,就是Array的复杂版本.与Java中的数组相比,它的容量能动态增长.ArrayList是List接口的可变数组的实现.实现了所有可选列表操作,并允许包 ...
- Java集合(六)--ArrayList、LinkedList和Vector对比
在前两篇博客,学习了ArrayList和LinkedList的源码,地址在这: Java集合(五)--LinkedList源码解读 Java集合(四)--基于JDK1.8的ArrayList源码解读 ...
随机推荐
- JavaScript面向对象编程之创建对象
参考资料依旧<JavaScript高级程序设计>,不得不说这本书写的太好了,讲的极为清晰凝练,好书! 先给出重点笔记,好好理解下面的三条笔记,每一句话都很重要: 1.实例的指针仅指向原型, ...
- PAT1089. Insert or Merge
PAT1089. Insert or Merge 题目大意 给定一个初始序列src, 一个排序当中的序列tar, 问排序方式是 Insert Sort, 或者 Merge Sort. 并输出下一次迭代 ...
- lucene&solr学习——创建和查询索引(理论)
1.Lucene基础 (1) 简介 Lucene是apache下的一个开放源代码的全文检索引擎工具包.提供完整的查询引擎和索引引擎:部分文本分析引擎. Lucene的目的是为软件开发人员提供一个简单易 ...
- 菜鸟笔记 -- Chapter 6.2.4 成员方法
6.2.4 成员方法 在Java中使用成员方法对应于类对象的行为,在有些地方也会将方法称之为函数,成员方法是定义在类中具有特定功能的一段独立小程序.方法格式如下: 修饰符 返回值类型 成员方法名 ( ...
- 简析--UUID
内容转载自:http://www.cnblogs.com/java-class/p/4727698.html 阅读目录 1.UUID简介 2.UUID组成 3.UUID实战演练 1.UUID 简介 U ...
- C#中rpt的数据类型和Oracle中数据类型的匹配
Oracle中number数据类型对应C#中decimal数据类型,结果是整数 Oracle中number数据类型对应C#中int32数据类型,结果是小数,保留两位小数 Oracle中中date类型数 ...
- Oracle单行函数用法
单行函数分为五种类型:字符函数.数值函数.日期函数.转换函数.通用函数. 1.字符函数: 对于输入的字符转换为需要转为的字符或数值. upper()大写 --小写字母转为大写字母 --对于表指定的字符 ...
- axios基础用法
概述: 1.axios:一个基于Promise用于浏览器和nodejs的HTTP客户端.本质是对ajax的封装. 特征: 1>从浏览器中创建XMLHttpRequest 2>从node.j ...
- POJ 1410--Intersection(判断线段和矩形相交)
Intersection Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 16322 Accepted: 4213 Des ...
- org.apache.tomcat.util.http.fileupload.FileUploadBase$FileSizeLimitExceededException: The field xxx exceeds its maximum permitted size of 1048576 bytes.
springboot 通过MultipartFile接受前端传过来的文件时是有文件大小限制的(springboot内置tomact的的文件传输默认为1MB),我们可以通过配置改变它的大小限制 首先在启 ...