集合家族——ArrayList
一、概述:
ArrayList 是实现 List 接口的动态数组,所谓动态就是它的大小是可变的。实现了所有可选列表操作,并允许包括 null 在内的所有元素。除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。
二、源码分析:
底层是Object类型数组,增删很慢,查询很快。不支持存贮基本数据类型
2.1底层使用数组
private transient Object[] elementData;
transient为 Java 关键字,为变量修饰符,如果用 transient 声明一个实例变量,当对象存储时,它的值不需要维持。Java 的 serialization 提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用 serialization 机制来保存它。为了在一个特定对象的一个域上关闭 serialization,可以在这个域前加上关键字 transient。当一个对象被序列化的时候,transient 型变量的值不包括在序列化的表示中,然而非 transient 型的变量是被包括进去的。这里 Object[] elementData,就是我们的 ArrayList 容器。
2.2构造函数
ArrayList 提供了三个构造函数:
- ArrayList():默认构造函数,提供初始容量为 10 的空列表。
- ArrayList(int initialCapacity):构造一个具有指定初始容量的空列表。
- ArrayList(Collection<? extends E> c):构造一个包含指定 collection 的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的。
/**
* 构造一个初始容量为 10 的空列表
*/
public ArrayList() {
this(10);
}
/**
* 构造一个具有指定初始容量的空列表。
*/
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "
+ initialCapacity);
this.elementData = new Object [initialCapacity];
}
/**
* 构造一个包含指定 collection 的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的。
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
2.3扩容原理
ensureCapacity():该方法就是 ArrayList 的扩容方法。 ArrayList 每次新增元素时都会需要进行容量检测判断,若新增元素后元素的个数会超过 ArrayList 的容量,就会进行扩容操作来满足新增元素的需求。所以当我们清楚知道业务数据量或者需要插入大量元素前,我可以使用 ensureCapacity 来手动增加 ArrayList 实例的容量,以减少递增式再分配的数量。
public void ensureCapacity(int minCapacity) {
//修改计时器
modCount++;
//ArrayList容量大小
int oldCapacity = elementData.length;
/*
* 若当前需要的长度大于当前数组的长度时,进行扩容操 作
*/
if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
//计算新的容量大小,为当前容量的1.5倍
int newCapacity = (oldCapacity * 3) / 2 + 1;
if (newCapacity < minCapacity)
newCapacity = minCapacity;
//数组拷贝,生成新的数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
通过 google 查找,发现 1.5 倍的扩容是最好的倍数。因为一次性扩容太大(例如 2.5 倍)可能会浪费更多的内存(1.5 倍最多浪费 33%,而 2.5 被最多会浪费 60%,3.5 倍则会浪费 71%……)。但是一次性扩容太小,需要多次对数组重新分配内存,对性能消耗比较严重。所以 1.5 倍刚刚好,既能满足性能需求,也不会造成很大的内存消耗。
除了这个 ensureCapacity() 这个扩容数组外,ArrayList 还给我们提供了将底层数组的容量调整为当前列表保存的实际元素的大小的功能。它可以通过 trimToSize() 方法来实现。该方法可以最小化 ArrayList 实例的存储量
public void trimToSize() {
modCount++;
int oldCapacity = elementData.length;
if (size < oldCapacity) {
elementData = Arrays.copyOf(elementData, size);
}
}
2.4增加
ArrayList 提供了 add(E e)、add(int index, E element)、addAll(Collection<? extends E> c)、addAll(int index, Collection<? extends E> c)这个几个方法来实现 ArrayList 增加。
2.4.1 add(E e):将指定元素添加到列表尾部
public boolean add(E e) {
ensureCapacity(size + 1); // Increments modCount!!
elementData[size++] = e; //将列表末尾元素指向e
return true;
}
2.4.2add(int index, E element):将指定的元素插入此列表中的指定位置
public void add(int index, E element) {
//判断索引位置是否正确
if (index > size || index < 0)
throw new IndexOutOfBoundsException(
"Index: "+index+", Size: "+size);
//扩容检测
ensureCapacity(size+1);
/*
* 对源数组进行复制处理(位移),从index + 1到size-index。
* 主要目的就是空出index位置供数据插入,
* 即向右移动当前位于该位置的元素以及所有后续元素。
*/
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//在指定位置赋值
elementData[index] = element;
size++;
}
2.4.3 addAll(Collection<? extends E> c):按照指定 collection 的迭代器所返回的元素顺序,将该 collection 中的所有元素添加到此列表的尾部
public boolean addAll(Collection<? extends E> c) {
// 将集合C转换成数组
Object[] a = c.toArray();
int numNew = a.length;
// 扩容处理,大小为size + numNew
ensureCapacity(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
2.4.4 addAll(int index, Collection<? extends E> c):从指定的位置开始,将指定 collection 中的所有元素插入到此列表中
public boolean addAll(int index, Collection<? extends E> c) {
//判断位置是否正确
if (index > size || index < 0)
throw new IndexOutOfBoundsException("Index: " + index + ", Size: "
+ size);
//转换成数组
Object[] a = c.toArray();
int numNew = a.length;
//ArrayList容器扩容处理
ensureCapacity(size + numNew); // Increments modCount
//ArrayList容器数组向右移动的位置
int numMoved = size - index;
//如果移动位置大于0,则将ArrayList容器的数据向右移动numMoved个位置,确保增加的数据能够增加
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
//添加数组
System.arraycopy(a, 0, elementData, index, numNew);
//容器容量变大
size += numNew;
return numNew != 0;
}
以上多次出现了多次arraycopy()这个方法,在这里简单介绍下:
方法原型是:
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
它的根本目的就是进行数组元素的复制。即从指定原数组中复制一个数组,复制从指定的位置开始,到目标数组的指定位置结束。将原数组 src从srcPos 位置开始复制到 dest 数组中,复制长度为 length,数据从 dest 的 destPos 位置开始粘贴
2.5修改
set(int index, E element) :用指定的元素替代此列表中指定位置上的元素(其实也算是添加)
public E set(int index, E element) {
//检测插入的位置是否越界
RangeCheck(index);
E oldValue = (E) elementData[index];
//替代
elementData[index] = element;
return oldValue;
}
2.6删除
ArrayList 提供了 remove(int index)、remove(Object o)、removeRange(int fromIndex, int toIndex)、removeAll() 四个方法进行元素的删除。
2.6.1 remove(int index) :移除此列表中指定位置上的元素
public E remove(int index) {
//位置验证
RangeCheck(index);
modCount++;
//需要删除的元素
E oldValue = (E) elementData[index];
//向左移的位数
int numMoved = size - index - 1;
//若需要移动,则想左移动numMoved位
if (numMoved > 0)
System.arraycopy(elementData, index + 1, elementData, index,
numMoved);
//置空最后一个元素
elementData[--size] = null; // Let gc do its work
return oldValue;
}
2.6.2 remove(Object o):移除此列表中首次出现的指定元素(如果存在)
public boolean remove(Object o) {
//因为ArrayList中允许存在null,所以需要进行null判断
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;
}
其中fastRemove()用于删除指定位置的元素
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; // Let gc do its work
}
2.6.3 removeRange(int fromIndex, int toIndex):移除列表中索引在 fromIndex(包括)和 toIndex(不包括)之间的所有元素
protected void removeRange(int fromIndex, int toIndex) {
modCount++;
int numMoved = size - toIndex;
System .arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved);
// Let gc do its work
int newSize = size - (toIndex - fromIndex);
while (size != newSize)
elementData[--size] = null;
}
2.6.4 removeAll():是继承自 AbstractCollection 的方法,ArrayList 本身并没有提供实现
2.7 查找
ArrayList 提供了 get(int index) 用读取 ArrayList 中的元素。由于 ArrayList 是动态数组,所以我们完全可以根据下标来获取 ArrayList 中的元素,而且速度还比较快,故 ArrayList 长于随机访问。
public E get(int index) {
RangeCheck(index);
return (E) elementData[index];
}
集合家族——ArrayList的更多相关文章
- Collection集合家族
集合家族 数组:存储相同类型的多个元素 对象:存储不同类型的多个元素 集合:存储多个不同类型的对象 List List继承自Collection接口,是有序可重复的集合. 它的实现类有:ArrayLi ...
- C#语言基础——集合(ArrayList集合)
集合及特殊集合 集合的基本信息: System.Collections 命名空间包含接口和类,这些接口和类定义各种对象(如列表.队列.位数组.哈希表和字典)的集合.System.Collections ...
- C#集合之ArrayList
C#中之所以有集合这个东东,是因为数组的长度是固定的,而实际需求是,不确定未来这个“数组”的个数,故出现集合这个概念,因为集合的容量会随元素的增加曾倍数增长.C#中有2类常用集合:ArrayList, ...
- Java基础知识强化之集合框架笔记64:Map集合之ArrayList嵌套HashMap
1. ArrayList集合嵌套HashMap集合并遍历. 需求: 假设ArrayList集合的元素是HashMap.有3个. 每一个HashMap集合的键和值都是字 ...
- C#重的数组、集合(ArrayList)、泛型集合(list<T>)三者比较及扩展延伸……
本来我只想总结下数组.集合(ArrayList).泛型集合(list<T>)三者的比较的,可以一写下来要扩展的知识点有点多了,只能写一个小的知识点列表了如下: 1.数组.集合(ArrayL ...
- 集合框架-ArrayList,Vector,Linkedlist
// ClassCastException 报错,注意,千万要搞清楚类型 * Vector的特有功能: * 1:添加功能 * public void addElement(Object obj) -- ...
- 转:C#常用的集合类型(ArrayList类、Stack类、Queue类、Hashtable类、Sort)
C#常用的集合类型(ArrayList类.Stack类.Queue类.Hashtable类.Sort) .ArrayList类 ArrayList类主要用于对一个数组中的元素进行各种处理.在Array ...
- C#基础课程之四集合(ArrayList、List<泛型>)
list泛型的使用 ArrayList list = new ArrayList(); ArrayList list = ); //可变数组 list.Add("我"); //Ad ...
- Java基础-集合框架-ArrayList源码分析
一.JDK中ArrayList是如何实现的 1.先看下ArrayList从上而下的层次图: 说明: 从图中可以看出,ArrayList只是最下层的实现类,集合的规则和扩展都是AbstractList. ...
随机推荐
- php 获取城市ip
/** * 获取ip城市信息 * CreateBy XueSong * @param string $ip * @return array|bool|mixed */ function getCity ...
- express相关操作
1. 安装应用生成器npm install express-generator –g 2. 生成项目Express --view=ejs myapp 3. 进入项目安装依赖包 npm in ...
- 微信小程序上传图片更新图像
解决思路: 1. 调用wx.chooseImage 选择图片 2.wx.uploadFile 上传图片 3.调用后台接口进行修改操作 修改原来的头像 wx.chooseImage({ success: ...
- WPF实战案例-MVVM模式下用附加属性在Xaml中弹出窗体
嗯..最近回家去了,2个月没写过代码了,面试只能吹牛,基础都忘了,今天回顾一下,分享一篇通过附加属性去处理窗体弹出的情况. 或许老司机已经想到了,通过设置附加属性值,值变更的回调函数去处理窗体弹出,是 ...
- 手写走通HTTP server 第三版本
后台 后端 服务端 功能:逻辑处理 算法处理 磁盘交互(数据库 静态文件处理) 要求:健壮性,安全性 并发性能和处理速度 架构合理便于维护扩展 网站后端 httpserver + WebFr ...
- list 列表 函数的引用
方法 意义 L.index(v [, begin[, end]]) 返回对应元素的索引下标, begin为开始索引,end为结束索引,当 value 不存在时触发ValueError错误 L.inse ...
- JavaScript 的基本使用
JavaScript 基本语法要求: 1.JS的写法是严格区分大小写的. 2.标识符的起名要求跟java的是一样的,第一个位置可以说字母.下划线.美元符号.其他位置可以字母.下划线.美元符号.数字. ...
- 配置了ssh免密登录,仍然需要输入密码
1 权限问题 原因 权限问题 chmod -R 700 ~/.ssh
- Mac&Appium&Python自动化测试-Appium安装
基础配置 1.JAVA和Git就不用多说了 2.Brew,也就是homebrew,它是MacOSX上的软件包管理工具,它就等同于linux上的apt-get.yum,如果没有安装,可以通过如下命令安装 ...
- Base64编码与解码的作用
1.便于网络传输. 2.不可见性. 3.适合在http,mime协议下快速传输数据. 4 区别于加密,Base64是可以解码的,编码是 公开的,Base 64编码,任何人都可以解码