Java进阶-集合(3)与泛型

这次介绍集合中的Iterator迭代器,以及泛型。简单来说,泛型对集合的元素类型进行了限制,使用泛型可以在编译时检查类型安全,提高代码的重用率。内容如下
在这里插入图片描述

一、Iterator迭代器

1、概念

Iterator迭代器是一个接口,作用是遍历容器的所有元素,也是 Java 集合框架的成员。

注:与 Collection 和 Map 系列的集合不同,Collection 和 Map 系列集合主要用于盛装其他对象,而 Iterator 则主要用于遍历Collection 集合中的元素。

2、Iterator接口定义的方法

可通过在IDEA中选中Iterator,ctrl+B查看源码的方式查看对应方法。

boolean hasNext():如果被迭代的集合元素还没有被遍历完,则返回 true。

next():返回集合里的下一个元素。返回类型为Object(可能涉及强转)

void remove():删除集合里上一次 next 方法返回的元素。

void forEachRemaining(Consumer action):这是 Java 8 为 Iterator 新增的默认方法,该方法可使用 Lambda 表达式来遍历集合元素。

3、案例

3.1 Iterator遍历集合
import java.util.Collection; //导包
import java.util.HashSet;
import java.util.Iterator;public class IteratorTest {public static void main(String[] args){Collection col=new HashSet(); //向上转型,把子类对象直接赋给父类引用(不用强转)col.add("zhangsan"); //添加元素col.add("lishi");col.add("wangwu");for(Object i:col){ //使用forEach()方法遍历集合System.out.println(i);}System.out.println("--------");Iterator it=col.iterator(); //获取迭代器遍历集合while (it.hasNext()){ //it.next()方法返回的数据类型是Object类型(需要强转)String coll=(String) it.next();//不强转直接用it.next()也能遍历出结果,但不能进行下一步的比较移除操作,规范类型,养成好的习惯很重要System.out.println(coll);if (coll.equals("zhangsan")){ //字符串比较it.remove(); //从集合中删除上一次next()方法返回的元素}coll="zhaoliu"; //对coll变量赋值,不会改变集合元素本身}System.out.println(col); //打印集合}
}

运行结果

lishi
zhangsan
wangwu
--------
lishi
zhangsan
wangwu
[lishi, wangwu]

注:上面程序中对迭代变量 coll赋值,但当再次输岀 col 集合时,集合里的元素不变。所以当使用 Iterator 对集合元素进行迭代时,Iterator 并不是把集合元素本身传给了迭代变量,而是把集合元素的值传给了迭代变量,所以修改迭代变量的值对集合元素本身没有任何影响

3.2 要点总结

1)Iterator 仅用于遍历集合,如果需要创建 Iterator 对象,则必须有一个被迭代的集合,否则Iterator 没有存在的价值。
2)Iterator 必须依附于 Collection 对象,若有一个 Iterator 对象,则必然有一个与之关联的 Collection 对象。(与上同理)
3)Iterator 提供了两个方法来迭代访问 Collection 集合里的元素,Collection 集合里的元素不能被改变,只有通过 Iterator 的 remove() 方法删除上一次 next() 方法返回的集合元素才可以,否则将会引发“java.util.ConcurrentModificationException”异常。

3.3 示例
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;public class IteratorTest {public static void main(String[] args){Collection col=new HashSet(); //向上转型,把子类对象直接赋给父类引用(不用强转)col.add("zhangsan"); //添加元素col.add("lishi");col.add("wangwu");Iterator it=col.iterator(); //获取迭代器遍历集合while (it.hasNext()){ //it.next()方法返回的数据类型是Object类型(需要强转)String coll=(String) it.next();//不强转直接用it.next()也能遍历出结果,但不能进行下一步的比较移除操作,规范类型,养成好的习惯很重要System.out.println(coll);if (coll.equals("zhangsan")){ //字符串比较//使用Iterator迭代过程中,不可修改集合元素,下面代码引发异常col.remove(coll);}}}
}

运行结果

lishi
zhangsan
Exception in thread "main" java.util.ConcurrentModificationExceptionat java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1510)at java.base/java.util.HashMap$KeyIterator.next(HashMap.java:1533)at test3.IteratorTest.main(IteratorTest.java:15)

4、Iterator错误检查机制

4.1 概述

Iterator 迭代器采用的是快速失败(fail-fast)机制一旦在迭代过程中检测到该集合已经被修改(通常是程序中的其他线程修改),程序立即引发 ConcurrentModificationException 异常,而不是显示修改后的结果,这样可以避免共享资源而引发的潜在问题

注:快速失败(fail-fast)机制,是 Java Collection 集合中的一种错误检测机制。

用户可以自行验证,当3.3 示例中改为删除“wangwu”字符串即将上面的coll.equals(“zhangsan”)改为coll.equals(“wangwu”)时,则不会引发异常,因为王五是最后添加的,相当于所有都迭代完成后才删除,故不会引发ConcurrentModificationException 异常。

总结:所以说尽量不要在迭代时删除集合元素,防止引发异常。

二、泛型

1、集合的设计角度

把集合看成容器,将对象“丢进”集合,集合不会记住对象的数据类型(即丢失了对象的状态信息),再次取出时,对象的编译类型变为Object(运行时类型不变)

1.1 优点

具有很好的通用性,能保存任何类型的对象(因为Object类是所有类的父类,即创建对象时都能向上转型,不用强转)

1.2 问题(若无泛型)

1)集合对元素类型没有任何限制,如想创建一个只保存 Dog 对象的集合,但程序也可以轻易地将 Cat 对象“丢”进去,可能引发异常。
2)把对象“丢进”集合时,集合丢失了对象的状态信息,只知道它盛装的是 Object,因此取出集合元素后通常还需要进行强制类型转换。(这样既增加了编程的复杂度,也可能引发 ClassCastException即类型转换异常)

1.3 解决

为了解决上述问题,从 Java 1.5 开始提供了泛型。

2、泛型

2.1 几点注意

1)抽象地说,泛型是一种“代码模板”,可以用一套代码套用各种类型(泛型编程)
2)具体而言,泛型本质上是提供类型的“类型参数”(参数化类型)。可以为类、接口或方法指定一个类型参数,通过这个参数限制操作的数据类型,从而保证类型转换的绝对安全。

泛型可以在编译时检查类型安全,并且所有的强制转换都是自动和隐式的,提高了代码的重用率。

3、泛型集合

示例:结合泛型与集合编写一个案例实现图书信息输出
1)创建一个Book类(图书编号、图书名称、价格)

public class Book { // 定义Book类 (完整javabean)private  int id; // 封装成员变量private String name;private int price;public Book(){ // 无参构造方法}public Book(int id,String name,int price){ // 带全部参数的构造方法this.id=id;  // this指向当前变量this.name=name;this.price=price;}public String toString(){ //重写父类(Object类)的toString()方法,返回对象的字符串表示return this.id+" "+this.name+" "+this.price;}public int getId() { // 提供get和set方法,习惯,虽然这里用不到return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getPrice() {return price;}public void setPrice(int price) {this.price = price;}
}

2)使用 Book 作为类型创建 Map 和 List 两个泛型集合,然后向集合中添加图书元素,最后输出集合中的内容

import java.util.ArrayList; //导包
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class BookTest { // 创建集合测试类public static void main(String[] args){Book book1=new Book(1,"唐诗三百首",18); // 创建对象并实例化Book book2 = new Book(2, "小星星", 12);Book book3 = new Book(3, "成语大全", 22);Map<Integer,Book> books=new HashMap<>(); // 创建泛型的Map集合//定义Interger类型的键,Book类型对象整体作为值,通过get()方法得到键对应的值打印输出即为全部图书信息books.put(1001,book1);  // 将Book对象存储到Map中books.put(1002, book2);books.put(1003, book3);System.out.println("泛型Map存储的图书信息如下:");for (Integer id : books.keySet()) {  // for-each循环 遍历键System.out.print(id + "——"); //获取键System.out.println(books.get(id)); //获取值(Book对象整体,即为全部图书信息)//不需要将books.get(id)获取的值强制转换为Book类型,程序会隐式转换(泛型功能)}List<Book> bookList = new ArrayList<>(); // 定义泛型的List集合bookList.add(book1); //添加book对象元素bookList.add(book2);bookList.add(book3);System.out.println("泛型List存储的图书信息如下:");for (int i = 0; i < bookList.size(); i++) { //for循环遍历输出System.out.println(bookList.get(i)); //get()方法得到索引对应的元素//不需要将bookList.get(i)强制转换为Book类型,程序会隐式转换(泛型功能)}}
}

运行结果

泛型Map存储的图书信息如下:
1001——1 唐诗三百首 18
1002——2 小星星 12
1003——3 成语大全 22
泛型List存储的图书信息如下:
1 唐诗三百首 18
2 小星星 12
3 成语大全 22

4、泛型类

4.1 几点注意

public class class_name<data_type1,data_type2,…>{}:data_type为类型参数(Java 泛型支持声明一个以上的类型参数,逗号隔开)。属性声明:如private data_type1 property_name1;

一般用于类中的属性类型不确定的情况下

在实例化泛型类时,需要指明泛型类中的类型参数,并赋予泛型类属性相应类型的值。

4.2 示例

创建一个学生的泛型类,包含姓名、年龄和性别3个属性
1)创建一个学生的泛型类

public class Stu<N,A,S> { //定义学生泛型类private N name; //封装成员变量private A age;private S sex;public Stu(){ //无参构造方法}public Stu(N name,A age,S sex){ //带全部参数的构造方法this.name=name;this.age=age;this.sex=sex;}public String toString(){ //toString()方法返回对象的字符串表示return this.name+" "+this.age+" "+this.sex;}public N getName() { //提供整套get和set方法return name;}public void setName(N name) {this.name = name;}public A getAge() {return age;}public void setAge(A age) {this.age = age;}public S getSex() {return sex;}public void setSex(S sex) {this.sex = sex;}
}

2)创建测试类

public class StuTest { // 创建学生泛型类的测试类public static void main(String[] args){// 实例化泛型对象,直接在类后面加上限定泛型类的类型参数Stu<String,Integer,Character> s=new Stu<>("zhangsan",20,'男');String name=s.getName(); //调用get方法Integer age=s.getAge();Character sex=s.getSex();//以上获取时不用类型转换,程序隐式地将Object类型的数据转换为相应的数据类型System.out.println("----------学生信息----------");System.out.println("学生姓名:"+name+" 年龄:"+age+" 性别:"+sex);}
}

5、泛型方法

5.1 注意与说明

泛型可以在类中包含参数化的方法,而方法所在的类可以是泛型类,也可以不是泛型类(即是否拥有泛型方法,与其所在的类是不是泛型没有关系)。

泛型方法使得该方法能够独立于类而产生变化。如果使用泛型方法可以取代类泛型化,那么就应该只使用泛型方法。

一个 static 方法无法访问泛型类的类型参数。因此,如果 static 方法需要使用泛型能力,就必须使其成为泛型方法。

格式:[访问权限修饰符] [static] [final] <类型参数列表> 返回值类型 方法名([形式参数列表]),如

如:public static <T> List find(Class<T> cs,int userId){}

一般来说编写 Java 泛型方法,其返回值类型至少有一个参数类型是泛型,且类型应该是一致的,如果只有返回值类型或参数类型之一使用了泛型,那么这个泛型方法的使用就被限制了。

5.2 示例

使用泛型方法打印图书信息。定义泛型方法,参数类型使用“T”来代替。

// 1) 定义一个Book类,代码同3、泛型集合示例第一个
// 2) 定义Book泛型方法的测试类
public class BookDemo { //创建book泛型方法的实现类public static <T> void List(T book){ //定义泛型方法,T为参数类型if(book!=null){ //若不为空,输出图书信息System.out.println(book);}}public static void main(String[] args){//实例化Book对象Book b=new Book(1,"java编程",20);List(b); //调用List泛型方法}
}
//result
//1 java编程 20

6、泛型高级用法

除在集合、类和方法中使用,泛型还有如下高级用法

6.1 限制泛型可用类型

语法:class 类名称,anyClass指某个接口或类,使用泛型限制后,泛型类的类型必须实现或继承 anyClass 这个接口或类,且在进行泛型限制时必须使用 extends 关键字(否则默认是Object类型,即其所有子类都可以实例化泛型类对象,这样就没有意义了)

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
// 限制ListClass的泛型类型必须实现List接口
public class ListClass <T extends List>{public static void main(String[] args) {// 实例化使用ArrayList的泛型类ListClass,正确ListClass<ArrayList> lc1 = new ListClass<ArrayList>();// 实例化使用LinkedList的泛型类LlstClass,正确ListClass<LinkedList> lc2 = new ListClass<LinkedList>();// 实例化使用HashMap的泛型类ListClass,错误,因为HasMap没有实现List接口// ListClass<HashMap> lc3=new ListClass<HashMap>();}
}
6.2 使用类型通配符<?>

类型通配符作用

在创建一个泛型类对象时限制这个泛型类的类型必须实现或继承某个接口或类。

list<?>

表示元素类型未知的list,其元素可以匹配任何的类型。表示它是各种泛型list的父类,并不能把元素添加到其中

类型通配符上限:<? extends 类型>

  • List<? extends Number>:表示的类型是Number或其子类型

类型通配符下限:<?super 类型>

  • List<? super Number>:表示的类型是Number或其父类型

语法

泛型类名称<? extends List>a = null;

A<? extends List>a = null;
a = new A<ArrayList> ();    // 正确
b = new A<LinkedList> ();    // 正确
// 错误 HashMap类没有实现List接口

继承泛型类和实现泛型接口

//继承泛型类
public class FatherClass<T1>{}
public class SonClass<T1,T2,T3> extents FatherClass<T1>{}
//在泛型中实现接口
interface interface1<T1>{}
interface SubClass<T1,T2,T3> implements
Interface1<T2>{}

扩展:
可变参数(即参数个数可变)

格式:修饰符 返回值类型 方法名(数据类型…变量名){}

范例

public static int sum(int…a){}

注意

这里的变量其实是一个数组。

如果一个方法有多个参数,包含可变参数,可变参数要放在后面。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/498289.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于RISC-V架构的通信DSP的设计以及在5G RedCap基带中的应用(五)-基于RISC-V的RedCap DSP在5G基带中的应用

4 基于RISC-V的RedCap DSP在5G基带中的应用 4.1 基带处理器的关键任务和性能需求 基带处理器是移动通信设备中的关键部件&#xff0c;负责处理无线信号&#xff0c;包括信号的接收、发送和处理。在5G通信系统中&#xff0c;基带处理器的关键任务和性能需求包括以下几个方面&a…

吉瑞苍穹外卖如何拓展?已经经过不同公司多轮面试。项目中会问到哪些问题?以及问题如何解决?

别催了&#xff0c;别催了&#xff0c;先收藏吧。 作者大大正在加班加点完成。 文章会尽快发布&#xff0c;关注收藏&#xff0c;尽请期待。 叠一波甲&#xff1a; 文档也参考了其他作者大大的资料&#xff0c;都会给出相应的标注。 本文档仅供交流学习&#xff0c;不牟利贩…

GEE:基于Landsat5/7/8/9数据提取一个点的NDVI时间序列(1986-2024)

作者:CSDN @ _养乐多_ 本文将介绍,基于Landsat5/7/8/9数据提取一个点的NDVI时间序列,时间序列数据可以是1986-2024年所有可用数据,也可以是月度合成、年度合成或者指定间隔合成的时间序列。 结果如下图所示, 文章目录 一、代码框架二、代码链接三、完整代码一、代码框架…

K8s Pod资源管理组件

目录 Pod基础概念 在Kubrenetes集群中Pod有如下两种使用方式 pause容器使得Pod中的所有容器可以共享两种资源 网络 存储 总结 kubernetes中的pause容器主要为每个容器提供功能 Kubernetes设计这样的Pod概念和特殊组成结构的用意 通常把Pod分为以下几类 自主式Pod 控…

【Unity】如何在Unity 中创建带有缩放效果的滚动视图(具有吸附效果的实现与优化)?

效果预览&#xff1a; 目录 效果预览&#xff1a; 一、引言&#xff1a; 二、问题描述 三、解决方案&#xff1a; 三、优化&#xff1a; 四、结论 一、引言&#xff1a; 在Unity开发中&#xff0c;经常需要实现滚动视图&#xff08;ScrollView&#xff09;中的内容吸附到…

Nginx 常用的基础配置(前端相关方面)

Nginx是一款高性能的Web服务器和反向代理服务器&#xff0c;广泛应用于互联网领域。作为一名前端同学&#xff0c;了解并掌握Nginx的配置是非常有必要的。 安装Nginx sudo apt-get update sudo apt-get install nginx查看Nginx版本 nginx -v启动、停止、重启Nginx服务 sudo …

【GAD】动态图的半监督异常检测

SAD: Semi-Supervised Anomaly Detection on Dynamic Graphs Limitations of existing semi-supervised methodsContributionRelated workMethodDeviation Networks with Memory BankContrastive Learning for Unlabeled Samples Experiments少样本评估2D t-SNE可视化消融实验 …

C++之queue和dqueue

1、queue queue&#xff08;队列&#xff09;&#xff0c;一种数据结构&#xff0c;可以让某些数据结构的操作变得简单。队列&#xff08;queue&#xff09;最大的特点就是先进先出。就是说先放入queue容器的元素一定是要先出队列之后&#xff0c;比它后进入队列的元素才能够出…

Unity(第十一部)场景

游戏有多个场景组成&#xff08;新手村&#xff0c;某某副本&#xff0c;主城&#xff09; 场景是有多个物体组成&#xff08;怪物&#xff0c;地形&#xff0c;玩家等&#xff09; 物体是有多个组件组成&#xff08;刚体组件&#xff0c;自定义脚本&#xff09; 创建场景 编辑…

逆向案例三:动态xhr包中AES解密的一般步骤,以精灵数据为例

补充知识&#xff1a;进行AES解密需要知道四个关键字&#xff0c;即密钥key,向量iv,模式mode,填充方式pad 一般网页AES都是16位的&#xff0c;m3u8视频加密一般是AES-128格式 网页链接:https://www.jinglingshuju.com/articles 进行抓包结果返回的是密文&#xff1a; 一般思…

Sui主网升级至V1.19.1版本

其他升级要点如下所示&#xff1a; #16190, #16193 现在CLI正确处理并修复了交易没有输入或命令时的输出表格。例如&#xff0c;调用 client call — package 0x2 — module kiosk — function default 现在具有正确格式的输出。 #15928 Move编译器的一系列变更 添加了宏函…

使用Axure RP并配置IIS服务结合内网穿透实现公网访问本地HTML原型页面

文章目录 前言1.在AxureRP中生成HTML文件2.配置IIS服务3.添加防火墙安全策略4.使用cpolar内网穿透实现公网访问4.1 登录cpolar web ui管理界面4.2 启动website隧道4.3 获取公网URL地址4.4. 公网远程访问内网web站点4.5 配置固定二级子域名公网访问内网web站点4.5.1创建一条固定…