news 2026/10/15 5:07:35

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/10/15 19:17:37

什么是当前读和快照读?

在 MySQL InnoDB 中&#xff0c;当前读和快照读是 MVCC 机制下的两种数据读取方式&#xff0c;核心区别在于 是否读取最新版本、是否加锁、是否受其他事务影响&#xff0c;二者分工协作实现了 “读写不阻塞” 的高效并发。 一、快照读&#xff08;Snapshot Read&#xff09; …

作者头像 李华
网站建设 2026/10/15 20:49:03

JAVA进阶 THREAD学习10 多线程案例--计时器

标准库中计时器的使用 Timer类的实例化对象TimerTask类的是实例化对象–在run方法中存放运行的代码延迟的时间&#xff08;相对时间&#xff0c;相对于当前时间之后的多少毫秒等&#xff09; 时间需要安排schedule public static void main(String[] args) {System.out.println…

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

面试 Java 基础八股文十问十答第二期

面试 Java 基础八股文十问十答第二期 作者&#xff1a;程序员小白条&#xff0c;个人博客 ⭐点赞⭐收藏⭐不迷路&#xff01;⭐ 11.什么是反射&#xff1f;反射有哪些作用&#xff1f;反射在Sping中的体现 (1): 什么是反射? 反射可以在运行时获取到一个类的所有信息&#xf…

作者头像 李华
网站建设 2026/10/15 21:02:57

毫米波V2I网络的链路层仿真研究(Matlab代码实现)

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

作者头像 李华
网站建设 2026/10/15 18:16:19

2026最全Gemini学生优惠申请指南

Google 在 2025 年正式推出 Gemini 学生优惠计划&#xff0c;符合条件的学生可在2026年1月31日前通过认证免费获得价值约 $300 的 Google AI Pro 服务长达 15 个月。这意味着你可以零成本使用&#xff1a;Gemini Advanced&#xff08;2.5 Pro / 3 系列模型&#xff09;2TB Goog…

作者头像 李华