张小飞的Java之路——第三十一章——List

本文详细介绍了Java中的List接口,包括ArrayList和LinkedList的实现原理与特性。List接口存储元素有序且允许重复,提供了如add(index, e)、remove(index)等特有方法。ArrayList基于可变数组,适合读取操作,而LinkedList基于链表,插入删除效率高。此外,还讨论了多线程安全问题和去重方法。" 84555547,8229635,使用canvas从零开始制作小游戏教程,"['HTML', 'canvas', '小游戏', '图形编程', 'JavaScript']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

写在前面:

视频是什么东西,有看文档精彩吗?

视频是什么东西,有看文档速度快吗?

视频是什么东西,有看文档效率高吗?


1. 介绍

张小飞:我看 List 也是一个接口

诸小亮:是的,它是 Collection 下最常用的子接口之一,有自己独有的特点

  • List:列表容器
  • 存储的元素是有序的(存储的顺序和取出的顺序一致)
  • 每个元素都有对应的下标,类似数组,所以可以通过下标获取元素
  • 允许存储重复元素

张小飞:原来如此,那么它都有哪些实现类呢?

诸小亮:按照数据结构(存储数据的方式)划分,它的实现类有3个:

  • ArrayList:最经常用的子类,底层使用可变数组存储数据
  • LinkedList:底层使用链表存储数据
  • Vector:底层也是可变数组,但不常使用,因为效率比 ArrayList 低

2. List的特有方法

张小飞:我们刚才用的就是 ArrayList

诸小亮:是的,不过,在讲解它的实现类之前,我们先看一下 List 接口的特有方法

1. add(int index, E e)

add(int index, E e):往指定位置插入元素,比如:

public static void main(String[] args) throws Exception {
    List list = new ArrayList();
    list.add("王昭君");
    list.add("甄姬");
    list.add(0,"嫦娥");//往指定位置添加元素,0表示第一个位置
    System.out.println(list);
}

结果:image.png

张小飞:这个参数 E 是什么意思?

诸小亮:这是泛型的意思,我们之后会详细解释的

2. remove(int index)

remove(int index):删除指定位置的元素,并返回这个元素

image.png

结果:
image.png

3. get(int index)

get(int index):获取指定位置的元素,经常使用

image.png

结果:
image.png

张小飞:如果指定的 index 不存在呢?

诸小亮:跟数组一样,会报异常,比如:

image.png

结果:

image.png

4. set(int index, E e)

set(int index, E e):修改指定位置的元素

image.png

结果:
image.png

5. for循环

诸小亮:List 类型的集合,可以使用 for 循环遍历,比如:

public static void main(String[] args) throws Exception {
    ArrayList list = new ArrayList();
    list.add("王昭君");
    list.add("甄姬");
    list.add(0,"嫦娥");
    for(int i =0;i<list.size();i++){// 经常使用
        System.out.println(list.get(i));
    }
}

张小飞:原来还可以这样,我正想说用那个 Iterator 很不方便呢

诸小亮:确实,所以工作用我们一般都用 for 循环

6. ListIterator

诸小亮:ListIterator 是 List 集合的特有迭代器

张小飞:不是已经有 Iterator 了吗,怎么又来一个?

诸小亮:这是因为,在使用迭代器遍历 List 的时候,不能添加或删除元素,比如:
image.png

结果:

image.png

诸小亮:如果使用 List 特有迭代器就没问题

image.png

结果:

image.png

3. ArrayList

诸小亮:接下来我们介绍ArrayList——最常使用的列表容器

张小飞:看名字就知道,它的底层是数组结构

1. 可变数组

诸小亮:不错,它最大的特点就是——底层是可变数组

张小飞:数组有默认长度吧

诸小亮:是的,创建 ArrayList 对象时,其数组的默认长度是10

张小飞:我记得用可变数组——就是在数组存满后会创建一个新的更大的数组?

诸小亮:你说的不错,因此从指定位置插入和删除元素时候,要移动后面的所有元素,导致速度较慢

11.gif
张小飞:既然这么慢,为什么您说 ArrayList 是最常用的列表容器呢?

诸小亮:因为它通过下标获取元素的速度非常块,工作中大多数都是读,而不是写

张小飞:明白了

诸小亮,还有一点,因为是可变数组,所以会自动扩容

张小飞:嗯嗯,这个我明白

诸小亮:重点是——扩容就是创建一个新的数组,是原来数组长度的 1.5 倍

2. 多线程

诸小亮:需要注意,ArrayList方法不是同步的,所以多线程操作是不安全的

张小飞:明白,就是跟 StringBuilder 差不多

诸小亮,额。。。,是有些类似

3. 存储重复元素

诸小亮:另外, List 是可以存储重复元素的

image.png

结果:
image.png

张小飞:难道还有其他的不能存储重复元素?

诸小亮:是的,我们之后会讲到

4. 操作自定义对象

诸小亮:之前操作的都是一些基本类型对象,下面我们操作自定义对象,比如:

class Hero {
    String name;
) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

public class StringDemo {
    public static void main(String[] args) throws Exception {
        ArrayList list = new ArrayList();
        //1. 存储自定义对象
        list.add(new Hero("西施"));
        list.add(new Hero("嫦娥"));
        list.add(new Hero("甄姬"));
        //2. 循环获取每个对象
        for(int i =0;i<list.size();i++){
            Hero hero = (Hero) list.get(i);//默认获取的是Object类型,需要向下转型
            System.out.println(hero.getName());
        }
    }
}

结果:image.png

诸小亮,另外,获取元素时,也可以使用增强 for 循环

image.png
张小飞:操作自定义对象跟操作其他对象,没什么区别啊

诸小亮:是的

张小飞:那为什么要单独举个例子呢?

诸小亮:因为我们下面会用到

5. 去除重复元素

诸小亮:因为 ArrayList 可以存储重复元素,所以去重是工作中非常常见的场景

张小飞:这还不简单,它不是有个 contains 方法吗?用这个方法就可以了判断是否有重复元素了

class Hero {
    String name;

    public Hero(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    //复写 equals 方法,判断两个Hero对象是否一样
    public boolean equals(Object obj){
        if(obj instanceof Hero){
            // 如果 name 一样,就认为 两个 hero 对象相同
            return this.getName().equals(((Hero)obj).getName());
        }
        return false;
    }

    @Override
    public String toString() {
        return "Hero{" +
                "name='" + name + '\'' +
                '}';
    }
}

public class StringDemo {
    public static void main(String[] args) throws Exception {
        ArrayList list = new ArrayList();
        list.add(new Hero("西施"));
        list.add(new Hero("妲己"));
        list.add(new Hero("嫦娥"));
        list.add(new Hero("甄姬"));
        list.add(new Hero("女娲"));
        list.add(new Hero("芈月"));
        list.add(new Hero("妲己"));
        list.add(new Hero("甄姬"));

        //去重
        getSingelElement(list);
        System.out.println(list);
    }

    private static void getSingelElement(ArrayList list) {
        //1. 创建一个新的列表存储不重复的元素
        ArrayList newList = new ArrayList();
        newList.add(list.get(0));
        for (int i = 1; i < list.size(); i++) {
            //2. 判断newList是否包含指定元素,如果不包含就存起来
            Hero hero = (Hero) list.get(i);
            if(newList.contains(hero)){
                //contains内部会调用元素的equals方法,判断你是否一样,所以Hero中要定义equals方法
                continue;
            }
            newList.add(hero);
        }
        list.clear();//清空老的数据
        list.addAll(newList);//放入新的数据
    }
}

诸小亮:不错,没想到你还真是考虑到在 Hero 中复写 equals 方法了

张小飞:嘿嘿,您之前不是说过嘛,contains 的内部就是调用了元素的 equals 方法

4. LinkedList

诸小亮:接着,我们说LinkedList——也是 List 接口的具体实现类之一,但工作中使用不是很多

链表

张小飞:LinkedList 底层也是用的数组吗?

诸小亮:非也,LinkedList 底层使用的是链表

  • 链表是一种数据结构:前面节点存储着下一个节点的地址,比如:

image.png
张小飞:这么说,只要拿到第一个节点,就能获取之后的所有节点了,

诸小亮:不错,不过这只是:单向链表,而 LinkedList 底层是双向链表

张小飞:什么是双向链表?

诸小亮:来,看下面这个图

image.png
张小飞:明白了,就是每个元素不仅存放着下一个元素的地址,也存放着前一个元素的地址

诸小亮:没错,因为这种特性所以链表在插入和删除的时候,效率很高,比如:

11.gif

张小飞:嗯嗯,确实,插入时只需要修改 next 属性就行了

张小飞:不过,LinkedList 的 get() 方法效率应该很慢比 ArrayList 慢

诸小亮:是的,比如 get(5),就意味着从头节点开始不断的向后找5次,才能获取到对应的数据

image.png

诸小亮:另外,链表跟数组最大的区别:数组在内存中是一块儿连续的内存,链表不是

张小飞:明白

诸小亮:LinkedList 还有一些其他特点,作为了解

  • 可以存储重复元素
  • 多线程下也是不安全的

2. 特有方法

诸小亮:LinkedList 也有一些自己特有的方法

1. addFirst

addFirst:把元素当成头节点加入链表中

public static void main(String[] args) throws Exception {
    LinkedList list = new LinkedList();
    list.add(new Hero("西施"));
    list.add(new Hero("妲己"));
    list.addFirst(new Hero("嫦娥"));//把 嫦娥 作为头节点

    System.out.println(list.getFirst());//获取头节点
}

结果:
image.png

2. removeFirst

removeFirst:删除头节点,并返回头节点元素

image.png

结果:

image.png

3. 遍历

诸小亮:因为 get(int index) 方法的效率很差,所以不推荐使用一般的 for 循环

image.png
张小飞:那,使用 Iterator 吗?

诸小亮:no,no,no,可以用 增强 for 循环

image.png
张小飞:这是为什么呢?

诸小亮:使用普通 for 循环就是根据下标获取元素,之前已经说了,效率很慢

张小飞:使用增强 for 循环就快了?

诸小亮:是的,而使用增强 for 循环不需要下标,而且更加简洁、易读

张小飞:明白了

诸小亮:但是,增强 for 循环在遍历时,无法对容器进行修改操作否则报错,你可以尝试一下

5. Vector

张小飞:这个 Vector 是???

诸小亮:Vector 是最早的一个集合类,底层也是可变数组

张小飞:这不是跟 ArrayList 一样吗?

诸小亮:非也,Vector 中都是同步方法,所以 ArrayList 是它的替代品

image.png

张小飞:原来如此

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值