List 接口常用实现类底层分析

一、集合

1.1 简介

集合主要分为两组(单列集合、双列集合),Collection 接口有两个重要的子接口 ListSet ,它们的实现子类都是单列集合。Map 接口的实现子类是双列集合,存放的是K-V

1.2 关系图

二、Collection 接口和常用方法

java 复制代码
public interface Collection<E> extends Iterator<E>

2.1 特点

1、Collection 实现子类可以存放多个元素,每个元素可以是 Object

2、 有些 Collection的实现类可以存放重复元素,有些不可以。

3、 有些 Collection 的实现类是有序的(List ),有些不是有序的(Set

4、Collection 接口没有直接的实现子类,是通过它的子接口 SetList来实现的

2.2 常用方法

下面使用实现类 ArrayList 来演示,如下

java 复制代码
public class CollectionTest {
    public static void main(String[] args) {
        Collection list = new ArrayList();
        // add: 添加单个元素
        list.add("java");
        list.add(10);
        list.add(true);
        System.out.println("list="+list);

        // remove: 删除指定元素
        list.remove(true);
        System.out.println("list="+list);
        
        // contains: 查找元素是否存在
        System.out.println(list.contains("java"));
        
        // size: 获取元素个数
        System.out.println("现在集合的大小为:"+list.size());
        
        // isEmpty: 判断是否为空
        System.out.println("判断集合是不是空的"+list.isEmpty());
        
        // clear: 清空集合
        list.clear();
        System.out.println("我要清空集合了,现在集合的大小为:"+list.size());

        // addAll: 添加多个元素
        ArrayList list2 = new ArrayList();
        list2.add("苹果");
        list2.add("香蕉");
        list.addAll(list2);
        System.out.println("添加完多个元素后集合的大小为:"+list.size());
        
        // containsAll: 查找多个元素是否都存在
        System.out.println("查找多个元素是否都存在:"+list.containsAll(list2));
        
        // removeAll: 删除多个元素
        list.removeAll(list2);
        System.out.println("删除多个元素后集合的大小为:"+list.size());
    }
}

2.3 接口遍历

2.3.1 Iterator 方式

Iterator 对象称为迭代器,主要用于遍历 Collection 集合中的元素。所有实现了 Collection 接口的集合类都有一个iterator() 方法,用于返回一个实现了 Iterator接口的对象,即可以返回一个迭代器。

需要注意的是,在调用 iterator.next() 方法之前必须要调用**iterator.hasNext()**方法进行检测,若不调用最终会报异常。

如果希望再次遍历,则需要重置我们的迭代器,即重新调用下 **coll.iterator()**方法即可。

java 复制代码
// 得到一个集合的迭代器
Iterator iterator = coll.iterator();
// 判断是否还有下一个元素
while(iterator.hasNext()){
	// next() 方法有两个作用:下移并且将下移以后集合位置上的元素返回
	System.out.println(iterator.next());
}

2.3.2 for 循环方式

增强 for 循环,可以代替 iterator 迭代器。它就是简化版的 iterator,本质是一样的,只能用于遍历集合或数组。

java 复制代码
for(元素类型 元素名:集合或数组名){

    // 访问元素
}

三、List 接口和常用方法

3.1 特点

1、List集合类中元素有序(即添加顺序和取出顺序是一致的)、且可重复。

2、List集合中每个元素都有其对应的顺序索引,即支持索引。

3、List容器中的元素都对应一个整数型的序号记录其在容器中的位置,可以根据序号存取容器中的元素。

3.2 常用实现类

ArrayListLinkedListVector

3.3 常用方法

java 复制代码
public class ListMethod {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("苹果");
        list.add("香蕉");

        // add(int index,Object ele): 在 index 位置插入 ele 元素
        list.add(1,"西瓜");
        System.out.println("list="+list);

        // addAll(int index,Collection els):从 index 位置开始将 els 中的所有元素添加进来
        List list2 = new ArrayList();
        list2.add("足球");
        list2.add("篮球");
        list.addAll(0,list2);
        System.out.println("list="+list);

        // Object get(int index):获取指定 index 位置的元素
        System.out.println(list.get(0));

        // int indexOf(Object obj):返回 obj 在集合中首次出现的位置
        System.out.println(list.indexOf("足球"));
        
        // int lastIndexOf(Object obj):返回 obj 在集合中末次出现的位置
        System.out.println(list.lastIndexOf("篮球"));
        
        // Object remove(int index):移除指定 index 位置的元素,并返回此元素
        System.out.println(list.remove(0));
        
        // Object set(int index,Object obj):设定指定 index 位置的元素为 obj,相当于是替换
        list.set(2,"美女");
        System.out.println("list="+list);
        
        // List subList(int fromIndex,int toIndex):返回从 fromIndex 到 toIndex 位置的子集合,左闭右开
        List list3 = list.subList(2,3);
        System.out.println("list3="+list3);
    }
}

3.4 排序方法

java 复制代码
// 按照 Book 类的 price 属性从小到大排序
public static void sort(List list){
	int size = list.size();
	for(int i=0;i<size-1;i++){
		for(int j=0;j<list.size()-1-i;j++){
			Book b1 = (Book)list.get(j);
			Book b2 = (Book)list.get(j+1);
			if(b1.getPrice()> b2.getPrice()){
				list.set(j,b2);
				list.set(j+1,b1);
			}
		}
	}
}

四、ArrayList

4.1 特点

ArrayList 可以添加 null ,并且可以存储多个。底层是由数组来实现数据存储的,ArrayList 基本等同于 Vector ,但是 ArrayList是线程不安全的。

ArrayList 中维护了一个 Object 类型的数组 elementData,如下:

java 复制代码
// transient 表示该属性不会被序列化
transient Object [] elementData

4.2 无参扩容分析

当创建 ArrayList 对象时,如果使用的是无参的构造器,则初始化 elementData 容量为 0 ,第一次添加元素的时候,elementData 会扩容为 10 ,如需要再次扩容,则扩容 elementData1.5倍。

java 复制代码
public class ArrayListTest {
    public static void main(String[] args) {
        // 使用无参构造器创建 ArrayList 对象
        // 创建一个容量为 0 的 elementData 数组
        ArrayList list = new ArrayList();
        for(int i=1;i<=10;i++){
            // add() 方法:先判断是否需要扩容,然后再执行赋值
            // 如果进行扩容,则第一次扩容为10,第二次即以后按照 1.5 倍扩容
            list.add(i);
        }
        for(int i=11;i<=15;i++) {
            // 此时要进行第二次扩容为: 10+10/2 = 15
            list.add(i);
        }
        // 此时要进行第三次扩容为: 15+15/2=22
        list.add(100);
        list.add(200);
        list.add(null);
    }
}

4.3 有参扩容分析

如果使用的是指定大小的构造器,则初始 elementData 容量为指定大小,如果需要扩容,则直接扩容 elementData1.5 倍。

java 复制代码
public class ArrayListTest2 {
    public static void main(String[] args) {
        // 使用有参构造器创建 ArrayList 对象
        // 创建一个容量为 8 的 elementData 数组
        ArrayList list = new ArrayList(8);
        for(int i=1;i<=10;i++){
            // add() 方法:先判断是否需要扩容,然后再执行赋值
            // 当 i=9 的时候需要进行扩容,此时按照 1.5 倍扩容:8+8/2 = 12
            list.add(i);
        }
        for(int i=11;i<=15;i++) {
            // 当 i=13 的时候,此时要进行第二次扩容为: 12+12/2 = 18
            list.add(i);
        }
        list.add(100);
        list.add(200);
        list.add(null);
    }
}

五、Vector

5.1 特点

Vector 底层也是一个对象数组,它是线程安全,Vector 类的操作方法带有 synchronized 关键字修饰。当涉及到线程安全时,可以使用 Vector

5.2 无参扩容分析

当创建 Vector 对象时,如果使用的是无参的构造器,则初始化 elementData 容量为 10 ,满了之后,扩容为 elementData 2倍。

java 复制代码
public class VectorTest {
    public static void main(String[] args) {
        // 使用无参构造器创建 Vector 对象
        // new Vector() 时会创建一个容量为 10 的 elementData 数组
        Vector list = new Vector();
        for(int i=1;i<=10;i++){
            // add() 方法:先判断是否需要扩容,然后再执行赋值
            list.add(i);
        }
        // 此时要进行第二次扩容为: 10+10=20
        list.add(100);
    }
}

5.3 有参扩容分析

如果使用的是指定大小的构造器,则初始 elementData 容量为指定大小,满了之后,扩容为 elementData 2倍。

java 复制代码
public class VectorTest {
    public static void main(String[] args) {
        // 使用有参构造器创建 Vector 对象
        // new Vector() 时会创建一个容量为 7 的 elementData 数组
        Vector list = new Vector(7);
        for(int i=1;i<=10;i++){
            // add() 方法:先判断是否需要扩容,然后再执行赋值
            // 当 i=8 的时候需要进行第一次扩容,容量为:7+7= 14
            list.add(i);
        }
        list.add(100);
    }
}

六、LinkedList

6.1 特点

LinkedList 底层实现了双向链表和双端队列的特点,可以添加任意元素,包括 null,线程不安全,没有实现同步。

6.2 底层结构

1、LinkedList底层维护了一个双向链表。

2、LinkedList 中维护了两个属性 firstlast分别指向首节点和尾节点。

3、 每个节点(Node 对象),里面又维护了 prevnextitem 三个属性,其中通过 prev 指向前一个,通过 next指向后一个节点。最终实现双向链表。

4、 所以 LinkedList的元素的添加和删除不是通过数组完成的,相对来说效率较高。

6.3 LinkedList 和 ArrayList 比较

|----------------|----------|-----------|-----------|
| | 底层结构 | 增删的效率 | 改查的效率 |
| ArrayList | 可变数组 | 较低,数组扩容 | 较高 |
| LinkedList | 双向链表 | 较高,通过链表追加 | 较低 |

如何选择 ArrayListLinkedList:

1、 如果我们增删的操作多,选择 LinkedList

2、 如果我们改查的操作多,选择 ArrayList

3、 一般来说,在程序中,80%90% 都是查询,因此大部分情况下会选择 ArrayList

4、 在一个项目中,根据业务灵活选择,有可能是一个模块使用的是 ArrayList ,另外一个模块是 LinkedList。也就是说,要根据业务来进行选择

相关推荐
原野心存5 分钟前
java基础进阶——继承、多态、异常捕获(2)
java·java基础知识·java代码审计
进阶的架构师10 分钟前
互联网Java工程师面试题及答案整理(2024年最新版)
java·开发语言
黄俊懿10 分钟前
【深入理解SpringCloud微服务】手写实现各种限流算法——固定时间窗、滑动时间窗、令牌桶算法、漏桶算法
java·后端·算法·spring cloud·微服务·架构
木子020419 分钟前
java高并发场景RabbitMQ的使用
java·开发语言
夜雨翦春韭30 分钟前
【代码随想录Day29】贪心算法Part03
java·数据结构·算法·leetcode·贪心算法
大霞上仙1 小时前
jmeter学习(1)线程组与发送请求
java·学习·jmeter
笃励1 小时前
Java面试题二
java·开发语言·python
易雪寒2 小时前
IDEA在git提交时添加忽略文件
java·git·intellij-idea
打码人的日常分享2 小时前
企业人力资源管理,人事档案管理,绩效考核,五险一金,招聘培训,薪酬管理一体化管理系统(源码)
java·数据库·python·需求分析·规格说明书
27669582922 小时前
京东e卡滑块 分析
java·javascript·python·node.js·go·滑块·京东