问: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_CAPACITY
private void ensureCapacityInternal(int minCapacity) {
//判断当前的元素容器是否是初始的空数组
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//如果是默认的空数组,则 minCapacity 至少为DEFAULT_CAPACITY
minCapacity = 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) // overflow
throw 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);
} @Override
public int size() {
return a.length;
} @Override
public 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;
} @Override
public E get(int index) {
return a[index];
} @Override
public E set(int index, E element) {
E oldValue = a[index];
a[index] = element;
return oldValue;
} @Override
public 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;
} @Override
public boolean contains(Object o) {
return indexOf(o) != -1;
} @Override
public Spliterator<E> spliterator() {
return Spliterators.spliterator(a, Spliterator.ORDERED);
} @Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
for (E e : a) {
action.accept(e);
}
} @Override
public 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]);
}
} @Override
public 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() 等。

                   

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

Java 集合 ArrayList 需要知道的几个问题的更多相关文章

  1. Java 集合 ArrayList和LinkedList的几种循环遍历方式及性能对比分析 [ 转载 ]

    Java 集合 ArrayList和LinkedList的几种循环遍历方式及性能对比分析 @author Trinea 原文链接:http://www.trinea.cn/android/arrayl ...

  2. Java基础系列 - JAVA集合ArrayList,Vector,HashMap,HashTable等使用

    package com.test4; import java.util.*; /** * JAVA集合ArrayList,Vector,HashMap,HashTable等使用 */ public c ...

  3. Java集合---ArrayList的实现原理

    目录: 一. ArrayList概述 二. ArrayList的实现 1) 私有属性 2) 构造方法 3) 元素存储 4) 元素读取 5) 元素删除                 6) 调整数组容量 ...

  4. Java集合 -- ArrayList集合及应用

    JAVA集合 对象数组 集合类之ArrayList 学生管理系统 斗地主案例 NO.one 对象数组 1.1 对象数组描述 A:基本类型的数组:存储的元素为基本类型 int[] arr={1,2,3, ...

  5. Java集合--ArrayList出现同步问题的原因

    1 fail-fast简介 fail-fast 机制是java集合(Collection)中的一种错误机制.当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件.例如:当某一个线 ...

  6. java集合-- arraylist小员工项目

    import java.io.*; import java.util.ArrayList; public class Emexe { public static void main(String[] ...

  7. Java集合ArrayList的应用

    /** * * @author Administrator * 功能:Java集合类ArrayList的使用 */ package com.test; import java.io.BufferedR ...

  8. java集合-ArrayList

    一.ArrayList 概述 ArrayList 是实现 List 接口的动态数组,所谓动态就是它的大小是可变的.实现了所有可选列表操作,并允许包括 null 在内的所有元素.除了实现 List 接口 ...

  9. Java集合ArrayList源码解读

    最近在回顾数据结构,想到JDK这样好的代码资源不利用有点可惜,这是第一篇,花了心思.篇幅有点长,希望想看的朋友认真看下去,提出宝贵的意见.  :) 内部原理 ArrayList 的3个字段 priva ...

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

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

随机推荐

  1. vue-cli npm install 失败

    1.$ npm install -g vue-cli 2.vue init webpack sell (sell 是项目) 3.然后就是自动下载模板,根据提示输入 4.cd cell => np ...

  2. ffmpeg 视音处理

    (经常用到ffmpeg 做一些视频数据的处理转换等,用来做测试,今天总结了一下,参考了网上部分朋友的经验,一起在这里汇总了一下,有需要的朋友可以收藏测试一下,有问题欢迎在下面回帖交流,谢谢;by te ...

  3. 中文情感分析 glove+LSTM

    最近尝试了一下中文的情感分析. 主要使用了Glove和LSTM.语料数据集采用的是中文酒店评价语料 1.首先是训练Glove,获得词向量(这里是用的300d).这一步使用的是jieba分词和中文维基. ...

  4. [BZOJ2843] 极地旅行社(LCT)

    传送门 模板. ——代码 #include <cstdio> #include <iostream> #define N 300001 #define get(x) (son[ ...

  5. codevs3411 洪水

    题目描述 Description 小浣熊松松和朋友到野外露营,没想到遇上了π年一次的大洪水,好在松松是一只爱观察的小浣熊,他发现露营地的地形和洪水有如下性质: ①露营地可以被看做是一个N*M的矩形方阵 ...

  6. 交友app

    编辑注记:这是由译者 han_qi 翻译纽约客的一篇文章,从女性的角度描写了交友产品的用户体验及需求,值得广大产品经理深入研究,文章略长,但值得深读.原文<Overwhelmed and Cre ...

  7. MYSQL中插入数据以及修改数据的部分方法

    #插入/增加:使用INSERT #修改:使用ALTER #修改数据类型ALTER TABLE table02 MODIFY COLUMN cname VARCHAR(100);ALTER TABLE ...

  8. 项目中应用到的框架和技术之三——echarts

    echarts是效果丰富的图表库,当时考虑怎么炫怎么来就引入了这个库来做图表展示,官网:http://echarts.baidu.com 项目里用的比较浅,估且一看吧 代码: this.toChart ...

  9. Linux: 通过命令行上传文件到ftp服务器

    url -T fie-name ftp://server-address --user user:password

  10. linux系统下安装R

    一.先通过ssh将R安装包R-3.2.2.tar.gz从本机复制到你的linux虚拟机上的/home下: 二.解压安装包 #tar -zxvf R-3.2.2.tar.gz 三.1).进入到解压后的R ...