当前位置: 首页 > news >正文

Effective Java 案例分享(九)

46、使用无副作用的Stream

本章节主要举例了Stream的几种用法。

案例一:

// Uses the streams API but not the paradigm--Don't do this!
Map<String, Long> freq = new HashMap<>();
try (Stream<String> words = new Scanner(file).tokens()) {words.forEach(word -> {freq.merge(word.toLowerCase(), 1L, Long::sum);});
}

案例一使用了forEach,代码看上去像是stream,但是并不是。为了操作HashMap不得不使用循环,导致代码更长。

// Proper use of streams to initialize a frequency table
Map<String, Long> freq;
try (Stream<String> words = new Scanner(file).tokens()) {freq = words.collect(groupingBy(String::toLowerCase, counting()));
}

代码二使用了Stream语法做了和代码一相同的事情,但是代码更短,语义更明确。ForEach应该只负责结果的输出,而不是用来做计算。

案例二:

// Pipeline to get a top-ten list of words from a frequency table
List<String> topTen = freq.keySet().stream().sorted(comparing(freq::get).reversed()).limit(10).collect(toList());

案例二给freq做排序,输出做多10个元素到List。**Collectors包含很多常用的静态方法,所以直接静态引用Collectors是非常明智的。**例如案例中的comparing方法。

案例三:

// Using a toMap collector to make a map from string to enum
private static final Map<String, Operation> stringToEnum = Stream.of(values()).collect(toMap(Object::toString, e -> e));

案例三将valus的值输出为以String为key, value为Operation的Map。

案例四:

// Collector to generate a map from key to chosen element for key
Map<Artist, Album> topHits 
= albums.collect(toMap(Album::artist, a->a, maxBy(comparing(Album::sales))));

案例四将albums输出为,Album::artist为key,Album为value,并按照Album::sales排序。

案例五:

Map<String, Long> freq = words.collect(groupingBy(String::toLowerCase, counting()));

案例五将words的key变为小写,并出输出。

47、返回值优先使用Collection,而不是Stream

如果一个方法需要按顺序返回多个数据,推荐的返回值类型为Collection。常用的Collection有List,Map。大多数情况下都需要遍历数据,Stream虽然也可以,但是遍历不如Collection方便,并且Collection可以方便的转成Stream。但是不要为了返回Collection而保存大量的元素。

例如:Set有a,b,c三个元素,返回所有的元素组合。 结果:{a},{ab},{abc},{ac},{b},{bc},{c},{}。

结果的个数是当前元素数量的2的n次方,如果Set里面包含更多的子元素,把所有的结果保存下来返回Collection就会占用非常大的内容空间。

// Returns a stream of all the sublists of its input list
public class SubLists {public static <E> Stream<List<E>> of(List<E> list) {return Stream.concat(Stream.of(Collections.emptyList()),prefixes(list).flatMap(SubLists::suffixes));}private static <E> Stream<List<E>> prefixes(List<E> list) {return IntStream.rangeClosed(1, list.size()).mapToObj(end -> list.subList(0, end));}private static <E> Stream<List<E>> suffixes(List<E> list) {return IntStream.range(0, list.size()).mapToObj(start -> list.subList(start, list.size()));}
}

Stream的写法类似使用了for-loop:

for (int start = 0; start < src.size(); start++)for (int end = start + 1; end <= src.size(); end++)System.out.println(src.subList(start, end));

48、谨慎的使用Stream并发

Stream提供了parallel()函数用于多线程操作,目标是提高运行效率,但是实际上可能并不会这样。

// Stream-based program to generate the first 20 Mersenne primes
public static void main(String[] args) {primes().map(p -> TWO.pow(p.intValueExact()).subtract(ONE)).filter(mersenne -> mersenne.isProbablePrime(50)).limit(20).forEach(System.out::println);
}
static Stream<BigInteger> primes() {return Stream.iterate(TWO, BigInteger::nextProbablePrime);
}

上面的代码正常运行时间为12.5s,使用了parallel()函数后,代码的速度并没与提升,且cpu提高到90%一直未执行结束,作者在半小时后强制关闭了程序。

如果资源来自Stream.iterate或者limit这种有中间操作,让管道并行不太可能提升提升效率。所以不能随意的使用parallel()。**如果Stream的数据来自于ArrayList , HashMap , HashSet , ConcurrentHashMap instances,arrays,int ranges,long ranges,使用parallel会让运行效率更高。**让Stream并行除了导致运行效率降低,还有可能出现错误的结果以及不可以预料的情况,所以在使用paralle()一定要经过测试验证,保证自己编写的代码运行正确。

在合适的环境下,Stream在多核机器下使用paralle()会得到接近线性的加速,例如如下代码:

// Prime-counting stream pipeline - benefits from parallelization
static long pi(long n) {return LongStream.rangeClosed(2, n).mapToObj(BigInteger::valueOf).filter(i -> i.isProbablePrime(50)).count();
}// Prime-counting stream pipeline - parallel version
static long pi(long n) {return LongStream.rangeClosed(2, n)// 此处使用了并行.parallel().mapToObj(BigInteger::valueOf).filter(i -> i.isProbablePrime(50)).count();
}

在作者的机器上第一个代码运行时间耗时31s,使用parallel()之后耗时降到9.2s。

49、检查参数的合法性

在大多数的方法和构造函数中都需要传递必要的参数,对每一个参数的合法性验证是非常重要的。在public和protected方法中,需要在JavaDoc说明参数的含义和有效范围,如果参数不合法是否抛出异常:

/**
* Returns a BigInteger whose value is (this mod m). This method
* differs from the remainder method in that it always returns a
* non-negative BigInteger.
*
* @param m the modulus, which must be positive
* @return this mod m
* @throws ArithmeticException if m is less than or equal to 0
*/
public BigInteger mod(BigInteger m) {if (m.signum() <= 0)throw new ArithmeticException("Modulus <= 0: " + m);... // Do the computation
}

常见的NullPointerException,可以使用@Nullable注解标注参数不可为null,在Java 7中,提供了Objects.requireNonNull方法帮助检查对象是否为空,为空则会抛出NullPointerException。在Java 9,java.util.Objects还提供了检查索引越界的方法:checkFromIndexSize , checkFromToIndex , checkIndex。还可以使用assert:

// Private helper function for a recursive sort
private static void sort(long a[], int offset, int length) {assert a != null;assert offset >= 0 && offset <= a.length;assert length >= 0 && length <= a.length - offset;... // Do the computation
}

如果断言不成立,将会抛出AssertionError。
总之检查参数合法性是非常必要的,它可以防止运行非法的参数造成程序的错误,每一个程序员都应该养成良好的编码习惯。

50、做必要的防御性Copy

为了防止保存的变量被其他人破坏,需要做一些防御性的对象拷贝。例如以下代码:

// Broken "immutable" time period class
public final class Period {private final Date start;private final Date end;/*** @param start the beginning of the period* @param end the end of the period; must not precede start* @throws IllegalArgumentException if start is after end* @throws NullPointerException if start or end is null*/public Period(Date start, Date end) {if (start.compareTo(end) > 0)throw new IllegalArgumentException(start + " after " + end);this.start = start;this.end = end;}public Date start() {return start;}public Date end() {return end;}... // Remainder omitted
}

Period的构造函数保存了start和end,但是这种做法不安全的,因为外部可以改变start和end的变量,所以不能保证此类运算结果不变:

// Attack the internals of a Period instance
Date start = new Date();
Date end = new Date();
Period p = new Period(start, end);
end.setYear(78); // Modifies internals of p!

为了解决此问题,应该保存start和end的副本,而不是直接保存start和end:

// Repaired constructor - makes defensive copies of parameters
public Period(Date start, Date end) {this.start = new Date(start.getTime());this.end = new Date(end.getTime());if (this.start.compareTo(this.end) > 0)throw new IllegalArgumentException(this.start + " after " + this.end);
}

除了参数以外,返回值也需要考虑返回副本,尤其是List、Map、Array,要防止直接返回原始数据,导致外部增删改查影响了原始数据。

相关文章:

Effective Java 案例分享(九)

46、使用无副作用的Stream 本章节主要举例了Stream的几种用法。 案例一&#xff1a; // Uses the streams API but not the paradigm--Dont do this! Map<String, Long> freq new HashMap<>(); try (Stream<String> words new Scanner(file).tokens()) …...

SpringBoot复习:(56)使用@Transactional注解标记的方法的执行流程

首先&#xff0c;如果在某个类或某个方法被标记为Transactional时&#xff0c;Spring boot底层会在创建这个bean时生成代理对象&#xff08;默认使用cglib) 示例&#xff1a; 当调用studentService的addStudent方法时&#xff0c;会直接跳到CglibAopProxy类去执行intercept方…...

JVM——引言+JVM内存结构

引言 什么是JVM 定义: Java VirtualMachine -java 程序的运行环境 (ava 二进制字节码的运行环境) 好处: 一次编写&#xff0c;到处运行自动内存管理&#xff0c;垃圾回收功能数组下标越界检查&#xff0c;多态 比较: jvm jre jdk 学习jvm的作用 面试理解底层实现原理中…...

open cv学习 (十)图形检测

图形检测 demo1 # 绘制几何图像的轮廓 import cv2img cv2.imread("./shape1.png")gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 将图像二值化 t, binary cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)# 检测图像中的所有轮廓 contours, hierarchy cv2.f…...

【C语言】字符函数和字符串函数

目录 1.求字符串长度strlen 2.长度不受限制的字符串函数 字符串拷贝strcpy 字符串追加strcat 字符串比较strcmp 3.长度受限制的字符串函数介绍strncpy strncat ​编辑strncmp 4.字符串查找strstr 5.字符串分割strtok 6.错误信息报告 strerror perror 7.字符分类函…...

前馈神经网络正则化例子

直接看代码&#xff1a; import torch import numpy as np import random from IPython import display from matplotlib import pyplot as plt import torchvision import torchvision.transforms as transforms mnist_train torchvision.datasets.MNIST(root…...

spring的核心技术---bean的生命周期加案例分析详细易懂

目录 一.spring管理JavaBean的初始化过程&#xff08;生命周期&#xff09; Spring Bean的生命周期&#xff1a; 二.spring的JavaBean管理中单例模式及原型&#xff08;多例&#xff09;模式 2.1 . 默认为单例&#xff0c;但是可以配置多例 2.2.举例论证 2.2.1 默认单例 2.2…...

【Maven教程】(一)入门介绍篇:Maven基础概念与其他构建工具:理解构建过程与Maven的多重作用,以及与敏捷开发的关系 ~

Maven入门介绍篇 1️⃣ 基础概念1.1 构建1.2 maven对构建的支持1.3 Maven的其他作用 2️⃣ 其他构建工具2.1 IDE2.2 Make2.3 Ant2.4 Jenkins 3️⃣ Maven与敏捷开发&#x1f33e; 总结 1️⃣ 基础概念 "Maven"可以翻译为 “知识的积累者” 或 “专家”。这个词源于波…...

今天,谷歌Chrome浏览器部署抗量子密码

谷歌已开始部署混合密钥封装机制&#xff08;KEM&#xff09;&#xff0c;以保护在建立安全的 TLS 网络连接时共享对称加密机密。 8月10日&#xff0c;Chrome 浏览器安全技术项目经理Devon O’Brien解释说&#xff0c;从 8 月 15 日发布的 Chrome 浏览器 116 开始&#xff0c;谷…...

SUMO traci接口控制电动车前往充电站充电

首先需要创建带有停车位的充电站(停车场和充电站二合一)&#xff0c;具体参考我的专栏中其他文章。如果在仿真的某个时刻&#xff0c;希望能够控制电动车前往指定的充电站充电&#xff0c;并且在完成充电后继续前往车辆原来的目的地&#xff0c;那么可以使用以下API&#xff1a…...

现代CSS中的换行布局技术

在现代网页设计中&#xff0c;为了适应不同屏幕尺寸和设备类型&#xff0c;换行布局是一项重要的技术。通过合适的布局技术&#xff0c;我们可以实现内容的自适应和优雅的排版。本文将介绍CSS中几种常见的换行布局技术&#xff0c;探索它们的属性、代码示例和解析&#xff0c;帮…...

简单理解Python中的深拷贝与浅拷贝

I. 简介 深拷贝会递归的创建一个完全独立的对象副本&#xff0c;包括所有嵌套的对象&#xff0c;而浅拷贝只复制嵌套对象的引用&#xff0c;不复制嵌套对象本身。 简单来说就是两者都对原对象进行了复制&#xff0c;因此使用is运算符来比较新旧对象时&#xff0c;返回的都是F…...

C++之std::pair<uint64_t, size_t>应用实例(一百七十七)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…...

前端打开后端返回的HTML格式的数据

前端打开后端返回的 HTML格式 的数据&#xff1a; 后端返回的数据格式如下示例&#xff1a; 前端通过 js 方式处理&#xff08;核心代码如下&#xff09; console.log(回调, path); // path 是后端返回的 HTML 格式数据// 必须要存进localstorage&#xff0c;否则会报错&am…...

How to deal with document-oriented data

Schema designData models for e-commerceNuts and bolts of databases, collection, and documents. Principles of schema design What are your application access pattern?Whats the basic unit of data? the basic unit of data is the BSON documentWhat are the ca…...

Http 状态码汇总

文章目录 Http 状态码汇总1xx&#xff08;信息性状态码&#xff09;2xx&#xff08;成功状态码&#xff09;3xx&#xff08;重定向状态码&#xff09;4xx&#xff08;客户端错误状态码&#xff09;5xx&#xff08;服务器错误状态码&#xff09; Http 状态码汇总 1xx&#xff08…...

mysql自定义实体类框架

根据表结构自动生产实体类和方法,根据反射与io生成,可自定义扩展方法 package com.digital.web.front; /*** pom依赖* <dependency>* <groupId>mysql</groupId>* <artifactId>mysql-connector-java</artifactId>* <version>5.1.27</ve…...

批量将Excel中的第二列内容从拼音转换为汉字

要批量将Excel中的第二列内容从拼音转换为汉字&#xff0c;您可以使用Python的openpyxl库来实现。下面是一个示例代码&#xff0c;演示如何读取Excel文件并将第二列内容进行拼音转汉字&#xff1a; from openpyxl import load_workbook from xpinyin import Pinyin # 打开Exce…...

消息推送:精准推送,提升运营效果,增添平台活力

对于app开发者而言&#xff0c;没有什么途径比消息推送更能直接、即时地触及目标用户群体了。消息推送与我们的日常生活息息相关&#xff0c;各种APP的状态和通知都通过消息推送来告知用户&#xff0c;引起用户的注意&#xff0c;吸引用户点开app。总而言之&#xff0c;推送服务…...

[保研/考研机试] KY43 全排列 北京大学复试上机题 C++实现

题目链接&#xff1a; 全排列https://www.nowcoder.com/share/jump/437195121692001512368 描述 给定一个由不同的小写字母组成的字符串&#xff0c;输出这个字符串的所有全排列。 我们假设对于小写字母有a < b < ... < y < z&#xff0c;而且给定的字符串中的字…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查

在对接支付宝API的时候&#xff0c;遇到了一些问题&#xff0c;记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql

智慧工地管理云平台系统&#xff0c;智慧工地全套源码&#xff0c;java版智慧工地源码&#xff0c;支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求&#xff0c;提供“平台网络终端”的整体解决方案&#xff0c;提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

MFC内存泄露

1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

前端倒计时误差!

提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...

python/java环境配置

环境变量放一起 python&#xff1a; 1.首先下载Python Python下载地址&#xff1a;Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个&#xff0c;然后自定义&#xff0c;全选 可以把前4个选上 3.环境配置 1&#xff09;搜高级系统设置 2…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

如何为服务器生成TLS证书

TLS&#xff08;Transport Layer Security&#xff09;证书是确保网络通信安全的重要手段&#xff0c;它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书&#xff0c;可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战

在现代战争中&#xff0c;电磁频谱已成为继陆、海、空、天之后的 “第五维战场”&#xff0c;雷达作为电磁频谱领域的关键装备&#xff0c;其干扰与抗干扰能力的较量&#xff0c;直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器&#xff0c;凭借数字射…...