ArrayList介绍

ArrayList是一个数组列表。与Java数组相比,ArrayList相当于一个动态数组。它继承于AbstractList,实现了List, RandomAccess, Cloneable, Serializable接口。

ArrayList中的成员变量

  • 默认数组容量
private static final int DEFAULT_CAPACITY = 10;

  • 用于空实例的共享空数组实例
private static final Object[] EMPTY_ELEMENTDATA = {};

  • 共享的空数组实例,用于默认大小的空实例
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

  • 元素数据
transient Object[] elementData;

  • ArrayList元素数量
private int size;

ArrayList中的构造方法

ArrayList中共有3个构造方法

  • 空参数构造方法

使用空参数构造方法可创建一个Object类型的数组

源码如下:

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

  • int类型为参数可指定数组容量的构造方法

使用int类型为参数的构造方法可以实例化一个指定长度的Object类型的数组

源码如下:

public ArrayList(int initialCapacity) {if (initialCapacity > 0) {this.elementData = new Object[initialCapacity];} else if (initialCapacity == 0) {this.elementData = EMPTY_ELEMENTDATA;} else {throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);}
}

  • Collection类型为参数的构造方法

使用Coolection类型为参数的构造方法可以将一个Collection类型的对象转换为ArrayList,比如:new ArrayList<>(new HashSet<>());

源码如下:

public ArrayList(Collection<? extends E> c) {elementData = c.toArray();if ((size = elementData.length) != 0) {// c.toArray might (incorrectly) not return Object[] (see 6260652)if (elementData.getClass() != Object[].class)elementData = Arrays.copyOf(elementData, size, Object[].class);} else {// replace with empty array.this.elementData = EMPTY_ELEMENTDATA;}
}

ArrayList的扩容机制

说到扩容机制就得先看看ArrayList中添加元素的方法。ArrayList中共有两个添加元素的方法。

  • 在末尾追加元素的添加方法

源码如下:

public boolean add(E e) {ensureCapacityInternal(size + 1);  // Increments modCount!!elementData[size++] = e;return true;
}

  • 可指定元素索引位置的添加方法

源码如下:

public void add(int index, E element) {rangeCheckForAdd(index);ensureCapacityInternal(size + 1);  // Increments modCount!!System.arraycopy(elementData, index, elementData, index + 1,size - index);elementData[index] = element;size++;
}

当添加元素时,add()方法会检查是否需要扩容,如果需要扩容则调用grow()方法进行扩容。

检查是否需要扩容的大致思路如下:

调用calculateCapacity()方法判断目标对象是否为空列表(DEFAULTCAPACITY_EMPTY_ELEMENTDATA),如果是空列表则返回默认表长度与size+1的最大值。如果不是空列表则返回size+1,然后调用ensureExplicitCapacity()方法,比较calculateCapacity()方法的返回值与ArrayList的元素个数,如果返回值大于ArrayList的表长时对ArrayList进行扩容。

源码如下:

private static int calculateCapacity(Object[] elementData, int minCapacity) {if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {return Math.max(DEFAULT_CAPACITY, minCapacity);}return minCapacity;
}private void ensureExplicitCapacity(int minCapacity) {modCount++;if (minCapacity - elementData.length > 0)grow(minCapacity);
}

ArrayList扩容大致思路如下:

  1. 将原容量值赋值给临时变量oldCapacity
  2. 新的容量值(newCapacity)设置为原来的容量加原来容量的一半
  3. 比较newCapacity与size+1,如果newCapacity<size+1则将size+1赋值给newCapacity
  4. 比较newCapacity与MAX_ARRAY_SIZE的值,如果newCapacity>MAXARRAY_SIZE,调用hugeCapacity,比较size+1与MAX_ARRAY_SIZE,如果size+1>MAX_ARRAY_SIZE则返回Integer.MAX_VALUE否则返回MAX_ARRAY_SIZE
  5. 调用Arrays.copyOf()方法将原数组复制到扩容后的数组(ArrayList底层为Object数组)

源码如下:

private void grow(int minCapacity) {// overflow-conscious codeint 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);
}private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflowthrow new OutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;
}

ArrayList的常用方法

  • add():添加元素
  • remove():异常元素
  • size():返回列表中的元素个数
  • isEmpty():判断列表是否为空
  • contains():判断列表中是否包含某元素
  • indexOf():返回元素的索引位置
  • lastIndexOf():返回元素最后一次出现的索引位置
  • toArray():将列表转换为Object数组
  • get():获取某一索引对应则元素
  • set():设置元素
  • clear():将列表清空
  • subList():获取子列表
  • addAll():将Collection中的元素全部添加至列表

ArrayList在遍历中删除元素

  • 使用for遍历删除,例如:
public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("ArrayList");list.add("LinkedList");list.add("Vector");list.add("CopyOnWriteArrayList");for (String str : list) {if (str.equals("ArrayList")) {list.remove(str);}}System.out.println(Arrays.toString(list.toArray()));
}

当使用此方式进行遍历删除时抛出异常,运行结果如下:

根据反编译结果可知,foreach循环在实际执行时其实使用的时Iterator,使用的核心方法时hasNext()和next()。反编译结果(使用XJad进行反编译)如图:

由ArrayList的Iterator实现可知,next()方法执行时会先调用checkForComodification()比较modCount与expectedModCount这两个变量的值,当这两个变量的值不相等时抛出ConcurrentModificationException。

在上面的示例中,开始遍历时modCount和expectedModCount的值都是4,当执行remove()方法后,第二次获取元素时modCount变为5,此时modCount与ExpectedModCount不相等,抛出异常。

next()方法源代码如下:

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];
}

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; // clear to let GC do its work
}

List中边遍历边删除的三种方式

  • 使用Iterator的remove()方法
  • for循环正序遍历
  • for循环倒序遍历
  1. 使用Iterator的remove()方法的实现如下:
public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("ArrayList");list.add("LinkedList");list.add("Vector");list.add("CopyOnWriteArrayList");Iterator<String> iterator = list.iterator();while (iterator.hasNext()) {String str = iterator.next();if (str.equals("ArrayList")){iterator.remove();}}System.out.println(Arrays.toString(list.toArray()));
}

输出结果为:

为什么Iterator的remove()方法可以成功呢,是因为Iterator的remove()方法执行时将modCount的值赋值给了expectedModCount使得modCount与expectedModCount的值相等

源代码如下:

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();}
}

2. for循环正序遍历

实现代码如下:

public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("ArrayList");list.add("LinkedList");list.add("Vector");list.add("CopyOnWriteArrayList");for (int i = 0; i < list.size(); i++) {String str = list.get(i);if(str.equals("ArrayList")){list.remove(str);i--;}}System.out.println(Arrays.toString(list.toArray()));
}

3. for循环倒序遍历

代码实现如下:

public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("ArrayList");list.add("LinkedList");list.add("Vector");list.add("CopyOnWriteArrayList");for (int i = list.size() - 1; i >= 0; i--) {String str = list.get(i);if(str.equals("ArrayList")){list.remove(str);}}System.out.println(Arrays.toString(list.toArray()));
}

三种方式中,个人推荐使用Iterator。

Java8中的Stream()流操作

  • stream():为集合创建串行流
  • parallelStream():为集合创建并行流
  • filter():筛选
  • map():将对应的元素按照给定的方法进行转换
  • sorted():排序
  • limit():限制返回个数
  • reduce():聚合
  • anyMatch():Stream 中任意一个元素符合传入的 predicate,返回 true
  • allMatch():Stream 中全部元素符合传入的 predicate,返回 true
  • noneMatch():Stream 中没有一个元素符合传入的 predicate,返回 true

ArrayList的使用场景

ArrayList是一个动态数组,也是我们最常用的集合。由于ArrayList底层使用数组实现,所以具有查询快的特点,并且ArrayList实现了RandmoAccess接口,所以ArrayList适用于查多写少的场景。同时ArrayList是线程不安全的,不建议在多线程环境中使用。

arraylist 后往前遍历_Java集合框架之ArrayList相关推荐

  1. arraylist 后往前遍历_面试官:请说出线程安全的 ArrayList 有哪些,除了Vector

    以下环境是 JDK 1.8 ArrayList 的初始容量 面试官:你看过 ArrayList 的源码? Python 小星:看过 面试官:那你说下ArrayList 的初始容量是多少? Python ...

  2. arraylist 后往前遍历_面试官:谈谈常用的Arraylist和Linkedlist的区别

    Arraylist:底层是基于动态数组,根据下表随机访问数组元素的效率高,向数组尾部添加元素的效率高:但是,删除数组中的数据以及向数组中间添加数据效率低,因为需要移动数组. 例如最坏的情况是删除第一个 ...

  3. arraylist 后往前遍历_ArrayList和LinkedList的深入浅出

    0. 说明 今天无意间看到网上在讨论ArrayList和LinkedList的区别,本文准备从源码中分析下这两个List的底层实现和应用选择. 1. ArrayList 从名称上可以看出来是数组的形式 ...

  4. java arraylist 构造_深入理解java集合框架之---------Arraylist集合 -----构造函数

    ArrayList有三个构造方法 ArrayList有三个常量 1.private transient Object[] elementData (数组); 2.private int size (元 ...

  5. java jcf框架干啥的_Java集合框架(常用类) JCF

    Java集合框架(常用类) JCF 为了实现某一目的或功能而预先设计好一系列封装好的具有继承关系或实现关系类的接口: 集合的由来: 特点:元素类型可以不同,集合长度可变,空间不固定: 管理集合类和接口 ...

  6. 记直接插入排序,为什么必须从后往前遍历

    从前往后遍历找到插入位置 @Slf4j public class InsertSort {@Testpublic void test() {int[] arr = new int[]{5, 3, 7, ...

  7. 【Java集合框架】ArrayList类方法简明解析(举例说明)

    本文目录 1.API与Java集合框架 2.ArrayList类方法解析 2.1 add() 2.2 addAll() 2.3 clear() 2.4 clone() 2.5 contains() 2 ...

  8. collection集合 多少钱_Java集合框架大汇总,建议收藏

    Java集合 Java集合框架:是一种工具类,就像是一个容器可以存储任意数量的具有共同属性的对象. Java集合中成员很丰富,常用的集合有ArrayList,HashMap,HashSet等.线程安全 ...

  9. java集合框架类_Java集合框架总结—超详细-适合面试

    Set和List接口是Collection接口派生的两个子接口,Queue是Java提供的队列实现,类似于List. Map实现类用于保存具有映射关系的数据(key-value). 2.Set.Lis ...

最新文章

  1. 2015 HIAST Collegiate Programming Contest J
  2. video/audio在ios/android上播放兼容
  3. 组织应该采用集中式发电机吗?
  4. 向io设备发出中断请求_人们常说的计算机设备管理是什么,深入解读计算机设备管理...
  5. IntelliJ IDEA(一、下载,安装与激活)
  6. 答读者问(6):有关IT培训和毕业之前的迷茫等问题
  7. 判断不为空和不为空串的方法java
  8. python写接口测试代码_python写运单接口测试(增改查)完整代码
  9. 炫界 (978) -(建工发现应用克隆漏)_除了DMA,这些漏损点检测与漏损区域识别技术你知道么?...
  10. XJOI 3864 农村连接城市
  11. Extjs store用法详解
  12. Centos下面Eclipse打开文件闪退
  13. cp命令强制覆盖方式实现
  14. 【正点原子STM32连载】第十章 STM32CubeMX简介 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1
  15. [今日阅读] [TSE 2019] - Mining Fix Patterns for FindBugs Violations
  16. 剑麻的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  17. 手机报彩信易操作平台
  18. 智鼎在线测评行测题记录
  19. XSS过关(一) 1~5关 详细
  20. 魔术师的猜牌术(一维数组) C++程序

热门文章

  1. AI竟然可以预测性取向,女装大佬在算法面前不堪一击!
  2. k8s概念入门之apiserver-针对1.1.版本阅读
  3. 求1+2+3+…+n
  4. 必看干货|成为大数据专业人员必要且重要的7大技能
  5. 机器视觉系统原理及学习策略
  6. C++添加程序到windows的启动项的代码
  7. DesignPattern(四)结构型模式(下)
  8. python面向对象编程 -- 封装、继承
  9. 为您解析大数据的未来趋势
  10. MySQL Online DDL的改进与应用