一、 List概述
1.1概念
List是一种常用的集合类型,它可以存储任意类型的对象,也可以结合泛型来存储具体的类型对象,本质上就是一个容器。
1.2体系
List中主要有ArrayList、LinkedList两个实现类
1.3 通用方法
ArrayList和LinkedList通用方法
方法名 | 说明 |
public boolean add(要添加的元素) | 将指定的元素追加到此集合的末尾 |
public boolean remove(要删除的元素) | 删除指定元素,返回值表示是否删除成功 |
public E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
public E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
public E get(int index) | 返回指定索引处的元素 |
public int size() | 返回集合中的元素的个数 |
boolean contains(Object o) | 如果此列表包含指定的元素,则返回 true |
boolean addAll(int index, Collection<? extends E> c) | 将指定集合中的所有元素插入到此列表中,从指定的位置开始 |
void clear() | 列表中删除所有元素 |
这里以ArrayList举例
代码测试:
public static void main(String[] args) { // TODO Auto-generated method stub // 创建集合 List list = new ArrayList<>(); // 添加元素 list.add("hello"); list.add("world"); list.add("java"); // public boolean remove(Object o):删除指定的元素,返回删除是否成功 System.out.println(list.remove("world"));//true System.out.println(list.remove("javaee"));//false // public E remove(int index):删除指定索引处的元素,返回被删除的元素 System.out.println(list.remove(1));//world // IndexOutOfBoundsException System.out.println(list.remove(3)); // public E set(int index,E element):修改指定索引处的元素,返回被修改的元素 System.out.println(list.set(1,"javaee"));//world // IndexOutOfBoundsException System.out.println(list.set(3,"javaee")); // public E get(int index):返回指定索引处的元素 System.out.println(list.get(0));//hello System.out.println(list.get(1));//world System.out.println(list.get(2));//java // IndexOutOfBoundsException System.out.println(list.get(3)); // public int size():返回集合中的元素的个数 System.out.println(list.size());//3 // 输出集合 System.out.println("list:" + list);//list:[hello, world, java] // boolean contains(Object o) 如果此列表包含指定的元素,则返回 true System.out.println(list.contains("world"));//true // boolean addAll(int index, Collection c) //将指定集合中的所有元素插入到此列表中,从指定的位置开始 List list2 = new ArrayList<>(); //addall前list2 System.out.println(list2);// [] System.out.println(list2.addAll(0, list));// true //addall后list2 System.out.println(list2);// [hello, world, java] }
二、List的特点
(1)有序性:List中的元素是按照添加顺序进行存放的。因为有序,所以有下标,下标从0开始
(2)可重复性: List中可以存储重复的元素
三、遍历方式
3.1 foreach
List list = new ArrayList<>(); //添加元素 list.add("hello"); list.add("world"); list.add("java"); for (Object object : list) { System.out.println(object); }
3.2 for循环
根据下标遍历
//创建集合 List list = new ArrayList<>(); //添加元素 list.add("hello"); list.add("world"); list.add("java"); for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); }
3.3迭代器
//创建集合 List list = new ArrayList<>(); //添加元素 list.add("hello"); list.add("world"); list.add("java"); Iterator iterator = list.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); }
四、ArrayList
4.1ArrayList概述
4.1.1概念
ArrayList是Java中的一个类,实现了List接口,底层使用数组来存储元素。与数组相比,它具有更灵活的大小和动态的增加和删除元素。
4.1.2数据结构
ArrayList的数据结构本质上就是数组。区别在于,数组是一种静态的数据结构,需要在创建数组时就指定它的长度,并且创建后长度无法改变。而ArrayList是一种动态的数据结构,它可以自动进行扩容。
ArrayList的底层数据结构:
4.2ArrayList的特点
除了具备List有序性、可重复性特点外,ArrayList还具备以下的特点:
4.2.1自动扩容
当向ArrayList中加入的元素超过了其默认的长度时(由于ArrayList是数组的封装类,在创建ArrayList时不用给定长度,其默认长度为10),它会自动扩容以增加存储容量
4.2.2随机访问
随机访问是指可以直接访问元素,而不需要从头部或者尾部遍历整个列表。由于ArrayList底层是用数组实现的,因此可以通过索引来快速访问元素。
4.2.3 慢速插入/删除:
相比于链表(如LinkedList),ArrayList在中间插入或删除元素较慢,因为需要移动元素。
4.2.4高效的遍历
由于ArrayList底层采用了数组来存储元素,所以对于ArrayList的遍历操作比较高效。
4.3常用方法
方法名 | 说明 |
trimToSize() | 将内部存储的数组大小调整为列表中元素的实际数量。 |
ensureCapacity(int minCapacity) | 设置内部存储的数组大小,以容纳指定数量的元素。 |
toArray(T[] a) | 将列表中的元素转换为指定类型的数组 |
代码测试:
package com.xqx.list; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; public class demo4 { public static void main(String[] args) throws Exception { //创建一个容量为100的集合 ArrayList list = new ArrayList(100); //新增数据前容量 elementDataLength(list); //当前List集合容量为:100 for (int i = 0; i < 101; i++) { list.add(i);//增加101个元素 } //新增数据后容量大小 elementDataLength(list); //当前List集合容量为:150 list.trimToSize(); //调用trimToSize()方法后容量大小,能够避免资源浪费 elementDataLength(list); //当前List集合容量为:101 //创建一个容量为4的集合 ArrayList list2 = new ArrayList(4); elementDataLength(list2);//当前List集合容量为:4 list2.add(1); list2.add(2); list2.add(3); list2.add(4); list2.add(5); //超过4个元素自动扩容 4*1.5=6 elementDataLength(list2);//当前List集合容量为:6 //手动扩容,减少扩容次数,影响性能 list2.ensureCapacity(100); elementDataLength(list2);//当前List集合容量为:100 Integer[] arr = new Integer[list2.size()];//注意,传入的数组长度应该至少等于ArrayList的元素个数,否则将会抛出空指针异常。 for (Integer integer : arr) { System.out.println(integer);//null null null null null } list2.toArray(arr); for (Integer integer : arr) { System.out.println(integer);// 1 2 3 4 5 } } /** * 定义一个名为 "elementDataLength" 的方法,该方法用于使用反射获取 ArrayList 中存储元素的底层数组的长度并输出 * */ public static void elementDataLength(List list) throws Exception { // 使用反射获取 ArrayList 对象的成员变量 elementData Field ed = list.getClass().getDeclaredField("elementData"); ed.setAccessible(true); // 设置允许访问私有变量 Object[] o = (Object[]) ed.get(list); // 获取 ArrayList 中存储元素的底层数组对象的引用 // 输出 ArrayList 的容量信息,即底层数组的长度 System.out.println("当前List集合容量为:" + o.length); } }
4.4 增长因子论证
我们说ArrayList的底层数据结构是数组,而数组的长度是固定的,但ArrayList长度却是可变的。这里就能产生两个问题:
1、数组和ArrayList的性质都不一样,你怎么能说ArrayList的底层数据结构是数组?
2、即使你已经证明了ArrayList的底层数据结构是数组,那为什么数组的长度是固定的,但ArrayList长度却是可变的?
4.4.1证明ArrayList的底层数据结构是数组
证明第一个问题其实很简单,我们查看源代码即可:
(1):
(2):
(3):
我们一步步往上查看,可以看到有一个名为 elementData 的字段: transient Object[] elementData;
这个字段是用于存储 ArrayList 中所有元素的数组。
通过这些代码实现,我们可以看到 ArrayList 的内部实现确实是基于数组实现的。
4.4.2证明ArrayList的容量可变
证明第二个问题,跟我们ArrayList的一个特点密切相关,没错,聪明的你已经猜出来了,就是自动扩容:
在ArrayList中,每当添加一个元素时,都需要先检查当前数组容量是否足够,如果容量不足,则需要进行扩容操作。而ArrayList的扩容机制是:将原数组的长度乘以一个增长因子,通常是1.5,生成一个新的大数组,然后将原数组中的元素复制到新数组中来,这样就完成了扩容操作。
Java集合之List集合(下)https://developer.aliyun.com/article/1386304