问:Arraylist 的动态扩容机制是如何自动增加的?简单说说你理解的流程?

答:当在 ArrayList 中增加一个对象时 Java 会去检查 Arraylist 以确保已存在的数组中有足够的容量来存储这个新对象(默认为 10,最大容量为 int 上限,减 8 是为了容错),如果没有足够容量就新建一个长度更长的数组(原来的1.5倍),旧的数组就会使用 Arrays.copyOf 方法被复制到新的数组中去,现有的数组引用指向了新的数组。下面代码展示为 Java 1.8 中通过 ArrayList.add 方法添加元素时,内部会自动扩容,扩容流程如下:

 public boolean add(E e) {//确保容量够用,内部会尝试扩容,如果需要ensureCapacityInternal(size + 1);  // Increments modCount!!elementData[size++] = e;return true;}
//在未指定容量的情况下,容量为DEFAULT_CAPACITY = 10//并且在第一次使用时创建容器数组,在存储过一次数据后,数组的真实容量至少DEFAULT_CAPACITYprivate void ensureCapacityInternal(int minCapacity) {//判断当前的元素容器是否是初始的空数组if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//如果是默认的空数组,则 minCapacity 至少为DEFAULT_CAPACITYminCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);}ensureExplicitCapacity(minCapacity);}//通过该方法进行真实准确扩容尝试的操作private void ensureExplicitCapacity(int minCapacity) {modCount++;//记录List的结构修改的次数//需要扩容if (minCapacity - elementData.length > 0)grow(minCapacity);}private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;//扩容操作private void grow(int minCapacity) {//原来的容量int oldCapacity = elementData.length;//新的容量 = 原来的容量 + (原来的容量的一半)int newCapacity = oldCapacity + (oldCapacity >> 1);//如果计算的新的容量比指定的扩容容量小,那么就使用指定的容量if (newCapacity - minCapacity < 0)newCapacity = minCapacity;//如果新的容量大于MAX_ARRAY_SIZE(Integer.MAX_VALUE - 8)//那么就使用hugeCapacity进行容量分配if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);// minCapacity is usually close to size, so this is a win://创建长度为newCapacity的数组,并复制原来的元素到新的容器,完成ArrayList的内部扩容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<Integer> list = new ArrayList<Integer>();list.add(1);list.add(2);list.add(3);Integer[] array1 = new Integer[3];list.toArray(array1);Integer[] array2 = list.toArray(new Integer[0]);System.out.println(Arrays.equals(array1, array2));// 1 结果是什么?为什么?
Integer[] array = { 1, 2, 3 };List<Integer> list =  Arrays.asList(array);list.add(4);// 2 结果是什么?为什么?
Integer[] array = { 1, 2, 3 };List<Integer> list = new ArrayList<Integer>(Arrays.asList(array));list.add(4);// 3 结果是什么?为什么?

1 输出为 true,因为 ArrayList 有两个方法可以返回数组 Object[] toArray() 和 <T> T[] toArray(T[] a),第一个方法返回的数组是通过 Arrays.copyOf 实现的,第二个方法如果参数数组长度足以容纳所有元素就使用参数数组,否则新建一个数组返回(这里也是使用copyOf来实现的),所以结果为 true。

    public <T> T[] toArray(T[] a) {if (a.length < size)// Make a new array of a's runtime type, but my contents:return (T[]) Arrays.copyOf(elementData, size, a.getClass());System.arraycopy(elementData, 0, a, 0, size);if (a.length > size)a[size] = null;return a;}

2 会抛出 UnsupportedOperationException 异常,因为 Arrays 的 asList 方法返回的是一个 Arrays 内部类的 ArrayList 对象,这个对象没有实现 add、remove 等方法,只实现了 set 等方法,所以通过 Arrays.asList 转换的列表不具备结构可变性。

    public static <T> List<T> asList(T... a) {return new ArrayList<>(a);}private static class ArrayList<E> extends AbstractList<E>implements RandomAccess, java.io.Serializable{private static final long serialVersionUID = -2764017481108945198L;private final E[] a;ArrayList(E[] array) {a = Objects.requireNonNull(array);}@Overridepublic int size() {return a.length;}@Overridepublic Object[] toArray() {return a.clone();}@Override@SuppressWarnings("unchecked")public <T> T[] toArray(T[] a) {int size = size();if (a.length < size)return Arrays.copyOf(this.a, size,(Class<? extends T[]>) a.getClass());System.arraycopy(this.a, 0, a, 0, size);if (a.length > size)a[size] = null;return a;}@Overridepublic E get(int index) {return a[index];}@Overridepublic E set(int index, E element) {E oldValue = a[index];a[index] = element;return oldValue;}@Overridepublic int indexOf(Object o) {E[] a = this.a;if (o == null) {for (int i = 0; i < a.length; i++)if (a[i] == null)return i;} else {for (int i = 0; i < a.length; i++)if (o.equals(a[i]))return i;}return -1;}@Overridepublic boolean contains(Object o) {return indexOf(o) != -1;}@Overridepublic Spliterator<E> spliterator() {return Spliterators.spliterator(a, Spliterator.ORDERED);}@Overridepublic void forEach(Consumer<? super E> action) {Objects.requireNonNull(action);for (E e : a) {action.accept(e);}}@Overridepublic void replaceAll(UnaryOperator<E> operator) {Objects.requireNonNull(operator);E[] a = this.a;for (int i = 0; i < a.length; i++) {a[i] = operator.apply(a[i]);}}@Overridepublic void sort(Comparator<? super E> c) {Arrays.sort(a, c);}}

3 当然可以正常运行咯,不可变结构的 Arrays 的 ArrayList 通过构造放入了真正的万能 ArrayList,自然就可以操作咯。

问:为什么 ArrayList 的增加或删除操作相对来说效率比较低?能简单解释下为什么吗?

答:ArrayList 在小于扩容容量的情况下其实增加操作效率是非常高的,在涉及扩容的情况下添加操作效率确实低,删除操作需要移位拷贝,效率是低点。因为 ArrayList 中增加(扩容)或者是删除元素要调用 System.arrayCopy 这种效率很低的方法进行处理,所以如果遇到了数据量略大且需要频繁插入或删除的操作效率就比较低了,具体可查看 ArrayList 的 add 和 remove 方法实现,但是 ArrayList 频繁访问元素的效率是非常高的,因此遇到类似场景我们应该尽可能使用 LinkedList 进行替代效率会高一些。

问:简单说说 Array 和 ArrayList 的区别?

答:这题相当小儿科,Array 可以包含基本类型和对象类型,ArrayList 只能包含对象类型;Array 的大小是固定的,ArrayList 的大小是动态变化的;ArrayList 提供了更多的方法和特性,譬如 addAll()、removeAll()、iterator() 等。

                   

阅读原文请关注微信公众号:码农每日一题

转载于:https://www.cnblogs.com/lostyears/p/8529594.html

Java 集合 ArrayList 需要知道的几个问题相关推荐

  1. java集合-ArrayList

    java集合 ArrayList Iterable Comparable(排序接口) 项目结构: class Dog implements Comparable<Dog> {private ...

  2. java集合-ArrayList学习笔记

    集合和数组关系? 1)     集合是一种容器,可以存储多个数据. 2)     数组的长度是固定的,集合的长度是可变的. 3)     集合中存储的元素必须为引用类型数据,初始值为NULL Arra ...

  3. Java集合ArrayList

    ArrayList List接口的一个实现类 内部封装了一个长度可变的数组对象 当存入的元素,超过数组长度时,会在内存中,分配一个更大的数组 来存储这些元素,可以看做是一个长度可变的数组 优点 允许通 ...

  4. Java集合—ArrayList底层原理

    原文作者:0 errors 0 warnings 原文地址:用大白话告诉你ArrayList的底层原理 目录 一.数据结构 二.线程安全性 三.继承关系 四.构造方法 五.add()方法 六.扩容机制 ...

  5. Java集合-ArrayList源码解析-JDK1.8

    ◆ ArrayList简介 ◆ ArrayList 是一个数组队列,相当于 动态数组.与Java中的数组相比,它的容量能动态增长.它继承于AbstractList,实现了List, RandomAcc ...

  6. Java集合 ArrayList 扩容

    Java的集合框架 集合的分类 可以分为单列集合和双列集合 单列集合主要有Collection下面的List与Set,其中List最长被用到的是ArrayList与LinkedList以及Vctor三 ...

  7. 深入Java集合ArrayList的源码解析

    现在由大恶人付有杰来从增删改查几个角度轻度解析ArrayList的源码 首先ArrayList的底层数据结构非常简单,就是一个数组. 从源码第115行我们可以得出信息,他的默认数组长度是10. /** ...

  8. Java 集合 —— ArrayList 分析

    List 集合的特征: 有序 可以重复 可以随机访问(使用下标 添加,删除,访问) ArrayList 是 List 的实现类,所以 ArrayList 具有 List 的特征 ArrayList 是 ...

  9. 三十二、Java集合中的ArrayList

    @Author:Runsen @Date:2020/6/3 作者介绍:Runsen目前大三下学期,专业化学工程与工艺,大学沉迷日语,Python, Java和一系列数据分析软件.导致翘课严重,专业排名 ...

最新文章

  1. 常见的跑马灯效果,无缝连接。mcake官网公告使用
  2. lombok中的@Data注解与MyBatis的懒加载机制冲突解决
  3. kotlin 构造函数_Kotlin程序| 主要构造函数示例
  4. 3399 mysql_手动安装 mysql
  5. 保护视力,我写的一个定时提醒的小玩意。
  6. 执行了rm-f,除了跑路,如何恢复?
  7. 初学者python笔记(函数)
  8. php如何判断是否关注,微信公众号判断用户是否已关注php代码解析
  9. 学会这4个表达「万能公式」,下次向领导汇报时不再语无伦次
  10. Leetcode 561.数组拆分I
  11. java中lookup函数怎么用,excel Lookup查表函数的使用方法
  12. 为计算机技术奉献一生语录,乐于奉献的名言警句40句
  13. 回归模型+自变量和因变量之间的关系、回归模型的种类、回归模型的输出类型、个数角度
  14. js的间隔调用和延迟调用
  15. python33.dll_python33.dll,下载,简介,描述,修复,等相关问题一站搞定_DLL之家
  16. 【数据结构与算法基础】最短路径问题
  17. 红帽linux6主板,Linux_I810 Graphics LINUX Driver的安装,我的机子的主板是承启6WIV主板 - phpStudy...
  18. [19]Window管理框架WMF(Window Management Framework) 5.0 VS Window管理框架WMF(Window Management Framework) 5.1
  19. EPLAN_采购列表的使用
  20. 查看mysql存储引擎及修改默认存储引擎

热门文章

  1. datatables[columns] 中的详细参数
  2. vue-router 快速入门
  3. Android 源代码自动编译packages/apps
  4. Android 控件之RatingBar评分条(五星)自定义样式
  5. Android button 居中
  6. Vue—相关工具—webpack
  7. 分布式服务常见问题—分布式事务
  8. SpringBoot连接Redis超简单
  9. ScrollView内嵌ListView或GridView的滑动处理
  10. SDNU 1011.盒子与球(斯特林函数)