基于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的更多相关文章

  1. 浅析Java源码之ArrayList

    面试题经常会问到LinkedList与ArrayList的区别,与其背网上的废话,不如直接撸源码! 文章源码来源于JRE1.8,java.util.ArrayList 既然是浅析,就主要针对该数据结构 ...

  2. Java源码阅读ArrayList

    1简介 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAc ...

  3. Java源码之ArrayList分析

    一.ArrayList简介 ArrayList底层的数据结构是数组,数组元素类型为Object类型,即可以存放所有类型数据. 与Java中的数组相比,它的容量能动态增长.当创建一个数组的时候,就必须确 ...

  4. Java源码之ArrayList

    本文源码均来自Java 8 总体介绍 Collection接口是集合类的根接口,Java中没有提供这个接口的直接的实现类.Set和List两个类继承于它.Set中不能包含重复的元素,也没有顺序来存放. ...

  5. Java源码-集合-LinkedList

    基于JDK1.8.0_191 介绍   LinkedList是以节点来保存数据的,不像数组在创建的时候需要申请一段连续的空间,LinkedList里的数据是可以存放在不同的空间当中,然后以内存地址作为 ...

  6. jdk源码->集合->ArrayList

    类的属性 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomA ...

  7. Java源码解析——集合框架(一)——ArrayList

    ArrayList源码分析 ArrayList就是动态数组,是Array的复杂版本,它提供了动态的增加和减少元素.灵活的设置数组的大小. 一.类声明 public class ArrayList< ...

  8. Java集合源码剖析——ArrayList源码剖析

    ArrayList简介 ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存. ArrayList不是线程安全的,只能用在单线程环境下,多线 ...

  9. Java 源码学习线路————_先JDK工具包集合_再core包,也就是String、StringBuffer等_Java IO类库

    http://www.iteye.com/topic/1113732 原则网址 Java源码初接触 如果你进行过一年左右的开发,喜欢用eclipse的debug功能.好了,你现在就有阅读源码的技术基础 ...

随机推荐

  1. 解决vscode出现两个光标的问题

    转载自本人独立博客:https://liushiming.cn/2020/01/20/vscode-two-cursors-in-vim-mode/ 问题概述 今天用vscode的vim模式编辑htm ...

  2. RS422接口与RS485接口

    RS422具体接线参考网站 RS485接口 RS485设备为半双工设备,RS485收发器信号相关引脚包括控制引脚.485A.485B,其中控制引脚的高低电平决定当前处于接收模式还是发送模式. RS48 ...

  3. php 加解密函数

    PHP 加密解密函数: /** * 系统加密方法 * @param string $data 要加密的字符串 * @param string $key 加密密钥 * @param int $expir ...

  4. java 多线程并发问题

    问题:50个线程,先查询数据库的一个记录 t,然后对这个记录+1,最后更新到数据库 (更新的时候,不允许使用 update  test_concurrent set sum =sum -1 where ...

  5. Go语言基础之rand(随机数)包

    在Golang中,有两个包提供了rand,分别为 "math/rand" 和 "crypto/rand",  对应两种应用场景. "math/rand ...

  6. 【PAT甲级】1075 PAT Judge (25 分)

    题意: 输入三个正整数N,K,M(N<=10000,K<=5,M<=100000),接着输入一行K个正整数表示该题满分,接着输入M行数据,每行包括学生的ID(五位整数1~N),题号和 ...

  7. Scrapy模拟登陆

    1. 为什么需要模拟登陆? #获取cookie,能够爬取登陆后的页面 2. 回顾: requests是如何模拟登陆的? #1.直接携带cookies请求页面 #2.找接口发送post请求存储cooki ...

  8. 洛谷 P2239 螺旋矩阵(模拟 && 数学)

    嗯... 题目链接:https://www.luogu.org/problem/P2239 这道题首先不能暴力建图,没有简单方法,只有进行进行找规律. AC代码: #include<cstdio ...

  9. 棍子Sticks(poj_1011)[经典搜索]

    [题意描述] George用相同的长度棍子,将他们随机切成最多64个单位的长度,现在,他想回到原来的状态,但他忘了他原来的多少根,以及他们原本是多长.请帮助他和设计一个程序,计算最小的可能的原始长度. ...

  10. springweb 详解。

    spring web架构图 从图中可以看出, 如果要对输出的内容进行重构,不需要视图的话,在handlerMethodReturnValueHandler里进行操作,可以重构这个对象,以达到自定义输出 ...