removeIf 方法设计理念及泛型界限限定
ArrayList
中的removeIf
方法是 Java 8 中引入的集合操作方法之一。它使用了Predicate
接口作为参数,以便根据指定的条件移除集合中的元素。以下是对removeIf
方法入参设计的详细解释:
Predicate
接口
Predicate
是一个函数式接口,定义了一个 test
方法,用于接收一个参数并返回一个布尔值。它的签名如下:
@FunctionalInterface
public interface Predicate<T> {boolean test(T t);
}
Predicate
接口通常用于对输入参数进行条件测试。结合 removeIf
方法,这个接口被用来判断集合中的元素是否应该被移除。
removeIf
方法的签名
在 ArrayList
类中,removeIf
方法的签名如下:
public boolean removeIf(Predicate<? super E> filter)
其中,E
是集合的泛型类型参数,而 Predicate<? super E>
表示可以接受 E
类型或其父类型的参数。
设计理由
-
灵活性: 使用
Predicate<? super E>
作为参数,使得removeIf
方法可以接受针对元素类型及其父类型的条件。这种设计提供了更大的灵活性。例如,如果有一个ArrayList<Number>
,可以传入Predicate<Object>
,因为Object
是Number
的父类型。 -
函数式编程: Java 8 引入了函数式编程的概念,允许使用 lambda 表达式和方法引用。这使得编写条件测试的代码变得更加简洁和直观。例如,可以使用 lambda 表达式来移除所有负数:
ArrayList<Integer> list = new ArrayList<>(Arrays.asList(1, -2, 3, -4, 5)); list.removeIf(n -> n < 0);
-
类型安全: 泛型确保了
Predicate
的参数类型与集合元素类型一致,从而在编译时提供类型安全。这样可以避免传递错误类型的条件测试,导致运行时错误。
removeIf
方法的实现
removeIf
方法的内部实现使用了迭代器来遍历集合,并应用 Predicate
进行条件测试。以下是简化的实现:
public boolean removeIf(Predicate<? super E> filter) {Objects.requireNonNull(filter);boolean removed = false;final Iterator<E> each = iterator();while (each.hasNext()) {if (filter.test(each.next())) {each.remove();removed = true;}}return removed;
}
在这个实现中:
- 检查
filter
是否为null
。 - 使用迭代器遍历集合。
- 对每个元素应用
Predicate
进行测试。 - 如果
Predicate
返回true
,则移除该元素。
使用示例
以下是一些使用 removeIf
方法的示例:
移除负数
ArrayList<Integer> numbers = new ArrayList<>(Arrays.asList(1, -2, 3, -4, 5));
numbers.removeIf(n -> n < 0);
System.out.println(numbers); // 输出:[1, 3, 5]
移除空字符串
ArrayList<String> strings = new ArrayList<>(Arrays.asList("apple", "", "banana", " ", "cherry"));
strings.removeIf(String::isEmpty);
System.out.println(strings); // 输出:[apple, , banana, cherry]
细品一下 removeIf() 方法入参设计
在 ArrayList
的 removeIf
方法中,使用 Predicate<? super E>
而不是 Predicate<E>
是为了增加方法的灵活性和通用性。具体来说,这与 Java 泛型的协变和逆变有关。以下是详细的解释:
泛型的协变和逆变
- 协变(Covariance):允许使用某种类型及其子类型。用
<? extends E>
表示。 - 逆变(Contravariance):允许使用某种类型及其超类型。用
<? super E>
表示。 - 不变(Invariance):只能使用某种特定类型。用
<E>
表示。
在 removeIf
方法中,Predicate<? super E>
表示这个 Predicate
可以接受类型 E
或 E
的任何超类型。这是一种逆变,用于增加灵活性。
为什么使用 <? super E>
而不是 <E>
-
灵活性: 使用
<? super E>
可以让Predicate
接受类型E
或E
的父类型的对象,从而增加了方法的灵活性。例如,如果E
是Integer
,那么Predicate<? super Integer>
可以接受Integer
及其父类Number
和Object
的Predicate
。这意味着你可以传入更多种类的
Predicate
,而不仅仅是严格匹配E
的Predicate
。例如:List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3)); Predicate<Object> isEven = obj -> obj instanceof Integer && (Integer) obj % 2 == 0; list.removeIf(isEven); // 使用 Predicate<Object> 也可以
-
兼容性: 假设你有一个处理父类
Number
的Predicate
,比如:Predicate<Number> isPositive = num -> num.doubleValue() > 0;
这个
Predicate
可以传递给ArrayList<Integer>
的removeIf
方法,因为Number
是Integer
的超类型。如果removeIf
方法只接受Predicate<E>
,那么你只能传入Predicate<Integer>
。 -
类型安全: 使用
<? super E>
仍然确保了类型安全,因为在调用removeIf
方法时,传入的Predicate
需要能够处理类型E
。例如,对于ArrayList<Integer>
,传入的Predicate
需要能够处理Integer
及其超类型。
注意: ? 无界通配符主要用于不关心元素类型的操作,基本都是公共类操作。
例子
使用 Predicate<E>
如果 removeIf
只接受 Predicate<E>
,那么只能传入 Predicate<Integer>
:
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3));
Predicate<Integer> isEven = n -> n % 2 == 0;
list.removeIf(isEven); // 正常工作
但是不能传入 Predicate<Number>
:
Predicate<Number> isPositive = num -> num.doubleValue() > 0;
// list.removeIf(isPositive); // 编译错误
使用 Predicate<? super E>
使用 Predicate<? super E>
,可以传入 Predicate<Integer>
或 Predicate<Number>
:
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3));
Predicate<Integer> isEven = n -> n % 2 == 0;
Predicate<Number> isPositive = num -> num.doubleValue() > 0;
list.removeIf(isEven); // 正常工作
list.removeIf(isPositive); // 也能正常工作
简而言之:
ArrayList
中 removeIf
方法使用 Predicate<? super E>
而不是 Predicate<E>
是为了增加方法的灵活性和通用性。通过允许传入类型 E
或其超类型的 Predicate
,removeIf
方法变得更加通用和灵活,同时仍然保持类型安全。这样的设计使得在处理集合元素时,可以应用更多种类的条件,增加了代码的可重用性和灵活性。
再举例说明逆变的用法
逆变(Contravariance)在 Java 泛型中通过下界通配符(<? super T>
)来实现。下界通配符允许使用某个类型及其父类型,这对于需要向集合中添加元素或处理泛型对象的写操作特别有用。以下是一些具体的例子来说明逆变的用法。
1. 使用逆变来添加元素到集合中
逆变可以确保能够向集合中添加类型 T
或 T
的子类型的元素。
例子 1:向集合中添加元素
import java.util.List;
import java.util.ArrayList;public class CollectionUtils {public static void addAnimals(List<? super Dog> list) {list.add(new Dog());list.add(new Puppy());// list.add(new Animal()); // 编译错误,Animal 不是 Dog 的子类}public static void main(String[] args) {List<Animal> animalList = new ArrayList<>();addAnimals(animalList);for (Object obj : animalList) {System.out.println(obj.getClass().getSimpleName());}}
}class Animal {}
class Dog extends Animal {}
class Puppy extends Dog {}
在这个例子中,addAnimals
方法接受一个 List<? super Dog>
类型的参数,这意味着它可以接受 Dog
类型及其超类型(如 Animal
和 Object
)的列表,并向其中添加 Dog
及其子类型(如 Puppy
)的元素。
2. 处理通用的数据结构
逆变可以用于处理具有多种元素类型的数据结构,比如在比较器中。
例子 2:使用逆变来编写通用比较器
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;public class SortingUtils {public static <T> void sortList(List<T> list, Comparator<? super T> comparator) {Collections.sort(list, comparator);}public static void main(String[] args) {List<Integer> intList = new ArrayList<>();intList.add(3);intList.add(1);intList.add(4);intList.add(1);intList.add(5);Comparator<Number> numberComparator = (Number n1, Number n2) -> Double.compare(n1.doubleValue(), n2.doubleValue());sortList(intList, numberComparator);System.out.println(intList); // 输出: [1, 1, 3, 4, 5]}
}
在这个例子中,sortList
方法接受一个 Comparator<? super T>
类型的参数,这意味着它可以接受 T
类型及其超类型的比较器,从而使得比较器更加通用和灵活。
3. 使用逆变来编写通用的消费操作
逆变也可以用于消费操作,例如在处理不同类型的消费者时。
例子 3:使用逆变来编写通用的消费者
import java.util.List;
import java.util.ArrayList;
import java.util.function.Consumer;public class ConsumerUtils {public static <T> void processElements(List<T> list, Consumer<? super T> consumer) {for (T element : list) {consumer.accept(element);}}public static void main(String[] args) {List<Dog> dogList = new ArrayList<>();dogList.add(new Dog());dogList.add(new Puppy());Consumer<Animal> animalConsumer = animal -> System.out.println(animal.getClass().getSimpleName());processElements(dogList, animalConsumer); // 输出: Dog Puppy}
}class Animal {}
class Dog extends Animal {}
class Puppy extends Dog {}
在这个例子中,processElements
方法接受一个 Consumer<? super T>
类型的参数,这意味着它可以接受 T
类型及其超类型的消费者,从而使得消费者更加通用和灵活。
总结(添加、通用/公用、消费类型参数使用逆变)
逆变通过下界通配符(<? super T>
)允许使用某个类型 T
及其父类型,主要用于写操作。使用逆变可以:
- 添加元素:确保可以向集合中添加类型
T
及其子类型的元素。 - 通用操作:处理具有多种元素类型的数据结构,使得操作更加灵活和通用。
- 消费操作:处理不同类型的消费者,使得消费者更加通用和灵活。
通过这些例子,可以看到逆变在编写通用和灵活的代码中发挥了重要作用,确保类型安全的同时增加了代码的灵活性。
相关文章:
removeIf 方法设计理念及泛型界限限定
ArrayList 中的 removeIf 方法是 Java 8 中引入的集合操作方法之一。它使用了 Predicate 接口作为参数,以便根据指定的条件移除集合中的元素。以下是对 removeIf 方法入参设计的详细解释: Predicate 接口 Predicate 是一个函数式接口,定义了…...
kubernetes集群部署elasticsearch集群,包含无认证和有认证模式
1、背景: 因公司业务需要,需要在测试、生产kubernetes集群中部署elasticsearch集群,因不同环境要求,需要部署不同模式的elasticsearch集群, 1、测试环境因安全性要求不高,是部署一套默认配置; 2…...
Java 随笔记: 集合与泛型
文章目录 1. 集合框架概述2. 集合接口2.1 Collection 接口2.2 List 接口2.3 Set 接口2.4 Map 接口 3. 集合的常用操作3.1 添加元素3.2 删除元素3.3 遍历元素3.4 判断大小3.5 判断是否为空 4. 迭代器4.1 迭代器的作用4.2 迭代器的使用4.3 迭代器与增强 for 循环4.4 迭代器的注意…...
SurrealDB:高效构建实时Web应用的数据库
SurrealDB:数据驱动,实时协同。用SurrealDB简化你的开发流程- 精选真开源,释放新价值。 概览 SurrealDB,一款专为现代Web应用设计的云原生数据库,以其创新的架构和功能,为开发者提供了一个强大的工具。它整…...
SQL Server查询计划阅读及分析
6.4.5. 查询计划阅读及分析 SQL Server中,SQL语句的查询计划可能会包含多个节点,每个节点除了包含和对应一个操作符外,还包含节点及操作符相关的其他信息,其细节与具体的操作符相关。SQL Server查询计划与Oracle执行计划中,虽然每个节点所包含内容的具体称谓…...
SAP Fiori 实战课程(二):新建页面
课程回顾 上一课中,利用Visual studio Code 新建、并运行了一个Demo工程。可以实现对项目的启动,启动后进入一个List清单。 那么本次课程的目前就是在上一节Demo的基础上,从零开始新建一个完整的页面。实现从首页清单,选择行后,鼠标点击,进入下一个页面。 准备工作 在开…...
【Rust光年纪】超越ORM:探索Rust语言多款数据库客户端库的核心功能和使用场景
数据库操作新选择:从异步操作到连接管理,掌握Rust语言数据库客户端库的全貌 前言 在现代软件开发中,与数据库进行交互是一个常见的任务。Rust语言作为一种高性能、内存安全的编程语言,拥有丰富的生态系统来支持各种数据库操作。…...
解决:事件监听器 addEventListener 被多次调用
背景: 给一个元素添加了事件监听,click 会触发 然而在实际场景中,点击一次,事件会被触发两次 阻止冒泡也没有用 解决: 使用API:event.stopImmediatePropagation() stopImmediatePropagation() 方法可防止…...
配置RIPv2的认证
目录 一、配置IP地址、默认网关、启用端口 1. 路由器R1 2. 路由器R2 3. 路由器R3 4. Server1 5. Server2 二、搭建RIPv2网络 1. R1配置RIPv2 2. R2配置RIPv2 3. Server1 ping Server2 4. Server2 ping Server1 三、模拟网络攻击,为R3配置RIPv2 四、在R…...
前端调试技巧:动态高亮渲染区域
效果: 前端界面的渲染过程、次数,会通过高亮变化来显示,通过这种效果排除一些BUG 高亮 打开方式 F12进入后点击ESC,进入rendering,选择前三个即可(如果没有rendering,点击橘色部分勾选上&…...
深克隆与浅克隆的区别与实现
在软件开发中,克隆对象是一个常见需求。克隆的方式主要有两种:深克隆(Deep Clone)和浅克隆(Shallow Clone)。了解它们的区别及其实现方法,对于编写高效、安全的代码非常重要。 深克隆与浅克隆的…...
【学习笔记】无人机系统(UAS)的连接、识别和跟踪(六)-无人机直接C2通信
目录 引言 5.4 直接C2通信 5.4.1 概述 5.4.2 A2X直接C2通信服务的授权策略 5.4.3 USS使用A2X直接C2通信服务的C2授权程序 5.4.4 直接C2通信建立程序 引言 3GPP TS 23.256 技术规范,主要定义了3GPP系统对无人机(UAV)的连接性、身份识别…...
认识和安装R的扩展包,什么是模糊搜索安装,工作目录和空间的区别与设置
R语言以其强大的功能和灵活的扩展性,成为了无数数据分析师和研究者的首选工具。R的丰富功能和海量扩展包直接相关,但如何高效管理这些扩展包,进而充分发挥R的强大潜力?本文将为您揭示这些问题的答案。 一、R的扩展包 R的包(packages)是由R函数、数据和预编译代码组成的一…...
解决STM32开启定时器时立即进入一次中断程序问题
转自 解决STM32开启定时器时立即进入一次中断程序问题_stm32f407定时器初始化自动进入一次-CSDN博客 配置STM32定时器时,定时器中断使能、定时器使能、清除更新中断标志位,三者不同顺序程序执行时有不同效果,具体如下: TIM_Clea…...
Unity UGUI 之EventSystem
本文仅作学习笔记与交流,不作任何商业用途 本文包括但不限于unity官方手册,唐老狮,麦扣教程知识,引用会标记,如有不足还请斧正 1.EventSystem是什么? 有需要请查看手册:Unity - 手册࿱…...
USB转多路UART - USB 基础
一、 前言 断断续续做了不少USB相关开发,但是没有系统去了解过,遇到问题就很被动了。做这个USB转UART的项目就是,于是专门花了一天的时间学习USB及CDC相关,到写这文章时估计也忘得差不多了,趁项目收尾阶段记录一下&am…...
接近50个实用编程相关学习资源网站
Date: 2024.07.17 09:45:10 author: lijianzhan 编程语言以及编程相关工具等实用性官方文档网站 C语言文档:https://learn.microsoft.com/zh-cn/cpp/c-languageMicrosoft C、C和汇编程序文档:https://learn.microsoft.com/zh-cn/cppJAVA官方文档&#…...
在数据操作中使用SELECT子句
目录 一、INSERT 语句中使用 SELECT子句 二、UPDATE 语句中使用 SELECT子句 三、DELETE 语句中使用 SELECT子句 一、INSERT 语句中使用 SELECT子句 在 INSERT 语句中使用 SELECT子句,可以将一个或多个表或视图中的数据添加到另外一个表中。使用 SELECT 子句还可以…...
Golang | Leetcode Golang题解之第274题H指数
题目: 题解: func hIndex(citations []int) int {// 答案最多只能到数组长度left,right:0,len(citations)var mid intfor left<right{// 1 防止死循环mid(leftright1)>>1cnt:0for _,v:range citations{if v>mid{cnt}}if cnt>mid{// 要找…...
区块链技术在智能家居中的创新应用探索
随着物联网技术的发展和智能家居市场的蓬勃发展,区块链技术作为一种去中心化的数据管理和安全保障技术,正在逐渐引入智能家居领域,并为其带来了新的创新应用。本文将探讨区块链技术在智能家居中的具体应用场景、优势以及未来发展方向。 智能家…...
无需业务改造,一套数据库满足 OLTP 和 OLAP,GaiaDB 发布并行查询能力
在企业中通常存在两类数据处理场景,一类是在线事务处理场景(OLTP),例如交易系统,另一类是在线分析处理场景(OLAP),例如业务报表。 OLTP 数据库擅长处理数据的增、删、改,…...
PHP 表单验证:邮件和URL
PHP 表单验证:邮件和URL 在Web开发中,表单验证是一个至关重要的环节,它确保了用户输入的数据的有效性和安全性。特别是在处理邮件地址和URL时,准确的验证尤为重要。本文将详细介绍如何使用PHP来验证表单中的邮件地址和URL。 邮件…...
前端八股文 路由的懒加载
为什么会有 路由的懒加载 在现代单页应用(SPA)的开发中,路由懒加载是一种提升应用性能的关键技术。通过按需加载组件,而非在应用启动时一次性加载所有模块,可以显著减少初次加载时间和资源消耗。本文旨在深入探讨前端…...
HarmonyOS Web组件(二)
1. HarmonyOS Web组件 官方文档 1.1. 混合开发的背景和好处 混合开发(Hybrid Development)是一种结合原生应用和Web应用的开发模式,旨在同时利用两者的优势。随着移动应用需求的多样化和复杂化,单一的开发方式往往难以满足所有…...
HarmonyOS应用开发者高级认证,Next版本发布后最新题库 - 单选题序号2
基础认证题库请移步:HarmonyOS应用开发者基础认证题库 注:有读者反馈,题库的代码块比较多,打开文章时会卡死。所以笔者将题库拆分,单选题20个为一组,多选题10个为一组,题库目录如下,…...
基于python深度学习遥感影像地物分类与目标识别、分割实践技术应用
目录 专题一、深度学习发展与机器学习 专题二、深度卷积网络基本原理 专题三、TensorFlow与Keras介绍与入门 专题四、PyTorch介绍与入门 专题五、卷积神经网络实践与遥感图像场景分类 专题六、深度学习与遥感图像检测 专题七、遥感图像检测案例 专题八、深度学习与遥感…...
叶再豪降龙精英课程总结
文章目录 1.思维认知1.1 稻盛和夫成功公式1.2 龙头主升模式1.3 龙头主升-两种路径1.4 股市新手的炒股思路1.5 龙头案例1.6 降龙心法 2.情绪周期2.1 情绪周期2.1 情绪演绎周期2.2 情绪的四个部分2.2.1 指数的情绪周期2.2.3 热点情绪周期2.2.4 热点情绪演绎周期2.2.5 大热点支线2…...
算法 - 查找算法(顺序、折半、红黑树、AVL树、B+树、散列)
查找 顺序查找 查找算法原理: 顺序查找是一种简单的查找方法,从数组的第一个元素开始,依次比较每个元素,直到找到目标元素或者数组结束为止。 实现步骤: 从数组的第一个元素开始。逐一比较数组中的元素与目标值。如…...
TCP与UDP网络编程
网络通信协议 java.net 包中提供了两种常见的网络协议的支持: UDP:用户数据报协议(User Datagram Protocol)TCP:传输控制协议(Transmission Control Protocol) TCP协议与UDP协议 TCP协议 TCP协议进行通信的两个应用进程:客户端、服务端 …...
媲美Midjourney-v6,Kolors最新文生图模型部署
Kolors模型是由快手团队开发的大型文本到图像生成模型,专门用于将文本描述转换成高质量的图像。 Kolors模型支持中英文双语输入,生成效果与Midjourney-v6相媲美,能够处理长达256个字符的文本输入,具备生成中英文文字的能力。 Ko…...
门户网站开发模板/seo网站排名的软件
最近遇到一个很奇怪的问题,两个项目弹出的dialog背景颜色不一样,一个是黑色的,一个是白色的,最后发现是AndroidManifest.xml文件里面application指定的android:theme设置的样式不一样。 黑色dialog背景效果图: dialog是黑色的时候application指定的样式如下: <!-…...
幕墙设计师培训/深圳网络优化推广公司
Log4Dom是模仿Log4J的思想建立的。Log4J能够向多种记录媒介以统一的格式写入各种级别的日志信息(包括错误、调试和信息等),还可以籍配置文件在运行时方便地修改记入日志的级别。Log4Dom提供了类似的功能。现在我们就来看看它的各部分元素和代…...
deramweaver做网站/系统优化是什么意思
dogdog是人类的朋友,古今中外,人们都很爱dog,英语中也有很多和dog相关的词汇。1.Every dog has its day.每条狗都有他的那天,意思就是人生总有出头之日,每个人都有春风得意的日子。2.Let sleeping dogs lie.让睡着的狗继续躺着吧!…...
白云区网站建设/网络营销论文
摘 要 在农业生产活动中,温度和湿度是影响农作物生长的重要因素。因此,对农作物生长的温湿度监测显得尤为重要。随着互联网技术的飞速发展,“互联网农业”已成为促进农业规模化生产的新动力,采用“互联网农业”的生产模式能有效的…...
学做美食去哪个网站好/软文推广的标准类型
自动生成代码工具 模板文件 package ${javapackage}.po;/*** <p>${title}</p>* 类名:${className}PO<br>* 创建人:${author}<br>* 创建时间:${.now?string("yyyy-MM-dd")}<br>*/ public class ${className}PO {<#list javaPropert…...
阿里云的云服务器做网站用哪种/互联网关键词优化
经常使用安卓手机的小伙伴,很多人应该都发现了:不少安卓手机上有一款名叫“便签”的app软件。这是手机系统自带的记事备忘小工具,无需下载安装,在手机桌面上找到它并点击,即可打开记录备忘内容,很方便&…...