ArrayList

存储机制

基于动态数组实现,默认长度 10 ;因为数组特性,其读取速度快,增删效率慢

因为写入或读取都是通过数组索引直接操作,所以,允许为 null 值, 且可以重复

当数组大小不满足时需要增加存储能力,就要将已经有数组的数据复制到新的存储空间中。当从 ArrayList 的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。

因此,它适合随机查找和遍历,不适合插入和删除。

类定义

在 jdk 8 中源码如下

public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable{

private static final int DEFAULT_CAPACITY = 10; // 默认大小为 10

transient Object[] elementData; // 数组存储

private int size;

}

ArrayList 实现了RandomAccess 接口, RandomAccess 是一个标志接口,表明实现这个这个接口的 List 集合是支持快速随机访问的。在 ArrayList 中,我们即可以通过元素的序号快速获取元素对象,这就是快速随机访问。

ArrayList 实现了Cloneable 接口,即覆盖了函数 clone(),能被克隆。

ArrayList 实现java.io.Serializable 接口,这意味着ArrayList支持序列化,能通过序列化去传输。

get()方法

public E get(int index) {

rangeCheck(index);

return elementData(index);

}

private void rangeCheck(int index) {

if (index >= size)

throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

}

通过以上代码可以看出,ArrayList 通过索引读取元素,所以其读取元素效率高

add()方法

public boolean add(E e) {

ensureCapacityInternal(size + 1); // Increments modCount!!

elementData[size++] = e;

return true;

}

private void ensureCapacityInternal(int minCapacity) {

ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));

}

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

}

private void grow(int minCapacity) {

int oldCapacity = elementData.length;

// 默认扩容 1.5 倍 ; >>1 相当于除 2

int newCapacity = oldCapacity + (oldCapacity >> 1);

if (newCapacity - minCapacity < 0)

newCapacity = minCapacity;

if (newCapacity - MAX_ARRAY_SIZE > 0)

newCapacity = hugeCapacity(minCapacity);

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;

}

通过以上代码发现,添加元素时会触发扩容机制,当集合扩容时采用的是Arrays.copyOf(elementData, newCapacity),因此造成了新增元素速度慢

remove()方法

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

elementData[--size] = null;

return oldValue;

}

删除方法同样进行了数组复制,所以效率同样不高

grow()方法

private void grow(int minCapacity) {

int oldCapacity = elementData.length;

// 默认扩容 1.5 倍 ; >>1 相当于除 2

int newCapacity = oldCapacity + (oldCapacity >> 1);

if (newCapacity - minCapacity < 0)

newCapacity = minCapacity;

if (newCapacity - MAX_ARRAY_SIZE > 0)

newCapacity = hugeCapacity(minCapacity);

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;

}

1、默认初始容量 10,如果数据长度超过,则容量扩大为原来的 1.5倍,

2、然后会再判断,扩容后的容量大小是否任然小于数据长度、是则,取为数据大小为最新容量大小

3、再判断此时的最新容量大小是否超过最大容量值 MAX_ARRAY_SIZE,

4、是则,判断数据大小是否超过最大容量值 ,

数据大小超过最大容量,则取 Integer 的最大值作为最新容量大小。

数据大小没有最大容量,则取最大容量作为最新容量大小。

线程安全

ArrayList 线程不安全原因:使用数组存储,当前多线程时,当对同一位置操作时,可能会被覆盖,或者读取到了未更新的数据等一系列问题。

线程安全解决方案

使用 Collections.synchronizedList(); 得到一个线程安全的 ArrayList。

使用 CopyOnWriteArrayList代替:读写分离的安全型并发集合对象,写操作需要加锁,防止并发写入时导致写入数据丢失。写操作在一个复制的数组上进行,写操作结束之后需要把原始数组指向新的复制数组。读操作还是在原始数组中进行,读写分离,互不影响。

使用场景:适合读多写少的应用场景。

缺点 1 :内存占用:在写操作时需要复制一个新的数组,使得内存占用为原来的两倍左右;

缺点 2 :数据不一致:读操作不能读取实时性的数据,因为部分写操作的数据还未同步到读数组中。

List 和Array转换

List 转Array:使用 list.toArray()

Array 转 List:使用 Arrays.asList(array)

//List 转Array

List list = new ArrayList();

Object[] array=list.toArray();

//Array 转 List

// 方式 1

List list = Arrays.asList(array); // 有缺陷

// 方式 2

List list = new ArrayList(Arrays.asList(array));

// 方式 3

List list = new ArrayList(array.length);

Collections.addAll(list, array);

注意:Arrays.asList(array) 不能作为一个 List 对象的复制操作

因为:方式 1 中 只是复制了引用,list 修改会改变 array 内容,因为返回的 list 是 Arrays 里面的一个静态内部类,该类并未实现add、remove方法,因此在使用时存在局限性

所以,一般使用第 2 中和第 3 种方式转换

String[] arr = {"1","2","3","4","5"};

List list1 = new ArrayList(Arrays.asList(arr));

List list2 = Arrays.asList(arr);

List list3 = new ArrayList(arr.length);

Collections.addAll(list3, arr);

list1.set(0, "a");

System.out.println("Array:"+Arrays.toString(arr));

System.out.println("List :"+list1.toString());

System.out.println();

list2.set(0, "b");

System.out.println("Array:"+Arrays.toString(arr));

System.out.println("List :"+list2.toString());

System.out.println();

list3.set(0, "c");

System.out.println("Array:"+Arrays.toString(arr));

System.out.println("List :"+list3.toString());

执行结果:

Array:[1, 2, 3, 4, 5]

List :[a, 2, 3, 4, 5]

Array:[b, 2, 3, 4, 5]

List :[b, 2, 3, 4, 5]

Array:[b, 2, 3, 4, 5]

List :[c, 2, 3, 4, 5]

ArrayList 去重

双重for循环去重

通过set集合判断去重,不打乱顺序

遍历后以 contains() 判断赋给另一个list集合

set和list转换去重

//双重for循环去重

for (int i = 0; i < list.size() - 1; i++) {

for (int j = list.size() - 1; j > i; j--) {

if (list.get(j).equals(list.get(i))) {

list.remove(j);

}

}

}

//set集合判断去重,不打乱顺序

List listNew=new ArrayList<>();

Set set=new HashSet();

for (String str:list) {

if(set.add(str)){ //HashSet 内部维持map,其键唯一

listNew.add(str);

}

}

ArrayList 排序

可以使用如下方式

对象实现 Comparable 接口,

使用 Collections.sort(List list, Comparator super T> c),

使用 ArrayList#sort(Comparator super E> c)

前一种:重写 compareTo(T o) 方法 ,后两种:通过匿名内部类的方式实现比较器 Comparator 接口,重写compare(T o1, T o2)

public class Student implements Comparable {

private int age;

@Override

public int compareTo(Student o) {

return this.age - o.age; // 升序

}

}

//自定义排序1

Collections.sort(list, new Comparator() {

@Override

public int compare(Student s1, Student s2) {

return s1.getAge() - s2.getAge();

}

});

//自定义排序2

new ArrayList().sort(new Comparator() {

@Override

public int compare(Student s1, Student s2) {

return s1.getAge() - s2.getAge();

}

});

Vector

同样基于动态数组实现,但它是线程安全的,它支持线程的同步,但实现同步需要很高的花费,

因此,访问它比访问 ArrayList 慢。

LinkedList

基于双向链表实现,只能顺序访问

因此,很适合数据的动态插入和删除,随机访问和遍历速度比较慢。

另外,他还提供了 List 接口中没有定义的方法,专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。

List 之间的区别

ArrayList 和 Vector 的

Vector 基本与ArrayList类似,数组存储,排列有序,可重复,允许多个NULL1、

访问效率比ArrayList稍慢,因为**Vector是同步的,线程安全**,它有对外方法都由 synchronized 修饰。

为什么要用Arraylist取代Vector呢

Vector类的所有方法都是同步的。可以由两个线程安全地访问一个Vector对象、但是一个线程访问Vector的话代码要在同步操作上耗费大量的时间。

Arraylist不是同步的,所以在不需要保证线程安全时时建议使用Arraylist。

Arraylist 与 LinkedList 区别

线程安全: ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;

底层数据结构:Arraylist 底层使用的是数组;LinkedList 底层使用的是双向链表数据结构

插入和删除是否受元素位置的影响

ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响,时间复杂度就为 O(n-i)

LinkedList 采用链表存储,所以插入和删除元素时间复杂度不受元素位置的影响,都是近似 O(1)而数组为近似 O(n)

是否支持快速随机访问: LinkedList 不支持高效的随机元素访问,而 ArrayList 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index)方法)。

java j集合_JNotes/Java-集合篇(2)集合之List.md at master · harryjudy2240/JNotes · GitHub...相关推荐

  1. java官网教程(进阶篇)—— 集合

    目录 集合--使用和扩展Java集合框架的课程. 简介 接口 Collection接口 Set接口 List接口 Queue 接口 Deque接口 Map接口 对象排序 SortedSet接口 Sor ...

  2. java编程规范每行代码窄字符,wiki/0xFE_编程规范.md at master · islibra/wiki · GitHub

    0xFE_编程规范 使用UTF-8编码 使用空格缩进 命名 清晰表达意图, 少用缩写(行业通用除外, 如: request=req, response=resp, message=msg), 不应使用 ...

  3. java并发框架支持锁包括,tip/面试题_并发与多线程.md at master · 171437912/tip · GitHub...

    01. java用()机制实现了进程之间的同步执行 A. 监视器 B. 虚拟机 C. 多个CPU D. 异步调用 正解: A 解析: 监视器机制即锁机制 02. 线程安全的map在JDK 1.5及其更 ...

  4. java cookbook 3_CookBook/Java核心/3-Java反射.md at master · zhgdbut/CookBook · GitHub

    #Java核心(三)反射 Java反射给我们提供了在运行时检查甚至修改应用行为的机制. 反射是java高级的核心技术,所有有经验的程序员都应该理解. 通过反射机制,我们可以在运行时检视 类.接口.枚举 ...

  5. java p=x,Java-Tutorial/20、javac和javap.md at master · allenchenx/Java-Tutorial · GitHub

    目录 title: 夯实Java基础系列20:从IDE的实现原理聊起,谈谈那些年我们用过的Java命令 date: 2019-9-20 15:56:26 # 文章生成时间,一般不改 categorie ...

  6. java反射 enum参数_CookBook/3-Java反射.md at master · Byron4j/CookBook · GitHub

    Java核心(三)反射 Java反射给我们提供了在运行时检查甚至修改应用行为的机制. 反射是java高级的核心技术,所有有经验的程序员都应该理解. 通过反射机制,我们可以在运行时检视 类.接口.枚举, ...

  7. java cookbook 3_CookBook/Java核心/3-Java反射.md at master · Byron4j/CookBook · GitHub

    #Java核心(三)反射 Java反射给我们提供了在运行时检查甚至修改应用行为的机制. 反射是java高级的核心技术,所有有经验的程序员都应该理解. 通过反射机制,我们可以在运行时检视 类.接口.枚举 ...

  8. java 正则表达式 table_JavaEdge/Java/Java中正则表达式.md at master · VegTableBird/JavaEdge · GitHub...

    主要用到的是这两个类 - java.util.regex.Pattern - java.util.regex.Matcher. Pattern对应正则表达式,一个Pattern与一个String对象关 ...

  9. linux at java,Linux-Tutorial/Java-bin.md at master · linsanityHuang/Linux-Tutorial · GitHub

    Java bin 目录下的工具 JVM 内存结构 运行时数据区(JVM 规范) VM 栈(JVM 虚拟机栈) 是线程私有的,它的生命周期和线程相同.它描述的是 Java 方法执行的内存模式. Java ...

最新文章

  1. python3.7.4+Django2.2.6一直提示path404报错问题:“Using the URLconf defined in XXX.urls, Django tried this...”
  2. 【Python基础】Pandas三种实现数据透视表的方法
  3. 初识 JSP---(Servlet映射 / ServletRequest / get与post区别 / 解决乱码)
  4. 未来十年,人人有望在家远程办公?
  5. 运筹学状态转移方程例子_如何确定动态规划的转移方程
  6. Spring DI(依赖注入)Xml篇
  7. 预约挂号费用保险赔吗?
  8. 跨林迁移用户、计算机、邮箱(1)
  9. Mybatis中javaType和jdbcType对应和CRUD例子
  10. [网络流24题] 软件补丁问题
  11. 无线AP和无线路由器区别 wifi热点
  12. 中国集成电路设计产业创新发展的认识和思考
  13. CentOS7下fdisk分区工具和LVM的简单使用
  14. 如何查看本机路由表并进行分析?
  15. 网易云精选评论,总有一句戳在你心里
  16. 安装了两种oracle数据库怎么卸载,oracle数据库卸载步骤
  17. 思科模拟器入门基础-静态路由
  18. 【存储过程造数mysql】
  19. 关于Blurry无法加载的问题
  20. 2020级李海扬、程志豪、杨本豪、周海涛——离散信源的熵的性质的简要介绍和证明

热门文章

  1. html2canvas导出地图,Leaflet OpenStreetMap使用html2canvas使用地图标记导出图像
  2. iOS连接linux服务器用什么,iOS实现通过SSH2协议链接Linux服务器,并执行相关指令...
  3. 宽屏企业网站源码中英php_宽屏版大气企业网站源码dede网站源码中英文网站模板SEO...
  4. Java黑皮书课后题第10章:*10.1(Time类)设计一个名为Time的类。编写一个测试程序,创建两个Time对象(使用new Time()和new Time(555550000))
  5. linux拷贝文件夹怎么删除,linux文件及文件夹拷贝移动删除
  6. Python列表的常用你操
  7. MySQL 查询表中某字段值重复的数据
  8. Jmeter基础(二)
  9. 51Nod1230 幸运数
  10. SpringBoot入门:新一代Java模板引擎Thymeleaf(实践)