从01开始 从01开始
首页
  • 计算机科学导论
  • 数字电路
  • 计算机组成原理

    • 计算机组成原理-北大网课
  • 操作系统
  • Linux
  • Docker
  • 计算机网络
  • 计算机常识
  • Git
  • JavaSE
  • Java高级
  • JavaEE

    • Ant
    • Maven
    • Log4j
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • Servlet
  • Spring
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC
  • SpringBoot
  • 学习网课的心得
  • 输入法
  • 节假日TodoList
  • 其他
  • 关于本站
  • 网站日记
  • 友人帐
  • 如何搭建一个博客
GitHub (opens new window)

peterjxl

人生如逆旅,我亦是行人
首页
  • 计算机科学导论
  • 数字电路
  • 计算机组成原理

    • 计算机组成原理-北大网课
  • 操作系统
  • Linux
  • Docker
  • 计算机网络
  • 计算机常识
  • Git
  • JavaSE
  • Java高级
  • JavaEE

    • Ant
    • Maven
    • Log4j
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • Servlet
  • Spring
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC
  • SpringBoot
  • 学习网课的心得
  • 输入法
  • 节假日TodoList
  • 其他
  • 关于本站
  • 网站日记
  • 友人帐
  • 如何搭建一个博客
GitHub (opens new window)
  • JavaSE

  • JavaSenior

    • 反射

    • 注解

    • 集合类

      • 集合介绍
      • List
        • List常用方法
        • of​方法
        • 遍历List
        • List和Array转换
        • equals方法
        • 推荐阅读
      • Map
      • Set
      • Queue
      • Stack
      • Collections
    • Java
  • JavaEE

  • JavaWeb

  • Spring

  • 主流框架

  • SpringMVC

  • SpringBoot

  • Java并发

  • Java源码

  • JVM

  • 韩顺平

  • Java
  • Java
  • JavaSenior
  • 集合类
2022-12-31
目录

List

# 01.List

在集合类中,List​是最基础的一种集合,可以理解为一种有序的线性表。

在数据结构里我们学过线性表,如果我们要往数组里添加或删除某个元素,需要挪动其他位置的元素;如果空间不够了,还要扩容。List已经自动帮我们实现了这些,我们直接用就可以了,不用关心其内部怎么实现。

List只是一个接口,常见的实现类有ArrayList​(用的最多,也推荐用)和LinkedList​,LinkedList​就相当于链表,两者的区别跟 数组和链表的区别一样。

​List​接口允许我们添加重复的元素,还运行添加null。

‍

‍

‍

# List常用方法

  • 在末尾添加一个元素:boolean add(E e)​
  • 在指定索引添加一个元素:boolean add(int index, E e)​
  • 删除指定索引的元素:E remove(int index)​
  • 删除某个元素:boolean remove(Object e)​
  • 获取指定索引的元素:E get(int index)​
  • 获取链表大小(包含元素的个数):int size()​
  • 是否含有某个元素:boolean contains(Object e)​
  • 返回某个元素的索引:int indexOf(Object o)​。如果元素不存在,就返回-1​。

‍

List<String> myWaves = new ArrayList<>();
myWaves.add("爱莉希亚");
myWaves.add("布洛尼娅");
myWaves.add(0,"梅比乌斯");
String ss = myWaves.get(0);
System.out.println(ss);
System.out.println("myWaves.size(): " + myWaves.size());
1
2
3
4
5
6
7

‍

运行结果:

梅比乌斯
myWaves.size(): 3
1
2

‍

‍

# of​方法

自Java9之后,可以通过List​接口提供的of()​方法,根据给定元素快速创建List​:

List<Integer> list = List.of(1, 2, 5);
1

该方法最多支持10个元素。

‍

注意事项:

  • 返回的只是一个只读List​,调用add()​、remove()​方法会抛出UnsupportedOperationException​。
  • List.of()​方法不接受null​值,如果传入null​,会抛出NullPointerException​异常。

‍

‍如果使用Java8,可以使用Arrays.asList方法来快速创建:

List<Integer> list = Arrays.asList(1, 2, 3);
1

Arrays.asList() 方法是 Arrays 的静态方法。这种方式构造的 List 是固定长度的,如果调用 add 方法增加新的元素时会报异常 java.lang.UnsupportedOperationException。这种方式仅适用于构造静态不变的 List。

# 遍历List

遍历可以说是集合类最常用的方法了。我们可以用之前学过的遍历数组的方法来遍历:

for (int i = 0; i < myWaves.size(); i++) 
  System.out.println(myWaves.get(i));
1
2

但这种方式并不推荐,一是代码复杂,二是因为get(int)​方法只有ArrayList​的实现是高效的,换成LinkedList​后,索引越大,访问速度越慢。

所以我们要始终坚持使用迭代器Iterator​来访问List​。Iterator​本身也是一个对象,但它是由List​的实例调用iterator()​方法的时候创建的。Iterator​对象知道如何遍历一个List​,并且不同的List​类型,返回的Iterator​对象实现也是不同的,但总是具有最高的访问效率。

​Iterator​对象有两个方法:boolean hasNext()​判断是否有下一个元素,E next()​返回下一个元素。因此,使用Iterator​遍历List​代码如下:

for(Iterator<String> it = myWaves.iterator(); it.hasNext(); ){
      System.out.println(it.next());
}
1
2
3

‍

有童鞋可能觉得使用Iterator​访问List​的代码比使用索引更复杂。但是,要记住,通过Iterator​遍历List​永远是最高效的方式。并且,由于Iterator​遍历是如此常用,所以,Java的for each​循环本身就可以帮我们使用Iterator​遍历。把上面的代码再改写如下:

for (String s : myWaves) {
  System.out.println(s);
}
1
2
3

实际上,只要实现了Iterable​接口的集合类都可以直接用for each​循环来遍历,Java编译器本身并不知道如何遍历集合对象,但它会自动把for each​循环变成Iterator​的调用,原因就在于Iterable​接口定义了一个Iterator<E> iterator()​方法,强迫集合类必须返回一个Iterator​实例。

‍

# List和Array转换

把List​变为Array​有三种方法,

  • 调用Object[] toArray()​方法直接返回一个数组:Object[] array = list.toArray();​ 。但这样会丢失类型信息,较少用。
  • 给toArray()​传入一个类型相同的Array:Integer[] array = list.toArray(new Integer[3]);​

如果我们传入的数组大小和List​实际的元素个数不一致怎么办?根据List接口 (opens new window)的文档,我们可以知道:

如果传入的数组不够大,那么List​内部会创建一个新的刚好够大的数组,填充后返回;如果传入的数组比List​元素还要多,那么填充完元素后,剩下的数组元素一律填充null​。

实际上,最常用的是传入一个“恰好”大小的数组:

Integer[] array = list.toArray(new Integer[list.size()]);
1

‍


把Array​变为List​就简单多了,通过List.of(T...)​方法最简单:

Integer[] array = { 1, 2, 3 };
List<Integer> list = List.of(array);
1
2

对于JDK 11之前的版本,可以使用Arrays.asList(T...)​方法把数组转换成List​。

‍

‍

# equals方法

前面我们简单演示了下 contains()​方法,其内部是用equals()​方法判断两个元素是否相等(indexOf()​也是用equals()​来查找的)。如果我们存放的类型没有实现equals()​方法,那么contains()​方法是无效的,我们来演示下。

首先创建一个Person类,里面没有实现equals方法:

class Person {
  public String name;
  public int age;

  Person(String name, int age){
    this.name = name;
    this.age = age;
  }
}
1
2
3
4
5
6
7
8
9

‍

然后测试contains()​:

    List<Person> myWaves = new ArrayList<>();
    myWaves.add(new Person("雷姆", 18));
    myWaves.add(new Person("拉姆", 18));
    myWaves.add(new Person("艾米莉雅", 18));

    boolean b = myWaves.contains(new Person("雷姆", 18));
    System.out.println(b);  //false
1
2
3
4
5
6
7

可以看到,虽然我们创建了一样的Person类(name和age相同),但是contains()​返回的是false

因此,要正确使用List​的contains()​、indexOf()​这些方法,放入的实例必须正确覆写equals()​方法,否则,放进去的实例,查找不到

至于实例是否equals是否相同,则是我们自己定义的逻辑。我们可以认为名字相同既是同一人,也可以认为名字和年龄都一样才是同一人。我们完善Person类如下:

class Person {
  public String name;
  public int age;

  Person(String name, int age){
    this.name = name;
    this.age = age;
  }

  public boolean equals(Object o){
    if(o instanceof Person){
      Person p = (Person) o;
      return this.name.equals(p.name) && this.age == p.age;
    }
    return false;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

‍

此时我们再使用contains()​方法,就可以判断是否存在某个实例了。

‍

‍

不过,此时的方法还不完善:

  1. 如果this.name为null,会报错空指针异常
  2. 如果有多个引用类型,那么判空的代码就更多了,可以使用Objects.equals()​静态方法。

‍

‍

‍

# 推荐阅读

LinkedList 的作者说他自己都不用 LinkedList? (opens new window)

在GitHub上编辑此页 (opens new window)
上次更新: 2023/3/2 09:47:04
集合介绍
Map

← 集合介绍 Map→

Theme by Vdoing | Copyright © 2022-2023 粤ICP备2022067627号-1 粤公网安备 44011302003646号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式