当前位置: 首页 > 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;而且给定的字符串中的字…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)

可以使用Sqliteviz这个网站免费编写sql语句&#xff0c;它能够让用户直接在浏览器内练习SQL的语法&#xff0c;不需要安装任何软件。 链接如下&#xff1a; sqliteviz 注意&#xff1a; 在转写SQL语法时&#xff0c;关键字之间有一个特定的顺序&#xff0c;这个顺序会影响到…...

微信小程序云开发平台MySQL的连接方式

注&#xff1a;微信小程序云开发平台指的是腾讯云开发 先给结论&#xff1a;微信小程序云开发平台的MySQL&#xff0c;无法通过获取数据库连接信息的方式进行连接&#xff0c;连接只能通过云开发的SDK连接&#xff0c;具体要参考官方文档&#xff1a; 为什么&#xff1f; 因为…...

Caliper 配置文件解析:config.yaml

Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?

uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件&#xff0c;用于在原生应用中加载 HTML 页面&#xff1a; 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...

以光量子为例,详解量子获取方式

光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学&#xff08;silicon photonics&#xff09;的光波导&#xff08;optical waveguide&#xff09;芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中&#xff0c;光既是波又是粒子。光子本…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用

文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么&#xff1f;1.1.2 感知机的工作原理 1.2 感知机的简单应用&#xff1a;基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...

vulnyx Blogger writeup

信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面&#xff0c;gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress&#xff0c;说明目标所使用的cms是wordpress&#xff0c;访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...

手机平板能效生态设计指令EU 2023/1670标准解读

手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读&#xff0c;综合法规核心要求、最新修正及企业合规要点&#xff1a; 一、法规背景与目标 生效与强制时间 发布于2023年8月31日&#xff08;OJ公报&…...

图解JavaScript原型:原型链及其分析 | JavaScript图解

​​ 忽略该图的细节&#xff08;如内存地址值没有用二进制&#xff09; 以下是对该图进一步的理解和总结 1. JS 对象概念的辨析 对象是什么&#xff1a;保存在堆中一块区域&#xff0c;同时在栈中有一块区域保存其在堆中的地址&#xff08;也就是我们通常说的该变量指向谁&…...