Java讲课笔记21:List接口及其实现类

文章目录

零、本讲学习目标

1、了解集合的概念和分类

2、了解Collection接口和List接口中的常用方法

3、掌握ArrayList集合和LinkedList集合的使用

4、会用Iterator与foreach循环

一、集合概述

1、引入集合的必要性

  • 数组局限性:数组可以保存多个对象,但在无法确定需要保存多少个对象时,数组将不再适用,因为数组的长度不可变。
  • 可能的场景:倘若要保存一个学校的学生信息,由于不停有新生来报道,同时也有学员毕业离开学校,这时学生的数目无法固定,并且随时可能变动。
  • 解决的方法:为了保存这些数目不确定的对象,Java中提供了集合,集合可以存储任意类型的对象,并且长度可变。

2、集合的定义

Java中的集合就像一个容器,专门用来存储Java对象。集合对象可以是任意的数据类型,并且长度可变。其中,这些集合类都位于java.util包中,在使用时一定要注意导包的问题。

还记得我们在《Java讲课笔记08:数组》里讲利用Arrays类的sort()方法对数组进行降序排列的案例Example810吧。
在这里插入图片描述
这个案例使用集合类Collections的reverseOrder()方法作为Arrays类的sort()方法的第二个参数,用于实现对数组的降序排列。大家可以看看程序开头,就有导包的语句:
在这里插入图片描述

3、集合的分类

(1)单列集合Collection

  • 单列集合根接口,用于存储一系列符合某种规则的元素。
  • Collection集合有两个重要的子接口,分别是List和Set。
  • List集合的特点是元素有序、可重复。该接口的主要实现类有ArrayList和LinkedList。
  • Set集合的特点是元素无序并且不可重复。该接口的主要实现类有HashSet和TreeSet。

(2)双列集合Map

  • 双列集合根接口,用于存储具有键(Key)、值(Value)映射关系的元素。
  • Map集合中每个元素都包含一对键值,并且Key唯一,在使用Map集合时通过指定的Key找到对应的Value。
  • Map接口的主要实现类有HashMap和TreeMap。

4、集合体系核心结构

在这里插入图片描述
说明:虚线框里填写的都是接口类型,实线框里填写的都是具体的实现类。

二、Collection接口

1、Collection接口概述

Collection是单列集合根接口,用于存储一系列符合某种规则的元素,它有两个重要的子接口,分别是List和Set。

2、Collection接口常用方法

方法声明 功能描述
boolean add(Object o) 向集合中添加一个元素
boolean addAll(Collection c) 将指定集合c中的所有元素添加到该集合中
void clear() 删除该集合中的所有元素
boolean remove(Object o) 删除该集合中指定的元素
boolean removeAll(Collection c) 删除该集合中包含指定集合c中的所有元素
boolean isEmpty() 判断该集合是否为空
boolean contains(Object o) 判断该集合中是否包含某个元素
boolean containsAll(Collection c) 判断该集合中是否包含指定集合c中的所有元素
Iterator iterator() 返回在该集合的元素上进行迭代的迭代器(Iterator),用于遍历该集合所有元素
int size() 获取该集合元素个数
Stream stream() 将集合源转换为有序元素的流对象(JDK 8新方法)

三、List接口

1、List接口概述

(1)List接口定义

List接口继承自Collection接口,是单列集合的一个重要分支,习惯性地会将实现了List接口的对象称为List集合。

(2)List接口特点

  • List集合中允许出现重复元素,所有的元素是以一种线性方式进行存储的,在程序中可以通过索引(类似于数组中的元素下标)来访问集合中的元素。
  • List集合还有一个特点就是元素有序,即元素的存入顺序和取出顺序一致。

(3)List接口常用方法

方法声明 功能描述
void add(int index,Object element) 将元素element插入在List集合的指定索引位置
boolean addAll(int index,Collection c) 将集合c包含的所有元素插入到List集合的指定索引位置
Object get(int index) 返回集合索引index处的元素
Object remove(int index) 删除index索引处的元素
Object set(int index, Object element) 将索引index处元素替换成element元素,并将替换后的元素返回
int indexOf(Object o) 返回对象o在List集合中首次出现的位置索引
int lastIndexOf(Object o) 返回对象o在List集合中最后一次出现的位置索引
List subList(int fromIndex, int toIndex) 返回从索引fromIndex(包括)到 toIndex(不包括)处所有元素集合组成的子集合
Object[] toArray() 将集合元素转换为数组
default void sort(Comparator<? super E> c) 根据指定的比较器规则对集合元素排序(JDK 8新方法)

2、List接口常用实现类

在这里插入图片描述

(1)ArrayList集合

ArrayList是List接口的一个实现类,它是程序中最常见的一种集合,其内部的数据存储结构是数组形式。

(2)LinkedList集合

LinkedList是List接口的另一个实现类,其内部包含有两个Node类型的first和last属性的双向循环链表结构。

(3)Vector集合

Vector 类实现了一个动态数组。和 ArrayList 很相似,但是两者是不同的:

  • Vector 是同步访问的
  • Vector 包含了许多传统的方法,这些方法不属于集合框架

四、ArrayList集合

1、ArrayList集合定义

ArrayList是List接口的一个实现类,它是程序中最常见的一种集合,其内部的数据存储结构是数组形式。

2、ArrayList集合特点

  • 由于ArrayList的存储结构,在增加或删除指定位置的元素时,会创建新的数组,效率比较低,因此不适合做大量的增删操作。
  • 这种数组结构允许程序通过索引的方式来访问元素,使用ArrayList集合在遍历和查找元素时显得非常高效。

3、案例演示:创建与操作城市数组列表

  • 创建Example2101
    在这里插入图片描述
package net.hw.lesson21;

import java.util.ArrayList;

/**
 * 功能:创建与操作城市数组列表
 * 作者:华卫
 * 日期:2020年05月22日
 */
public class Example2101 {
    public static void main(String[] args) {
        // 创建城市数组列表对象
        ArrayList cities = new ArrayList();
        // 判断数组列表是否为空
        System.out.println("cities.isEmpty() = " + cities.isEmpty());
        // 向数组列表里添加元素
        cities.add("北京");
        cities.add("上海");
        cities.add("深圳");
        cities.add(2, "广州"); // 广州在列表第3个位置
        cities.add("泸州");
        // 输出整个数组列表
        System.out.println("cities = " + cities);
        // 获取数组列表的长度
        System.out.println("cities.size() = " + cities.size());
        // 采用for循环遍历数组列表
        for (int i = 0; i < cities.size(); i++) {
            System.out.println("cities[" + i + "] = " + cities.get(i));
        }
        // 修改指定元素,比如将第5个元素改成“南京”
        cities.set(4, "南京");
        // 输出整个数组列表
        System.out.println("cities = " + cities);
        // 元素在列表的位置
        String city = "深圳";
        System.out.println("[" + city + "]在列表中的下标 = " + cities.indexOf(city));
        // 按下标删除元素,比如删除下标为2的元素
        cities.remove(2);
        System.out.print("删除列表中下标为2的元素:");
        // 输出整个数组列表
        System.out.println("cities = " + cities);
        // 按对象删除元素,比如删除“南京”
        cities.remove("南京");
        System.out.print("删除列表中的[南京]元素:");
        // 输出整个数组列表
        System.out.println("cities = " + cities);
        // 判断数组列表是否为空
        System.out.println("cities.isEmpty() = " + cities.isEmpty());
    }
}
  • 运行程序,查看结果
    在这里插入图片描述

4、简要说明

  • 必须导包:import java.util.ArrayList;
  • 定义数组列表对象最好加上泛型:ArrayList<String> cities = new ArrayList<>();表名数组列表里的每个元素都是字符串类型,这样可以杜绝安全隐患
  • 数组列表添加元素有两个重载的方法
    (1)public boolean add(E e):默认在列表末尾添加新元素
    (2)public void add(int index, E element):在列表指定位置添加新元素
  • 设置或获取数组列表元素,传入的下标不存在,会抛出下标越界异常
    在这里插入图片描述
  • 可以采用foreach循环遍历数组列表
    在这里插入图片描述

5、课堂练习:创建与操作成绩数组列表

  • 创建成绩数组列表scores,泛型为Integer
  • 添加8个成绩:67、89、90、35、54、80、69、86
  • 采用for循环与foreach循环遍历成绩数组列表
  • 将成绩数组列表排序输出(利用sort()方法,可参看第08讲的Example810)
    在这里插入图片描述

五、LinkedList集合

1、LinkedList集合定义

LinkedList是List接口的另一个实现类,其内部包含有两个Node类型的first和last属性的双向循环链表结构。

2、LinkedList集合特点

  • 由于LinkedList的存储结构, 对于元素的遍历和查找效率较低。
  • LinkedList集合对于元素的增删操作表现出很高的效率。

3、双向循环链表结构

在这里插入图片描述

  • 左图为新增元素,图中的元素1和元素2在集合中彼此为前后关系,在它们之间新增一个元素时,只需要让元素1记住它后面的元素是新元素,让元素2记住它前面的元素为新元素就可以了。
  • 右图为删除元素,要想删除元素1和元素2之间的元素3,只需要让元素1与元素2变成前后关系就可以了。

4、LinkedList的特有方法

方法声明 功能描述
void add(int index, E element) 在此列表中指定的位置插入指定的元素。
void addFirst(Object o) 将指定元素插入集合的开头
void addLast(Object o) 将指定元素添加到集合的结尾
Object getFirst() 返回集合的第一个元素
Object getLast() 返回集合的最后一个元素
Object removeFirst() 移除并返回集合的第一个元素
Object removeLast() 移除并返回集合的最后一个元素
boolean offer(Object o) 将指定元素添加到集合的结尾
boolean offerFirst(Object o) 将指定元素添加到集合的开头
boolean offerLast(Object o) 将指定元素添加到集合的结尾
Object peek() 获取集合的第一个元素
Object peekFirst() 获取集合的第一个元素
Object peekLast() 获取集合的最后一个元素
Object poll() 移除并返回集合的第一个元素
Object pollFirst() 移除并返回集合的第一个元素
Object pollLast() 移除并返回集合的最后一个元素
void push(Object o) 将指定元素添加到集合的开头
Object pop() 移除并返回集合的第一个元素

5、案例演示:创建与操作学生链表

  • 创建Example2102
    在这里插入图片描述
package net.hw.lesson21;

import java.util.LinkedList;

/**
 * 功能:创建与操作学生链表
 * 作者:华卫
 * 日期:2020年05月23日
 */
public class Example2102 {
    public static void main(String[] args) {
        // 创建学生链表(采用泛型更加安全)
        LinkedList<String> students = new LinkedList<>();
        // 判断学生链表是否为空
        System.out.println("students.isEmpty(): " + students.isEmpty());

        // 添加三个元素
        System.out.print("链表添加三个元素:");
        students.add("李晓红");
        students.add("吴文燕");
        students.add(0, "王晓刚");
        // 输出这个链表
        System.out.println(students);
        // 在链表头添加元素
        System.out.print("在链表头添加元素:");
        students.push("毛晓玲");
        // 输出整个链表
        System.out.println(students);
        // 在链表尾添加元素
        System.out.print("在链表尾添加元素:");
        students.offer("郑小翠");
        // 输出整个链表
        System.out.println(students);

        // 获取第一个元素
        System.out.println("链表第一个元素:" + students.getFirst()); // 等价于 students.peek()、students.peekFirst()

        // 获得最后一个元素
        System.out.println("链表最后一个元素:" + students.getLast()); // 等价于 students.peekLast()

        // 获取指定位置的元素
        int index = 2;
        System.out.println("students[" + index + "] = " + students.get(index));

        // 遍历学生链表
        System.out.print("采用for循环遍历链表:");
        for (int i = 0; i < students.size(); i++) {
            System.out.print(students.get(i) + " ");
        }
        System.out.println();

        System.out.print("采用foreach循环遍历链表:");
        for (String student : students) {
            System.out.print(student + " ");
        }
        System.out.println();

        // 删除第一个元素
        System.out.print("删除第一个元素:");
        students.remove(0); // 等价于 students.removeFirst()、students.poll()、students.pollFirst()
        // 输出整个链表
        System.out.println(students);
        // 删除最后一个元素
        System.out.print("删除最后一个元素:");
        students.pollLast(); // 等价于 students.remove(students.size() - 1)
        // 输出整个链表
        System.out.println(students);
        // 删除下标为1的元素
        System.out.print("删除下标为1的元素:");
        students.remove(1);
        // 输出整个链表
        System.out.println(students);
        // 删除全部链表元素
        students.clear();
        System.out.println("删除全部元素:" + students);
    }
}
  • 运行程序,查看结果
    在这里插入图片描述
  • 思考题:如何反序输出链表的全部元素?

六、Collection集合遍历

(一)Iterator遍历集合

1、Iterator接口定义

Iterator接口是Java集合框架中的一员,主要用于迭代访问(即遍历)Collection中的元素,因此Iterator对象也被称为迭代器。

2、案例演示:利用迭代器遍历学生数组列表

  • 创建Example2103
    在这里插入图片描述
package net.hw.lesson21;

import java.util.ArrayList;
import java.util.Iterator;

/**
 * 功能:利用迭代器遍历学生数组列表
 * 作者:华卫
 * 日期:2020年05月23日
 */
public class Example2103 {
    public static void main(String[] args) {
        // 创建学生数组列表
        ArrayList<String> students = new ArrayList<>();
        // 添加列表元素
        students.add("李晓红");
        students.add("郑晓彤");
        students.add("邱子涵");
        students.add("吴晓燕");
        students.add("董文刚");
        // 由列表获取迭代器
        Iterator iterator = students.iterator();
        // 利用迭代器遍历数组列表
        while (iterator.hasNext()) { // 判断迭代器是否有下一个元素
            // 获取迭代器下一个元素
            Object object = iterator.next();
            System.out.print(object + " ");
        }
    }
}
  • 运行程序,查看结果
    在这里插入图片描述

3、Iterator遍历集合的工作原理

在这里插入图片描述

  • Iterator遍历集合时,内部采用指针的方式来跟踪集合中的元素。在调用next()方法之前,索引位于第一个元素之前,不指向任何元素。
  • 第一次调用next()方法后,索引会向后移动一位,指向第一个元素并将该元素返回;
  • 再次调用next()方法时,索引会指向第二个元素并将该元素返回;
  • 以此类推,直到hasNext()方法返回false,表示到达了集合的末尾终止对元素的遍历。

4、脚下留心

Iterator迭代器对集合中的元素进行迭代时,如果调用了集合对象的remove()方法删除元素,会抛出ConcurrentModificationException(并发修改异常)。

  • 创建Example2106
    在这里插入图片描述
package net.hw.lesson21;

import java.util.ArrayList;
import java.util.Iterator;

/**
 * 功能:演示并发修改异常
 * 作者:华卫
 * 日期:2020年05月23日
 */
public class Example2106 {
    public static void main(String[] args) {
        ArrayList<String> names = new ArrayList<>();

        names.add("李香兰");
        names.add("陈慧娴");
        names.add("童安格");
        names.add("谭咏麟");
        System.out.println("姓名列表:" + names);

        Iterator<String> iterator = names.iterator();
        while (iterator.hasNext()) {
            String name = iterator.next();
            if (name.equals("童安格")) {
                names.remove(name);
            }
        }
        System.out.println("姓名列表:" + names);
    }
}
  • 运行程序,查看结果
    在这里插入图片描述
    可是并没有抛出ConcurrentModificationException异常,这是怎么回事呢?

(二)foreach遍历集合

1、foreach循环定义

foreach循环是一种更加简洁的for循环,也称增强for循环,用于遍历数组或集合中的元素。

2、foreach循环语法格式

for (容器中元素类型 临时变量 :容器变量) {
     // 执行语句
}

比如:

for (Object obj : list) {	
	System.out.println(obj);
} 	

3、案例演示:利用增强for循环遍历学生数组列表

  • 创建Example2104
    在这里插入图片描述
package net.hw.lesson21;

import java.util.ArrayList;

/**
 * 功能:利用增强for循环遍历学生数组列表
 * 作者:华卫
 * 日期:2020年05月23日
 */
public class Example2104 {
    public static void main(String[] args) {
        // 创建学生数组列表
        ArrayList<String> students = new ArrayList<>();
        // 添加列表元素
        students.add("李晓红");
        students.add("郑晓彤");
        students.add("邱子涵");
        students.add("吴晓燕");
        students.add("董文刚");
        // 利用增强for循环遍历数组列表
        for (String student : students) {
            System.out.print(student + " ");
        }
    }
}
  • 运行程序,查看结果
    在这里插入图片描述

4、脚下留心

foreach循环遍历集合和数组时,只能访问集合中的元素,不能对其中的元素进行修改。

  • 创建Example2104
    在这里插入图片描述
package net.hw.lesson21;

import java.util.ArrayList;

/**
 * 功能:增强for循环不能修改集合元素
 * 作者:华卫
 * 日期:2020年05月23日
 */
public class Example2105 {
    public static void main(String[] args) {
        ArrayList<String> names = new ArrayList<>();

        names.add("李香兰");
        names.add("陈慧娴");
        names.add("童安格");
        names.add("谭咏麟");
        System.out.println("姓名列表:" + names);
      
        // 利用增强for循环遍历数组
        for (String name : names) {
            name = "萌萌哒";
        }
        System.out.println("增强for循环修改后的列表:" + names);

        // 利用普通for循环遍历数组
        for (int i = 0; i < names.size(); i++) {
            names.set(i, "萌萌哒");
        }
        System.out.println("普通for循环修改后的列表:" + names);
    }
}
  • 运行程序,查看结果
    在这里插入图片描述
  • 解释说明:从运行结果可以看出来,增强for循环并不能修改列表中元素的值。其原因是第22行代码中的name = "萌萌哒";只是将临时变量name指向了一个新的字符串,这与列表中的元素没有一点关系,而在普通for循环里,可以通过索引来修改列表中指定元素的值。

(三)在JDK 8里用forEach遍历集合

1、针对所有集合都有效的遍历

(1)遍历方法

forEach(Consumer action)方法是JDK 8中新增的遍历集合元素的方法,根据Lambda表达式特性,该方法所需要的参数是一个函数式接口。比如list.forEach(obj -> System.out.println("迭代集合元素:" + obj));

(2)案例演示:遍历姓名数组列表

  • 创建Example2107
    在这里插入图片描述
package net.hw.lesson21;

import java.util.ArrayList;

/**
 * 功能:演示forEach遍历集合
 * 作者:华卫
 * 日期:2020年05月24日
 */
public class Example2107 {
    public static void main(String[] args) {
        ArrayList<String> names = new ArrayList<>();

        names.add("李香兰");
        names.add("陈慧娴");
        names.add("童安格");
        names.add("谭咏麟");
        System.out.println("姓名列表:" + names);

        // 使用JDK8新增的forEach(Consumer action)方法遍历集合
        System.out.print("采用forEach方法遍历:");
        names.forEach(name -> System.out.print(name + " "));
    }
}
  • 运行程序,查看结果
    在这里插入图片描述
  • 简要说明:使用列表的forEach(Consumer action)方法遍历集合,该方法传入一个Lambda表达式形式书写的函数式接口,在执行时,会自动遍历集合元素,并将元素逐个传递给Lambda表达式的形参。

2、针对迭代器提供的遍历方法

(1)遍历方法

JDK 8中还针对Iterator迭代器对象提供了一个forEachRemaining(Consumer action)方法来进行遍历,该方法同样需要一个函数式接口。

Iterator it = list.iterator();
it.forEachRemaining(obj -> System.out.println("迭代集合元素:" + obj));

(2)案例演示:遍历姓名数组列表

  • 创建Example2108
    在这里插入图片描述
package net.hw.lesson21;

import java.util.ArrayList;
import java.util.Iterator;

/**
 * 功能:演示forEachRemaining遍历迭代器
 * 作者:华卫
 * 日期:2020年05月24日
 */
public class Example2108 {
    public static void main(String[] args) {
        ArrayList<String> names = new ArrayList<>();

        names.add("李香兰");
        names.add("陈慧娴");
        names.add("童安格");
        names.add("谭咏麟");
        System.out.println("姓名列表:" + names);

        // 获取数组列表的迭代器
        Iterator<String> iterator = names.iterator();
        // 使用JDK8新增的forEachRemaining(Consumer action)方法遍历迭代器
        System.out.print("采用forEachRemaining方法遍历:");
        iterator.forEachRemaining(name -> System.out.print(name + " "));
    }
}
  • 运行程序,查看结果
    在这里插入图片描述

3、小结List集合的遍历

遍历List集合有三种方法:

  • foreach循环遍历集合
  • forEach()方法遍历集合
  • forEachRemaining()方法遍历集合

这三种方法,对于单例集合Collection都是有效的,比如下一讲我们要学习的Set集合,也可以用上述三种方法进行遍历。

猜你喜欢

转载自blog.csdn.net/howard2005/article/details/106271820