news 2026/2/22 21:33:32

Java进阶06List集合泛型

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java进阶06List集合泛型

Java进阶06 集合

一、集合及其体系结构

集合是一个长度可变的容器

1、集合的体系结构
1.1 单列集合
  • 单列集合使用add()方法添加集合元素,一次只能添加一个元素。

  • 单列集合均实现了Collection接口,该接口还有两个子接口List和Set。

    • List接口

      List集合的特点是存取有序、有索引、可以存储重复的;包含ArrayList、LinkedList两个集合

    • Set接口

      Set集合的特点是存取无序、没有索引、不可以存储重复的;包含TreeSet、HashSet、LinkedHashSet

1.2 双列集合
  • 双列集合使用put()方法添加集合元素,一次可以添加两个元素

  • 双列集合均实现了Map接口

  • 双列集合包括:TreeMap、HashMap、LinkedHashMap

二、Collection的使用

方法名

说明

public booleanadd(E e)

把给定的对象添加到当前集合中,返回是否添加成功

public voidclear()

清空集合中所有的元素

public booleanremove(E e)

把给定的对象在当前集合中删除,返回是否删除成功

public booleancontains(Object obj)

判断当前集合中是否包含给定的对象

public booleanisEmpty()

判断当前集合是否为空

public intsize()

返回集合中元素的个数/集合的长度

注意事项:

  • remove()、contains()底层都是依赖equals方法

  • clear()是清空集合中所有元素,不是销毁集合容器。清空后还是可以继续往集合中添加元素的

三、集合遍历方式(5种)

1、普通for循环
ArrayList<String> list = new ArrayList<>(); ? for (int i = 0; i < list.size(); i++) { ? ?String s = list.get(i); }
2、迭代器遍历
2.1 Collection接口的方法

方法

说明

public Iteratoriterator()

获取遍历集合的迭代器对象

public booleanhasNext()

判断集合中是否还有元素

public Enext()

取出集合中元素,并且将指针向后移动一位

2.2 迭代器遍历
public class CollectionDemo2 { ? ?public static void main(String[] args) { ? ? ? ?//多态创建集合容器,左边为接口引用,右边为实现类对象 ? ? ? ?Collection<Student> c = new ArrayList<>(); ? ? ? ? ? ? ? ?c.add(new Student("张三", 23)); ? ? ? ?c.add(new Student("李四", 24)); ? ? ? ?c.add(new Student("王五", 25)); ? ? ? ? ? ? ? ?// 1. 获取迭代器 其实这句代码相当于 Iterator<Student> it = new Itr(); ? ? ? ?Iterator<Student> it = c.iterator(); ? ? ? ?// 2. 循环的判断, 集合中是否还有元素 ? ? ? ?while (it.hasNext()) { ? ? ? ? ? ?// 3. 通过迭代器取出集合的元素 ? ? ? ? ? ?Student stu = it.next(); ? ? ? ? ? ?System.out.println(stu.getName() + "---" + stu.getAge()); ? ? ? ? ? ? ? ? ? ? ? ?//这样调用会出现信息错乱!!! ? ? ? ? ? ?System.out.println(it.next().getName() + "---" + it.next().getAge()); ? ? ? } ? } }

**注意:**next()方法每调用一次,迭代器指针会后移一位,就会把不同集合元素的信息拼接到一起打印,为了避免这种信息错乱,建议在循环中,next()只调用一次

2.3 迭代器源码分析
private class Itr implements Iterator<E> { ? ?//定义游标,表示指针指向 ? ?int cursor; ? ? ? ?public boolean hasNext() { ? ? ? ?//判断指针值是否等于集合长度 ? ? ? ?return cursor != size; ? ? ? } ? ? ? ? ?public E next() { ? ? ? ?//定义i变量记录当前指针所指向的元素下标 ? ? ? ?int i = cursor; ? ? ? ? ? ? ? ?//指针后移 ? ? ? ?cursor = i + 1; ? ? ? ? ? ? ? ?//返回i队应下标所记录的元素值 ? ? ? ?return (E) elementData[lastRet = i]; ? ? } }
3、增强for循环

增强for循环是JDK5之后出现的,其内部原理就是一个Iterator迭代器,它简化迭代器的代码书写,是迭代器遍历的语法糖。

3.1 格式
for(元素的数据类型 变量名 : 数据或者集合){ }

快捷键需要迭代的集合.for再回车

3.2 Demo
public class CollectionDemo3 { ? ?public static void main(String[] args) { ? ? ? ?Collection<Student> c = new ArrayList<>(); ? ? ? ?c.add(new Student("张三", 23)); ? ? ? ?c.add(new Student("李四", 24)); ? ? ? ?c.add(new Student("王五", 25)); ? ? ? ? ?//增强for循环遍历集合 ? ? ? ?for (Student stu : c) { ? ? ? ? ? ?System.out.println(stu); ? ? ? } ? ? ? ? ?System.out.println("----------------------"); ? ? ? ? ?//增强for循环遍历数组 ? ? ? ?int[] nums = {11, 22, 33}; ? ? ? ?for (int num : nums) { ? ? ? ? ? ?System.out.println(num); ? ? ? } ? } }

注意细节:增强for循环遍历数组时,循环变量直接代表每一份元素,并不是下标。为了避免和出错和fori搞混,这个循环变量我们一般不会取名为i

4、foreach方法
//遍历集合 default void forEach(Consumer<? super I>action)

跟进源码后发现该方法需要的参数Consumer是一个接口类型,那么我们就要传入该接口的实现类对象,可以创建一个并传入。然而源码中验证该接口还是一个函数式接口,因此可以传入匿名内部类,还可以将其改写成Lambda表达式。

public class CollectionDemo4 { ? ?public static void main(String[] args) { ? ? ? ?Collection<Student> c = new ArrayList<>(); ? ? ? ?c.add(new Student("张三",23)); ? ? ? ?c.add(new Student("李四",24)); ? ? ? ?c.add(new Student("王五",25)); ? ? ? ? ?//匿名内部类写法 ? ? ? ?c.forEach(new Consumer<Student>() { ? ? ? ? ? ?@Override ? ? ? ? ? ?public void accept(Student student) { ? ? ? ? ? ? ? ?System.out.println(student); ? ? ? ? ? } ? ? ? }); ? ? ? ? ?//Lambda表达式写法 ? ? ? ?c.forEach(s-> System.out.println(s)); ? } }
5、ListIterator遍历

继承了Iterator,是List集合派系所特有的迭代器,遍历方式与Iterator遍历类似,但也有其特殊之处:它内部含有hasPrevious()方法和previous()方法,可以配合使用进行倒序遍历,前提是必须要先正序遍历让指针移至最后,否则倒叙遍历没有效果

public static void main(String[] args) { ? ? ? ?List<String> list = new ArrayList<>(); ? ? ? ? ?list.add("张三"); ? ? ? ?list.add("李四"); ? ? ? ?list.add("王五"); ? ? ? ? ? ?ListIterator<String> it = list.listIterator(); ? ? ? ? ?//正序遍历 ? ? ? ?while (it.hasNext()) { ? ? ? ? ? ?String s = it.next(); ? ? ? ? ? ?System.out.println(s); ? ? ? } ? ? ? ? ?System.out.println("---------------------------------"); ? ? ? ? ?//倒序遍历,前提必须先正序遍历让指针后移至最后,否则没有效果 ? ? ? ?while (it.hasPrevious()) { ? ? ? ? ? ?String s = it.previous(); ? ? ? ? ? ?System.out.println(s); ? ? ? } ? }

四、List接口

list接口因为支持索引,所以多了很多索引操作的独特API

方法名

说明

voidadd(int index,E element)

在此集合中的指定位置插入指定的元素

Eremove(int index)

删除指定索引处的元素,返回被删除的元素

Eset(int index,E element)

修改指定索引处的元素,返回被修改的元素

Eget(int index)

返回指定索引处的元素

五、数据结构

数据结构是计算机底层存储组织数据的方式,是指数据相互之间是以什么方式排列在一起的

1、栈和队列

队列

一端开口(栈顶),一端封闭(栈底)

两端均开口

栈顶出入栈

队尾入队,队头出队

后进先出

先进先出

2、数组和链表

数组

链表

内存连续区域

在内存中游离,不连续

查询速度快:通过地址和索引定位,查任意数据耗时相同

查询速度慢:没有索引,无论查询哪个数据都要从头遍历

增删效率低:增删有可能大批量的移动数组中其他元素

增删效率相对数组快:增删不用移动大量元素,只需修改指针即可

  • 单链表&双链表

链表元素在内存中是游离的,其中每个结点是独立的对象,在内存中不是连续的,每个结点有自己的存储地址,包含其存储的具体数据值和下一个结点的地址。见名知义,单链表即链接方向是单向的,对链表的访问要通过顺序读取从头部开始。双链表的链接方向是双向的,即每个数据结点中都有两个指针,分别指向直接后继和直接前驱。因此双向链表首尾操作极快!!!

六、ArrayList类&LinkedList类

1、ArrayList类

ArrayList底层是基于数组实现的,所以查询元素快,增删相对慢

1.1 ArrayList长度可变原理

ArrayList底层是数据结构,数组默认长度为10;当数组添加满了之后,会自动扩容为1.5倍,扩容时会先将原数组数据拷贝到新数组中,再将新元素添加到新数组

1.2 ArrayList源码解析

使用空参构造器创建的集合,在底层创建一个默认长度为0的数组

添加第一个元素时,底层会创建一个新的长度为10的数组

存满时,会扩容1.5倍

2、LinkedList类

LinkedList底层基于双链表实现的,查询元素慢,增删首尾元素是非常快的

特有方法

说明

public void addFirst(E e)

在该列表开头插入指定的元素

public void addLast(E e)

将指定的元素追加到此列表的末尾

public E getFirst()

返回此列表中的第一个元素

public E getLast()

返回此列表中的最后一个元素

public E removeFirst()

从此列表中删除并返回第一个元素

public E removeLast()

从此列表中删除并返回最后一个元素

  • **注意:**LinkedList的get()方法,表面看起来是根据索引获取元素,实际并非如此。它的原理很简答,是通过遍历链表来查找指定索引的元素。具体来说,get()方法从链表的表头开始遍历,它经过一个节点,就将计数器加一。当计数器的值等于要查找的索引时,get()方法就返回该节点的元素值,否则继续遍历直到表尾。

七、泛型

JDK5引入泛型,可以在编译阶段约束操作的数据类型,并进行检查。使用泛型的**好处是:统一数据类型,将运行期的错误提升到了编译期。**泛型中只能编写引用型数据,如果不指定泛型的具体类型,则系统默认创建Object对象

1、泛型类
1.1 使用场景

当类中的属性或是方法却无法确定具体类型时,可以设计泛型类

1.2 确定具体类型

在创建对象的时候确定到具体数据类型

//泛型类 public class ArrayList<E>{ ? ?private E e; ? ?public E getE(){ ? ? ? ?return e; ? } ? ?public void setE(E e){ ? ? ? ?this.e = e; ? } } ? public static void main(String[] args){ ? ?//创建对象,指定类型为Integer ? ?Student<Integer> stu = new Student<>; }
2、泛型方法
2.1 非静态泛型方法

泛型是根据类的泛型去匹配的

public class ArrayList<E>{ ? ?public boolean add(E e){ ? } }
2.2 静态泛型方法

需要声明出自己独立的泛型

public static<T> void printArray(T[] array){} public class Demo3 { ? ?public static void main(String[] args) { ? ? ? ?Integer[] arr1 = {11,22,33}; ? ? ? ?Double[] arr2 = {11.1,22.2,33.3}; ? ? ? ?String[] arr3 = {"张三","李四","王五"}; ? ? ? ? ?printArray(arr1); ? ? ? ?printArray(arr2); ? ? ? ?printArray(arr3); ? } ? ? ?//该方法在main函数中调用,因此必须是static修饰,又想接收各种类型,所以自己定义独立的泛型 ? ?private static<T> void printArray(T[] arr) { ? ? ? ?System.out.print("["); ? ? ? ?for (int i = 0; i < arr.length-1; i++) { ? ? ? ? ? ?System.out.print(arr[i]+","); ? ? ? } ? ? ? ?System.out.println(arr[arr.length-1]+"]"); ? } }
3、泛型接口
3.1 使用场景

接口中的某个抽象方法确定不了参数的具体类型,就可以声明泛型,让该方法的泛型去匹配接口的泛型

3.2 确定具体数据类型

类实现接口时,如果接口带有泛型,有两种操作方式

  • 类实现接口的时候,直接确定类型

  • 实现类延续接口的泛型,等创建对象的时候再确定

    //泛型接口
    interface Inter{
    ? ?//抽象方法的参数匹配接口的泛型
    ? ?void show(E e);
    }
    ?
    //类实现接口的时候直接指定类型为String
    class InterAImpl implements Inter{
    ?
    ? ? @Override
    ? ? public void show(String s) {
    ?
    ? ? }
    }
    ?
    //实现类延用接口泛型,则在该实现类创建对象的时候一定要给出具体类型
    class InterBImpl implements Inter{
    ?
    ? ? @Override
    ? ? public void show(E e) {
    ? ? ? ? ?
    ? ? }
    }

4、泛型通配符

书写位置在<>内,有以下三种用法

  • :任意类型

  • ?extends E:只能接收E或者E的子类

  • ?super E:只能接收E或者E的父类

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/22 2:33:18

CSDN(中国软件开发网)作为中国领先的IT技术交流平台,每年都会基于社区活跃度、开发者关注热点

CSDN&#xff08;中国软件开发网&#xff09;作为中国领先的IT技术交流平台&#xff0c;每年都会基于社区活跃度、开发者关注热点、技术文章发布趋势、开源项目动态以及行业应用进展等多维度数据&#xff0c;发布年度技术趋势预测。以下是根据近年来的技术发展和CSDN社区动态总…

作者头像 李华
网站建设 2026/2/22 21:12:28

深度学习毕设项目推荐-深度学习基于python的CNN训练识别吃的美食基于python的深度学习CNN训练识别吃的美食

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/2/21 18:04:23

Java 进阶:如何让线程主动让出 CPU

Java 进阶如何让线程主动让出 CPU ThreadsleepThreadyieldThreadcurrentThreadsuspendObjectwaitLockSupportparkThreadstop Java 进阶&#xff1a;如何让线程主动让出 CPU Thread.sleep sleep 方法可以让线程主动让出 CPU&#xff0c;但是并不会释放锁。 /*** Causes the …

作者头像 李华
网站建设 2026/2/21 14:33:17

HDFS的架构优势与基本操作

目录 写在前面一、 HDFS概述 1.1 HDFS简介1.2 HDFS优缺点 1.2.1 优点1.2.2 缺点 1.3 HDFS组成架构1.4 HDFS文件块大小 二、HDFS的Shell操作&#xff08;开发重点&#xff09; 2.1 基本语法2.2 命令大全2.3 常用命令实操 2.3.1 上传2.3.2 下载2.3.3 HDFS直接操作 三、HDFS的AP…

作者头像 李华
网站建设 2026/2/21 23:00:50

通信协议仿真:蓝牙协议仿真_(8).蓝牙仿真硬件平台

蓝牙仿真硬件平台 在进行蓝牙协议仿真时&#xff0c;选择合适的硬件平台是至关重要的。蓝牙仿真硬件平台不仅需要支持蓝牙协议的基本功能&#xff0c;还需要具备灵活性和可扩展性&#xff0c;以便进行各种复杂场景的仿真和测试。本节将详细介绍蓝牙仿真硬件平台的选择、配置和使…

作者头像 李华
网站建设 2026/2/22 13:15:49

基于 VSC 的 UPFC(统一潮流控制器)研究(Simulink)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

作者头像 李华