Java进阶(List)——面试时List常见问题解读 & 结合源码分析

前言

List、Set、HashMap作为Java中常用的集合,需要深入认识其原理和特性。

本篇博客介绍常见的关于Java中List集合的面试问题,结合源码分析题目背后的知识点。

关于的Set的博客文章如下:

关于HaseMap的博客文章如下:

其他相关的List的文章合集如下:

目录

引出


1.ArrayList如何扩容,1.5倍;

2.ArrayList如何拷贝,深拷贝,浅拷贝;

3.ArrayList的增加或者删除效率低,arraycopy方法;

4.指定长度创建ArrayList对象,避免频繁扩容;

5.线程安全的ArrayList集合:Collections.synchronizedList;

6.LinkedList 和 ArrayList 该如何选择:ArrayList增删效率低,查询效率高;LinkedList 查询效率低,增删效率高

7.Vector 集合和 ArrayList 区别:Vector扩容机制为原始的2倍,线程安全;

ArrayList 如何扩容的?/ArrayList的大小是如何自动增加的?

ArrayList初始化的时候,若没有给定长度,则默认调用无参构造

此处elelmentData为ArrayList底层数组,后面DEFAULTCAPACITY_EMPTY_ELEMENTDATA 为常量数组初值为空,即长度为0

1.add添加第一个元素

当执行add方法添加第一个元素时,执行下面的代码

此处涉及到两条代码:

  • ensureCapacityInternal方法内会调用calculateCapacity方法,此处才是赋予数组长度为10

ensureCapacityInternal 方法

calculateCapacity方法

java 复制代码
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    //如果数组是空的
       if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
           //返回数组的容量,DEFAULT_CAPACITY=10
           return Math.max(DEFAULT_CAPACITY, minCapacity);
       }
       return minCapacity;
   }

此时集合可以添加默认的10个元素

2.添加第11个元素时

当添加第11个元素时,ensureExplicitCapacity方法中,minCapacity为11,而原数组长度为10,所以if结构进入grow方法-扩容核心方法

ensureExplicitCapacity方法

grow方法-扩容核心方法

java 复制代码
private void grow(int minCapacity) {
       // overflow-conscious code
        //把旧的长度赋值给oldCapacity
       int oldCapacity = elementData.length;
        //新的长度就=旧的长度*1.5
       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);
   }
  • oldCapacity赋值为原数组长度,为10 ,newCapacity赋值为原长度1.5倍,即15

  • 调用复制数组方法,将之前的数组复制到新的数组中,并将需要添加的元素加到数组最后一位,完成扩容!

如何复制某个ArrayList到另一个Arraylist中去?

重写clone方法,ArrayList克隆

厘清概念:深浅拷贝

Java进阶(4)------结合类加载JVM的过程理解创建对象的几种方式:new,反射Class,克隆clone(拷贝),序列化反序列化

浅拷贝:虽然返回一个元素一样的ArrayList,复制的是元素的引用,即其中一个改变了元素,另一个也会跟着改变

两个集合中间存储了同一份元素的引用

例如:

深拷贝:重写clone方法,利用迭代器iterator或遍历集合,重新创建引用对象,逐个添加

例如:

复制的方法

list.clone()

clone.addALl(list);

Collections.copy(clone,list);

在索引中ArrayList的增加或者删除某个对象的运行过程?效率很低吗?解释一下为什么?

效率确实低

效率是很低的,因为ArrayList无论是增加或者删除某个对象,我们都要通过对数组中的元素进行移位来实现。

  • 增加元素时,我们要把要增加位置及以后的所有元素都往后移一位,先腾出一个空间,然后再进行添加。
  • 删除某个元素时,我们也要把删除位置以后的元素全部元素往前挪一位,通过覆盖的方式来删除。

而这种移位就需要不断的arraycopy,是很耗时间的,所以效率自然也很低。

源码arraycopy方法

增加元素时

删除元素时

现在我有一个很大的数组需要拷贝,原数组大小是 5k,请问如何快速拷贝?

指定长度创建ArrayList对象,避免频繁扩容

如何获得一个线程安全的ArrayList集合?

Collections.synchronizedList

java 复制代码
List<Object> datas = Collections.synchronizedList(new ArrayList<>());

源码分析

从源码可以看到集合操作都加了synchronized 关键字,保证了在同一时刻,数组和链表只会被一个线程所修改。

LinkedList 和 ArrayList 该如何选择?

选择原则

  • ArrayList 底层为数组,在增加和删除元素时会频繁的调用arraycopy,所以查询效率高,增删效率低
  • LinkedList 底层为链表,故查询效率低,但增删效率高。

LinkedList源码node节点

  • item 为当前元素
  • next指向下一个元素,若为最后一个则为null
  • prev指向上一个元素,若为第一个则为null

Vector 集合

  1. vector 和 ArrayList 基本一样
  2. 区别在于 vector扩容机制为原始的2倍,ArrayList为之前的1.5倍
  3. vector 是线程安全的,ArrayList是非线程安全的

总结

1.ArrayList如何扩容,1.5倍;

2.ArrayList如何拷贝,深拷贝,浅拷贝;

3.ArrayList的增加或者删除效率低,arraycopy方法;

4.指定长度创建ArrayList对象,避免频繁扩容;

5.线程安全的ArrayList集合:Collections.synchronizedList;

6.LinkedList 和 ArrayList 该如何选择:ArrayList增删效率低,查询效率高;LinkedList 查询效率低,增删效率高

7.Vector 集合和 ArrayList 区别:Vector扩容机制为原始的2倍,线程安全;

相关推荐
customer087 小时前
【开源免费】基于SpringBoot+Vue.JS体育馆管理系统(JAVA毕业设计)
java·vue.js·spring boot·后端·开源
Miketutu8 小时前
Spring MVC消息转换器
java·spring
乔冠宇8 小时前
Java手写简单Merkle树
java·区块链·merkle树
LUCIAZZZ9 小时前
简单的SQL语句的快速复习
java·数据库·sql
komo莫莫da9 小时前
寒假刷题Day19
java·开发语言
S-X-S10 小时前
算法总结-数组/字符串
java·数据结构·算法
linwq810 小时前
设计模式学习(二)
java·学习·设计模式
桦说编程11 小时前
CompletableFuture 超时功能有大坑!使用不当直接生产事故!
java·性能优化·函数式编程·并发编程
@_@哆啦A梦11 小时前
Redis 基础命令
java·数据库·redis
Dr.勿忘12 小时前
C#面试常考随笔8:using关键字有哪些用法?
开发语言·unity·面试·c#·游戏引擎