lambda 流 peek java_JDK8 流与λ表达式

λ表达式

什么是λ表达式

λ表达式有三部分组成:参数列表,箭头(->),以及一个表达式或者语句块。

public int add(int x, int y) {

return x + y;

}

转换为λ表达式

(int x, int y) -> x + y;

去除参数类型

(x, y) -> x + y;

无参 以及 只有一个参数

() -> { System.out.println("Hello Lambda!");

c -> { return c.size(); }

λ表达式的类型

λ表达式可以被当做是一个Object(注意措辞)。λ表达式的类型,叫做“目标类型(target type)”。λ表达式的目标类型是“函数接口(functional interface)”,这是Java8新引入的概念。它的定义是:一个接口,如果只有一个显式声明的抽象方法,那么它就是一个函数接口。一般用@FunctionalInterface标注出来(也可以不标)。举例如下:

@FunctionalInterface

public interface Runnable{

void run();

}

public interface Callable{

V call() throws Exception;

}

public interface Comparator{

int compare(T o1, T o2); boolean equals(Object obj);

}

所以 可以定义

Runnable r1 = () -> {System.out.println("Hello Lambda!");};

思考:可否定义 Object o = () -> {System.out.println("Hello Lambda!");};

JDK常用的预定义接口函数

//入参为T,返回R

@FunctionalInterface

public interface Function {

R apply(T t);

}

//入参为T,无返回值

@FunctionalInterface

public interface Consumer {

void accept(T t);

}

//无入参,返回T(通常配合构造方法)

@FunctionalInterface

public interface Supplier {

T get();

}

//入参为T,返回值为boolean

@FunctionalInterface

public interface Predicate {

boolean test(T t);

}

//入参为T,U,返回值为R 类似还有BiConsumer等

@FunctionalInterface

public interface BiFunction {

R accept(T t, U u);

}

方法引用

方法引用让开发者可以直接引用现存的方法、Java类的构造方法或者实例对象。方法引用和Lambda表达式配合使用,使得java类的构造方法看起来紧凑而简洁,没有很多复杂的模板代码。

public static class Car {

public static Car create( final Supplier supplier ) {

return supplier.get();

}

public static void collide( final Car car ) {

System.out.println( "Collided " + car.toString() );

}

public void follow( final Car another ) {

System.out.println( "Following the " + another.toString() );

}

public void repair() {

System.out.println( "Repaired " + this.toString() );

}

}

第一种方法引用的类型是构造器引用,语法是Class::new,或者更一般的形式:Class::new。注意:这个构造器没有参数。

Car car = Car.create(Car::new);

List cars = Arrays.asList(car);

第二种方法引用的类型是静态方法引用,语法是Class::static_method。注意:这个方法接受一个Car类型的参数。

cars.forEach(Car::collide);

第三种方法引用的类型是某个类的成员方法的引用,语法是Class::method,注意,这个方法没有定义入参:

cars.forEach(Car::repair);

第四种方法引用的类型是某个实例对象的成员方法的引用,语法是instance::method。注意:这个方法接受一个Car类型的参数:

final Car police = Car.create(Car::new);

cars.forEach(police::follow);

什么是流

Stream 不是数据结构,不保存数据,它是有关算法和计算的,就如同一个高级版本的迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。同时又与迭代器不同,迭代器只能串行操作,Stream可以并行化操作。

流的构成

当我们使用一个流的时候,通常包括三个基本步骤:

获取一个数据源(source)→数据转换→执行操作获取想要的结果,每次转换原有 Stream 对象不改变,返回一个新的 Stream 对象(可以有多次转换),这就允许对其操作可以像链条一样排列,变成一个管道

,如下图所示。

a6ee65618a1c

image

常用的构建流的方式:

集合Collection

Collection.stream()

Collection.parallelStream()

数组

Stream.of(T[] tArray)

多个相同类型对象

Stream.of("chaimm","peter","john");

流的基本使用

常见操作

中间操作

无状态

map (mapToInt, flatMap 等)、filter、peek

有状态

distinct、sorted、limit、skip

终结操作

非短路

forEach、forEachOrdered、toArray、reduce、collect、min、 max、 count

短路操作

anyMatch、allMatch、noneMatch、findFirst、findAny

Stream中的操作可以分为两大类:中间操作与终结操作

中间操作(Intermediate):一个流可以后面跟随零个或多个 intermediate 操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。中间操作又可以分为无状态(Stateless)操作与有状态(Stateful)操作,前者是指元素的处理不受之前元素的影响;后者是指该操作只有拿到所有元素之后才能继续下去。

终结操作(Terminal):一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果。终结操作又可以分为短路与非短路操作,短路是指遇到某些符合条件的元素就可以得到最终结果,比如找到第一个满足条件的元素。而非短路是指必须处理所有元素才能得到最终结果。

map/flatMap

对流中的每个元素执行一个函数,使得元素转换成另一种类型输出。

map 一对一 (入参 Function)

List persons = new ArrayLisy<>();

List result = persons.stream().map(x -> x.getId())

.collect(Collectors.toList());

List result = persons.stream().map(x -> {

Student s = new Student();

s.setName(x.getName());

return s;

}).collect(Collectors.toList());

flatMap 一对多 (入参 Function)

List> listAll = new ArrayList<>();

List result = listAll.stream().flatMap(x -> x.stream())

.collect(Collectors.toList());

List result = listAll.stream().flatMap(x -> x.stream())

.map(x -> {

Student s = new Student();

s.setName(x.getName());

return s;

}).collect(Collectors.toList());

filter

filter 对原始 Stream 进行某项测试,通过测试的元素被留下来生成一个新 Stream。 (入参 Predicate)

Integer[] nums = new Integer[]{1, 2, 3, 4, 5, 6};

Integer[] evens = Stream.of(nums).filter(n -> n%2 == 0)

.toArray(Integer[]::new);

List result = persons.stream().filter(x -> x.getAge() < 20)

.map(x -> {

Student s = new Student();

s.setName(x.getName());

return s;

}).collect(Collectors.toList());

peek

peek 方法我们可以拿到元素,然后做一些其他事情。(入参 Consumer)

List result = persons.stream().map(x -> x.getId())

.peek(x -> System.out.println(x))

.collect(Collectors.toList());

Map map = new HashMap<>();

List result = persons.stream()

.filter(x -> x.getAge() < 20 )

.peek(x -> map.put(x.getId(), x) )

.map(x -> {

Student s = new Student();

s.setName(x.getName());

return s;

}).collect(Collectors.toList());

limit/skip

limit 返回 Stream 的前面 n 个元素,skip 则是扔掉前 n 个元素

List result = persons.stream().map(x -> x.getId())

.limit(10).skip(3).collect(Collectors.toList());

sorted

对元素进行排序 (入参 Comparator)

List personList2 = persons.stream()

.sorted((p1, p2) -> p1.getName().compareTo(p2.getName()))

.collect(Collectors.toList());

forEach

对元素进行遍历消费 (入参 Consumer)

persons.stream().sorted((p1, p2) -> p1.getName().compareTo(p2.getName()))

.forEach(peron -> {

System.out.println(persion.getName());

});

collect

对元素进行收集

List personList2 = persons.stream()

.sorted((p1, p2) -> p1.getName().compareTo(p2.getName()))

.collect(Collectors.toList());

List personList2 = persons.stream()

.sorted((p1, p2) -> p1.getName().compareTo(p2.getName()))

.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);

Collectors.toMap

转换为MAP

Map personIdNameMap = persons.stream()

.collect(Collectors.toMap(Person::getId, Person::getName);

Map personIdNameMap = persons.stream()

.collect(Collectors.toMap(Person::getId, Person::getName, (v1,v2)->v1);

Collectors.groupingBy

分组

Map> personAgeMap = persons.stream()

.collect(Collectors.groupingBy(Person::getAge));

Map> personAgeMap = persons.stream()

.collect(Collectors.groupingBy(Person::getAge,

Collectors.toMap(Person::getId, Function.identity())));

Collectors.collectingAndThen

收集然后处理

Map personAgeMap = persons.stream()

.collect(Collectors.groupingBy(Person::getAge,

Collectors.collectingAndThen(

Collectors.toList(),

list->list.size()

)));

接口方法

接口default方法

默认方法使得开发者可以在不破坏兼容性的前提下,往现存接口中添加新的方法,即不强制那些实现了该接口的类也同时实现这个新加的方法。

默认方法和抽象方法之间的区别在于抽象方法需要实现,而默认方法不需要。接口提供的默认方法会被接口的实现类继承或者覆写

private interface HelloService {

default String sayHello() {

return "hello";

}

}

private static class HelloImpl implements HelloService {

}

private static class HelloWorldImpl implements HelloService {

@Override

public String sayHello() {

return "hello world";

}

}

思考:为啥要加入default方法?

接口static方法

private interface HelloService {

static boolean testHello(String s) {

return Objects.equals(s,"hello");

}

}

Stream流水线解决方案

Stage(Pipeline)

java8用Stage来记录Stream的中间操作,很多Stream操作会需要一个回调函数(Lambda表达式),因此一个完整的操作是构成的三元组。Stream中使用Stage的概念来描述一个完整的操作,并用某种实例化后的Pipeline来代表Stage,然后将具有先后顺序的各个Stage连到一起,就构成了整个流水线。

a6ee65618a1c

image

Sink

有了操作,我们需要将所有操作叠加起来,让流水线起到应有的作用,java用Sink来协调相邻Stage之间的调用关系。每个Stage必须实现opWrapSink方法。Sink接口的主要方法如下

方法名

作用

void begin(long size)

开始遍历元素之前调用该方法,通知Sink做好准备

void end()

所有元素遍历完成之后调用,通知Sink没有更多的元素了

boolean cancellationRequested()

是否可以结束操作,可以让短路操作尽早结束

void accept(T t)

遍历元素时调用,接受一个待处理元素,并对元素进行处理

每个Stage都会将自己的操作封装到一个Sink里,前一个Stage只需调用后一个方法即可,并不需要知道其内部是如何处理的。当然对于有状态的操作,Sink的begin()和end()方法也是必须实现的。比如Stream.sorted()是一个有状态的中间操作,其对应的Sink.begin()方法可能创建一个乘放结果的容器,而accept()方法负责将元素添加到该容器,最后end()负责对容器进行排序。对于短路操作,Sink.cancellationRequested()也是必须实现的,比如Stream.findFirst()是短路操作,只要找到一个元素,cancellationRequested()就应该返回true,以便调用者尽快结束查找。Sink的四个接口方法常常相互协作,共同完成计算任务。实际上Stream API内部实现的的本质,就是如何重载Sink的这四个接口方法。

map方法的主要实现

public final Stream map(Function super P_OUT, ? extends R> mapper) {

Objects.requireNonNull(mapper);

return new StatelessOp(this, StreamShape.REFERENCE,

StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) {

@Override

Sink opWrapSink(int flags, Sink sink) {

return new Sink.ChainedReference(sink) {

@Override

public void accept(P_OUT u) {

downstream.accept(mapper.apply(u));

}

};

}

};

}

static abstract class ChainedReference implements Sink {

protected final Sink super E_OUT> downstream;

public ChainedReference(Sink super E_OUT> downstream) {

this.downstream = Objects.requireNonNull(downstream);

}

@Override

public void begin(long size) {

downstream.begin(size);

}

@Override

public void end() {

downstream.end();

}

@Override

public boolean cancellationRequested() {

return downstream.cancellationRequested();

}

}

// Stream.sort()方法用到的Sink实现

class RefSortingSink extends AbstractRefSortingSink {

private ArrayList list;// 存放用于排序的元素

RefSortingSink(Sink super T> downstream, Comparator super T> comparator) {

super(downstream, comparator);

}

@Override

public void begin(long size) {

...

// 创建一个存放排序元素的列表

list = (size >= 0) ? new ArrayList((int) size) : new ArrayList();

}

@Override

public void end() {

list.sort(comparator);// 只有元素全部接收之后才能开始排序

downstream.begin(list.size());

if (!cancellationWasRequested) {// 下游Sink不包含短路操作

list.forEach(downstream::accept);// 2. 将处理结果传递给流水线下游的Sink

}

else {// 下游Sink包含短路操作

for (T t : list) {// 每次都调用cancellationRequested()询问是否可以结束处理。

if (downstream.cancellationRequested()) break;

downstream.accept(t);// 2. 将处理结果传递给流水线下游的Sink

}

}

downstream.end();

list = null;

}

@Override

public void accept(T t) {

list.add(t);// 1. 使用当前Sink包装动作处理t,只是简单的将元素添加到中间列表当中

}

}

多个Sink叠加

多个Stage组成的链路如图所示,那么什么时候出发执行结束操作(Terminal Operation),一旦调用某个结束操作,就会触发整个流水线的执行。

final void copyInto(Sink wrappedSink, Spliterator spliterator){

...

if (!StreamOpFlag.SHORT_CIRCUIT.isKnown(getStreamAndOpFlags())) {

wrappedSink.begin(spliterator.getExactSizeIfKnown());// 通知开始遍历

spliterator.forEachRemaining(wrappedSink);// 迭代

wrappedSink.end();// 通知遍历结束

}

...

}

元空间

永久代的消除

从JDK1.7开始,贮存在永久代的一部分数据已经转移到了Java Heap或者是Native Heap。符号引用(Symbols)转移到了native heap;字面量(interned strings)转移到了java heap;类的静态变量(class statics)转移到了java heap。但永久代仍然存在于JDK7,并没有完全的移除。在JDK1.8版本中永久带被彻底移除。永久代的参数-XX:PermSize和-XX:MaxPermSize也被移除。该参数在JDK1.8使用会有警告

元空间

JDK1.8将类信息存储在元空间中,元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,但可以通过以下参数来指定元空间的大小:

-XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。

-XX:MaxMetaspaceSize,最大空间,默认是没有限制的(取决于内存)。

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

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

相关文章

理解java虚拟机工作后了解吗_JAVA入门到再次入门——深入理解JAVA虚拟机(二)|七日打卡...

前言为什么叫做入门到到再次入门请参考前一篇或个人博客&#xff0c;在此不再赘述&#xff0c;嗯哼&#xff0c;了解了JVM的基本运行流程以及内存结构&#xff0c;算是初步认识了JVM&#xff0c;跟着课本往前走&#xff0c;继续了解根据JVM的内存模型探索java当中变量的可见性以…

java访问错误404_如何解决 Java web 项目中的 404 错误

在使用 Tomcat 进行 Java Web 开发的时候&#xff0c;经常会遇到以下 HTTP 404 错误&#xff1a;错误代码为 HTTP 404(未找到)&#xff0c;描述信息是&#xff1a;“The origin server did not find a current representation for the target resource or is not willing to di…

java double 的精度_Java Double的精度问题

Java.text类 DecimalFormatjava.lang.Objectjava.text.Formatjava.text.NumberFormatjava.text.DecimalFormatvoid setMaximumFractionDigits(int newValue) 设置某个数的小数部分中所允许的最大数字位数。void setMinimumFractionDigits(int newValue) …

java餐饮管理系统图片,基于jsp的酒店餐饮管理系统-JavaEE实现酒店餐饮管理系统 - java项目源码...

基于jspservletpojomysql实现一个javaee/javaweb的酒店餐饮管理系统, 该项目可用各类java课程设计大作业中, 酒店餐饮管理系统的系统架构分为前后台两部分, 最终实现在线上进行酒店餐饮管理系统各项功能,实现了诸如用户管理, 登录注册, 权限管理等功能, 并实现对各类酒店餐饮管…

php 验证码一直不对,ThinkPHP验证码老是出错怎么办

ThinkPHP验证码老是出错的解决办法&#xff1a;1、找到服务器php配置文件php.ini在网站根目录下建一个info.php文件。例如&#xff1a;D:\wwwRoot\wp 这个是网站的根目录&#xff0c;在此目录下&#xff0c;新建一个txt文档&#xff0c;输入如下代码&#xff1a;然后另存为info…

如何在php中插入数据并修改,php怎么同时向2张表里插入数据

情况是这个样子的&#xff1a;我要做一个发消息的表&#xff0c;因为接受人可能是多个&#xff0c;所以又给接收人一单独的表&#xff0c;(这种方案好还是全部都放到一张表里好点呢&#xff1f;)2张表的字段如下&#xff1a;message_id是第一张表的主键&#xff0c;如果收件人有…

java设计模式之道文字版,Java Web设计模式之道 PDF

资源名称&#xff1a;Java Web设计模式之道 PDF第一部分 仙人指路——设计模式简介第1章 设计模式概述1.1 设计模式是什么1.2 软件设计模式的发展历程1.3 作者阐述软件设计模式的主要方式第二部分 设计红宝书——设计模式原则详解第2章 设计原则之开闭原则2.1 何谓开闭原则2.2 …

matlab变量由非标量,matlab中的if语句

有条件性地执行语句语法if expressionstatementsend描述MATLAB计算表达式&#xff0c;如果产生一个逻辑真或者非零结果&#xff0c;然后就执行一条或者多条MATLAB命令语句。当有嵌套if时&#xff0c;每一个if必须和一个相应的end匹配。当你在if语句里面嵌套使用else if或者else…

rodbc 连接oracle,R語言 使用RODBC連接oracle數據庫

使用R語言有多種包可以連接oracle數據庫&#xff0c;我今天在這里講一下使用使用RODBC連接oracle數據庫。1. 如果你的本地是windows系統的話&#xff0c;你需要安裝oracle客戶端。2. 然后需要在ODBC管理者界面配置你要進行連接的數據庫數據及使用的驅動等信息。如下圖所示&…

oracle实验七 答案,Oracle表的常用查询实验(七)

Oracle表的常用查询实验(七)1.问题描述&#xff1a;有一个商品信息表&#xff0c;该表反应了各种商品的销售情况&#xff0c;一个产品是按照gid和gname两个字段来区分的&#xff0c;一个产品可能会有多个型号。create table T_Goods(Id int primary key,GId varchar2(10) not n…

oracle ogg00423,【案例】Oracle报错PLS-00378 PLS-00439产生原因和MOS官方解决办法

【案例】Oracle报错PLS-00378 PLS-00439产生原因和MOS官方解决办法时间:2016-11-12 21:31 来源:Oracle研究中心 作者:代某人 点击:次天萃荷净PLS-00378此版本的PL / SQL编译单元无效原因&#xff1a;编译单元是一个文件&#xff0c;其中包含的PL / SQL传递给编译器的源代…

linux系统如何备份系统软件,Linux折腾记(十三):我该如何备份系统

在前面的一些文章中&#xff0c;我反复提到经常会把系统搞崩溃&#xff0c;所以备份系统就是一件不容忽视的事情。由于Linux系统本身的优越性&#xff0c;系统的备份和还原还是比较容易的。主要表现在以下方面&#xff1a;1.Linux系统所有的数据都以文件的形式存在&#xff0c;…

linux kset subsystem 3.10内核,Kobject、Kset 和 Subsystem

2014年5月12日Kobject 、Kset 和 Subsy stem - 海王 - 博客园http://www.doczj.com/doc/93b5b6113b3567ec112d8a49.html/leav en/archiv e/2010/04/24/1719191.html 4/8件(2)4. Linux input 子系统 io 控制字段(2)5. linux 内核定时器 tim er_list 详解(2) (2)把kobject 的 kse…

linux 自定义vi dd,Vim常用命令及配置方案

几句话很久之前就接触到vim&#xff0c;初学那阵觉得vim很酷炫&#xff0c;但确实对新手不是很友好。我也就简单看了下基本操作就上手了&#xff0c;但又不是长期在vim下工作&#xff0c;这就导致了每一次重新使用vim都要再去回温下基本操作&#xff0c;很是难受&#xff0c;所…

linux指纹登录实现原理,指纹识别技术原理与基于Linux系统的指纹识别门禁系统设计...

指纹作为人体的身体特征&#xff0c;具有唯一性、稳定性和不易盗用等特点。随着指纹识别理论逐渐成熟、指纹采集工具这一难题得到解决&#xff0c;指纹识别已经成为目前最广泛应用的生物识别之一&#xff0c;逐渐取代了传统的认证识别方式&#xff0c;广泛应用于金融、公安、证…

linux wps历史版本下载,WPS Office 2019 For Linux 11.1.0.9604版携新更新发布下载

WPS Office 2019 For Linux 11.1.0.9604版本发布了&#xff0c;仅支持64位计算机&#xff0c;提供Deb和Rpm格式软件包下载。更新详情1、WPS公共&#xff1a;优化界面绘制相关api的调用效率。适配ibus和fcitx框架的输入法&#xff0c;解决无法输入中文&#xff0c;预输入显示等问…

Linux双独立显卡SLI,玩点新奇的 双显卡给你不同的“SLI”效果

显卡老是千篇一律的常规测试对于我们这些测试人员来说早就已经没有什么意思了&#xff0c;无非就是跑分&#xff0c;超频后跑分&#xff0c;拷机等等一些测试项目&#xff0c;常看我们测试的玩家应该也知道同芯片显卡除非是频率的差别非常大才会有较为明显的性能差距体现出来&a…

android win10 驱动安装失败,解决在win10系统下小米手机驱动安装失败的具体步骤...

随着系统的升级越来越快&#xff0c;对于许多用户的系统都已经升级到win10系统了&#xff0c;win10系统在给用户带来方便的同时&#xff0c;有时候也会遇到一些小麻烦&#xff0c;比如说对于最新的Window10正式版系统而言&#xff0c;基本主流的手机驱动都能自动检测安装。不过…

z3735f android x86,英特尔的Atom Z3735F分析 全文总结_台电 X80HD_平板电脑评测-中关村在线...

硬件配置&#xff1a;英特尔64位提供性能保证台电X80HD双系统使用Intel Atom Z3735F四核处理器&#xff0c;最高睿频可达1.83GHz&#xff0c;其采用64位X86架构&#xff0c;搭载最新一代睿频加速技术&#xff0c;并拥有四核四线程&#xff0c;支持多任务同时处理。英特尔的Atom…

浏览器 android x86,360浏览器x86版下载

360浏览器x86版下载&#xff0c;是专门针对现在越来越多的win10和安卓双系统的国产平板电脑使用的安卓版app,例如台电平板&#xff0c;适用机型 适用于联想K800、联想K900、MOTO MT788、MOTO XT890、Acer联想平板等,只要安装了x86版本就可以愉快的玩耍啦&#xff0c;你还不赶快…