news 2026/4/15 2:39:20

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/4/12 23:05:08

什么是当前读和快照读?

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

作者头像 李华
网站建设 2026/4/7 6:53:31

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

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

作者头像 李华
网站建设 2026/4/1 23:51:01

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

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

作者头像 李华
网站建设 2026/4/8 23:58:55

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

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

作者头像 李华
网站建设 2026/4/13 18:15:13

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…

作者头像 李华