Java源码-集合-ArrayList
基于JDK1.8.0_191
介绍
在Java中,对于数据的保存和使用有多种方式,主要的目的是以更少的资源消耗解决更多的问题,数组就是其中的一种,它的特点是所有的数据都保存在内存的一段连续空间中,使它能更容易的进行数据的修改和查找。
而ArrayList就是基于数组的特性,进行一系列封装而得到的一个数据的工具类,它有以下特点:
- 大小可变(数组的大小并不可变,ArrayList实际上是申请一个新的更大的数组,然后把原来数组的数据拷贝到新数组当中,以此实现扩容)
- 修改和查找的时间复杂度都为o(1)
- 允许所有元素,包括null
- ArrayList是线程不安全的(如果需要使用线程安全的,可以这么写
List list = Collections.synchronizedList(new ArrayList(...));
构造函数
ArrayList有三个构造函数
- 第一个,无参构造函数
public ArrayList();
翻译一下就是Object[] elementData = {},创建一个空数组
- 第二个,设定初始大小
public ArrayList(int initialCapacity);
创建一个initialCapacity大小的数组,建议使用并根据实际当中的情况设置初始大小,因为扩容ArrayList是一个很耗资源的事
- 第三个,创建包含指定元素的数组
public ArrayList(Collection<? extends E> c);
创建一个和传入的参数一模一样的数组。参数是实现了Collection接口的类,会通过集合的toArray方法转换为数组
部分源码解析
点击查看详细内容
//新增,放到数组末尾
public boolean add(E e) {
//判断是否需要扩容
ensureCapacityInternal(size + 1);
//插到末尾
elementData[size++] = e;
return true;
}
//在指定位置插入
public void add(int index, E element) {
//判断index大小是否合法
rangeCheckForAdd(index);
//判断是否扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
//要插入的位置之后的数据整体后移一位。耗费资源极大,所以不建议频繁插入
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//插入数据
elementData[index] = element;
//调整大小
size++;
}
//插入集合
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;
}
//清空ArrayList
public void clear() {
modCount++;
//将数组的每一位设为空
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
//扩容判断
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// 如果不是空数组,可以设置成任意大小的正数值
? 0
//如果是空数组,最小就是10,参数比10还小,是不会扩容的
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
//jdk 8新增的通过lambda表达式遍历ArrayList
public void forEach(Consumer<? super E> action);
//使用案例
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.forEach(x -> System.out.println(x));
//最后一次出现指定对象的位置,注意区分NULL
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
//移除位置上的对象
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
elementData[--size] = null;
return oldValue;
}
//删除满足过滤条件的对象,为1.8新出的方法
public boolean removeIf(Predicate<? super E> filter);
//使用案例
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
//删除比1大的
list.removeIf(x -> x > 1);
System.out.println(list.toString());
//输出结果:[1]
//对数组的每个元素,执行某个操作
public void replaceAll(UnaryOperator<E> operator);
//使用案例
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
//删除比1大的
list.replaceAll(x -> ++x);
System.out.println(list.toString());
//输出结果[2, 3, 4]
//程序对数组的每个元素都进行了+1的操作
ArrayList的查询
通过阅读源码我们发现,在源码当中需要使用到遍历整个ArrayList的时候,都是使用的传统for循环,那么可以试一试各个遍历ArrayList的方式有什么不同
1.传统for循环
List<String> list = new ArrayList<>();
for(int i = 0; i < 10000000; i++){
list.add(i + "");
}
long time1 = System.currentTimeMillis();
for(int i = 0; i < list.size(); i++){}
long time2 = System.currentTimeMillis();
System.out.println(time2 - time1);
输出的结果为5ms
2.简便for循环
把循环方式改为
for(String str : list){}
输出的结果为40ms
3.Iterator
把循环方式改为
Iterator<String> iter = list.iterator();
while(iter.hasNext()){
iter.next();
}
输出的结果为7ms
4.foreach
list.forEach(x -> {});
使用jdk8新出的foreach
输出的结果为77ms
这四种方式差距的还是有些明显的,那么来分析下原因
首先第二种的简便for循环,其实内部使用的还是Iterator方式,我们把第二种代码进行编译,然后查看.class文件可以发现,for(String str : list){}编译后就变成了
String var5;
for(Iterator var4 = list.iterator(); var4.hasNext(); var5 = (String)var4.next()) {}
然后我们看第四种,第四种方式的源码为:
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
我们可以看出来,其实它内部嵌套的是第二种循环方式,所以耗时更久
总结:使用第一种最好,虽然代码长了一点,但是效率会提高很多
Java源码-集合-ArrayList的更多相关文章
- 浅析Java源码之ArrayList
面试题经常会问到LinkedList与ArrayList的区别,与其背网上的废话,不如直接撸源码! 文章源码来源于JRE1.8,java.util.ArrayList 既然是浅析,就主要针对该数据结构 ...
- Java源码阅读ArrayList
1简介 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAc ...
- Java源码之ArrayList分析
一.ArrayList简介 ArrayList底层的数据结构是数组,数组元素类型为Object类型,即可以存放所有类型数据. 与Java中的数组相比,它的容量能动态增长.当创建一个数组的时候,就必须确 ...
- Java源码之ArrayList
本文源码均来自Java 8 总体介绍 Collection接口是集合类的根接口,Java中没有提供这个接口的直接的实现类.Set和List两个类继承于它.Set中不能包含重复的元素,也没有顺序来存放. ...
- Java源码-集合-LinkedList
基于JDK1.8.0_191 介绍 LinkedList是以节点来保存数据的,不像数组在创建的时候需要申请一段连续的空间,LinkedList里的数据是可以存放在不同的空间当中,然后以内存地址作为 ...
- jdk源码->集合->ArrayList
类的属性 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomA ...
- Java源码解析——集合框架(一)——ArrayList
ArrayList源码分析 ArrayList就是动态数组,是Array的复杂版本,它提供了动态的增加和减少元素.灵活的设置数组的大小. 一.类声明 public class ArrayList< ...
- Java集合源码剖析——ArrayList源码剖析
ArrayList简介 ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存. ArrayList不是线程安全的,只能用在单线程环境下,多线 ...
- Java 源码学习线路————_先JDK工具包集合_再core包,也就是String、StringBuffer等_Java IO类库
http://www.iteye.com/topic/1113732 原则网址 Java源码初接触 如果你进行过一年左右的开发,喜欢用eclipse的debug功能.好了,你现在就有阅读源码的技术基础 ...
随机推荐
- linux下windows访问samba服务器
今天学习windows如何访问linux的共享文件. 开通samba服务,首先需要在linux下安装samba程序包,又再次使用光盘镜像挂载: [root@localhost home]# mount ...
- oracle 11g快捷版的安装与使用
oracle 11g快捷版的安装与使用 [oracle 11g 下载地址(]https://www.oracle.com/technetwork/cn/database/enterprise-edit ...
- 吴裕雄 python 机器学习——模型选择验证曲线validation_curve模型
import numpy as np import matplotlib.pyplot as plt from sklearn.svm import LinearSVC from sklearn.da ...
- Abaqus中的单位制
量纲 SI SI/mm US/ft US/inct 长度 m mm ft in 载荷 N N lbf lbf 质量 kg kg3 slug lbfs2/in 时间 s s s s 量纲 SI SI/m ...
- 【PAT甲级】1099 Build A Binary Search Tree (30 分)
题意: 输入一个正整数N(<=100),接着输入N行每行包括0~N-1结点的左右子结点,接着输入一行N个数表示数的结点值.输出这颗二叉排序树的层次遍历. AAAAAccepted code: # ...
- ASP.NET Core搭建多层网站架构【10-使用JWT进行授权验证】
2020/01/31, ASP.NET Core 3.1, VS2019, Microsoft.AspNetCore.Authentication.JwtBearer 3.1.1 摘要:基于ASP.N ...
- 创业学习---《如何预判创业可行性》--B-1.预判模块---HHR计划---以太一堂
<如何预判创业可行性>----对创业进行占卜 一,<开始学习> 1,预热思考题: (1)预判一个模式的可行性.你有一个朋友要创业,给你讲了他的创业计划,你帮他判断一下是否靠谱. ...
- numpy中的max()函数
1.ndarray.max([int axis]) 函数功能:求ndarray中指定维度的最大值,默认求所有值的最大值. axis=0:求各column的最大值 axis=1:求各row的最大值
- CentOS7安装jenkis
注意:终止运行Ctrl+c , 退回到shell命令Ctrl+d 一.先检查是否有java [root@huangyh huangyh]# rpm -qa |grep java 或 java 因为C ...
- Codeforces Round #611 (Div. 3) C
There are nn friends who want to give gifts for the New Year to each other. Each friend should give ...