一直在用JDK8 ,却从未用过Stream,为了对数组或集合进行一些排序、过滤或数据处理,只会写for循环或者foreach,这就是我曾经的一个写照。

刚开始写写是打基础,但写的多了,各种乏味,非过来人不能感同身受。今天,我就要分享一篇如何解决上述问题的新方法 - Stream API。但学习Stream之前却不得不学一下Lambda表达式。说实话,网上介绍Lambda表达式的文章很多,大多晦涩难懂,今天我就想用自己的理解去说一下Lambda表达式是如何让我们的代码写的更少!

02来自IDEA的提示

在IDE中,你是否遇到在写以下列代码时,被友情提示的情况:

new Thread(new Runnable() {
@Override
public void run() {
System.out.println("thread");
}
});

  

这时候,我们按一下快捷键,IDE自动帮我们把代码优化为酱个样子:

new Thread(() -> System.out.println("thread"));

  

这就是Java8的新特性:lambda表达式。

03Lambda入门

借用上面的示例,在调用new Thread的含参构造方法时,我们通过匿名内部类的方式实现了Runnable对象,但其实有用的代码只有System.out.println("thread")这一句,而我们却要为了这一句去写这么多行代码。正是这个问题,才有了Java8中的lambda表达式。那lambd表达式究竟是如何简化代码的呢?

先来看lambda表达式的语法:

() -> {}

(): 括号就是接口方法的括号,接口方法如果有参数,也需要写参数。只有一个参数时,括号可以省略。

->: 分割左右部分的,没啥好说的。

{} : 要实现的方法体。只有一行代码时,可以不加括号,可以不写return。

看了上面的解释,也就不难理解IDE优化后的代码了。不过看到这里你也许意识到,如果接口中有多个方法时,按照上面的逻辑lambda表达式恐怕不行了。没错,lambda表达式只适用于函数型接口。说白了,函数型接口就是只有一个抽象方法的接口。这种类型的接口还有一个对应的注解:@FunctionalInterface。为了让我们在需要这种接口时不再自己去创建,Java8中内置了四大核心函数型接口。

消费型接口(有参无返回值)

Consumer<T>

void accept(T t);
供给型接口(无参有返回值) Supplier<T> T get();
函数型接口(有参有返回值) Function<T, R> R apply(T t);
断言型接口(有参有布尔返回值) Predicate<T> boolean test(T t);

  

看到这里如果遇到一般的lambda表达式,你应该可以从容面对了,但高级点的恐怕看到还是懵,不要急,其实也不难。Lambda表达式还有两种简化代码的手段,分别是方法引用构造引用

04方法引用

方法引用是什么呢?如果我们要实现接口的方法与另一个方法A类似,(这里的类似是指参数类型与返回值部分相同),我们直接声明A方法即可。也就是,不再使用lambda表达式的标准形式,改用高级形式。无论是标准形式还是高级形式,都是lambda表达式的一种表现形式。

举例:

Function function1 = (x) -> x;
Function function2 = String::valueOf;

  

对比Function接口的抽象方法与String的value方法,可以看到它们是类似的。

R apply(T t);

public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}

  

方法引用的语法:

对象::实例方法
类::静态方法
类::实例方法

前两个很容易理解,相当于对象调用实例方法,类调用静态方法一样。只是第三个需要特殊说明。

当出现如下这种情况时:

Compare<Boolean> c = (a, b) -> a.equals(b);

用lambda表达式实现Compare接口的抽象方法,并且方法体只有一行,且该行代码为参数1调用方法传入参数2。此时,就可以简化为下面这种形式:

Compare<Boolean> c = String::equals;

也就是“类::实例方法”的形式。

值得一提的是,当参数b不存在时,该方式依旧适用。例如:

Function function1 = (x) -> x.toString();
Function function1 = Object::toString;

05构造引用

先来创建一个供给型接口对象:

Supplier<String> supplier = () -> new String();

  

在这个lammbda表达式中只做了一件事,就是返回一个新的String对象,而这种形式可以更简化:

Supplier<String> supplier = String::new;

  

提炼一下构造引用的语法:

类名::new
当通过含参构造方法创建对象,并且参数列表与抽象方法的参数列表一致,也就是下面的这种形式: Function1 function = (x) -> new String(x);
也可以简化为: Function1 function = String::new;
特殊点的数组类型: Function<Integer,String[]> function = (x) -> new String[x];

  

可以简化为:

Function<Integer,String[]> function = String[]::new;

06Lambda总结

上面并没有给出太多的lambda实例,只是侧重讲了如何去理解lambda表达式。到这里,不要懵。要记住lambda的本质:为函数型接口的匿名实现进行简化与更简化。

所谓的简化就是lambda的标准形式,所谓的更简化是在标准形式的基础上进行方法引用和构造引用。

方法引用是拿已有的方法去实现此刻的接口。

构造引用是对方法体只有一句new Object()的进一步简化。

07Stream理解

如何理解Stream?在我看来,Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。简单来说,它的作用就是通过一系列操作将数据源(集合、数组)转化为想要的结果。

Stream有三点非常重要的特性:

  1. Stream 是不会存储元素的。

  2. Stream 不会改变原对象,相反,他们会返回一个持有结果的新Stream。

  3. Stream 操作是延迟执行的。意味着它们会等到需要结果的时候才执行。

08Stream生成

Collection系的 stream() 和 parallelStream()

List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
Stream<String> stringStream = list.parallelStream();

  

通过Arrays.stram()

Stream<String> stream1 = Arrays.stream(new String[10]);

  

通过Stream.of()

Stream<Integer> stream2 = Stream.of(1, 2, 3);

  

通过Stream.iterate()生成无限流

Stream<Integer> iterate = Stream.iterate(0, (x) -> x + 2);
iterate.limit(10).forEach(System.out::println);

  

通过Stream.generate()

Stream<Double> generate = Stream.generate(() -> Math.random());generate.forEach(System.out::println);

 

09Stream中间操作

多个中间操作连接而成为流水线,流水线不遇到终止操作是不触发任何处理的,所为又称为“惰性求值”。

list.stream()
.map(s -> s + 1) //映射
.flatMap(s -> Stream.of(s)) //和map差不多,但返回类型为Stream,类似list.add()和list.addAll()的区别
.filter(s -> s < 1000) //过滤
.limit(5) //限制
.skip(1) //跳过
.distinct() //去重
.sorted() //自然排序
.sorted(Integer::compareTo) //自定义排序

  

关于map方法,参数为一个Function函数型接口的对象,也就是传入一个参数返回一个对象。这个参数就是集合中的每一项。类似Iterator遍历。其它的几个操作思想都差不多。

执行上面的方法没什么用,因为缺少终止操作。

10Stream的终止操作

常用的终止API如下:

list.stream().allMatch((x) -> x == 555); // 检查是否匹配所有元素
list.stream().anyMatch(((x) -> x>600)); // 检查是否至少匹配一个元素
list.stream().noneMatch((x) -> x>500); //检查是否没有匹配所有元素
list.stream().findFirst(); // 返回第一个元素
list.stream().findAny(); // 返回当前流中的任意一个元素
list.stream().count(); // 返回流中元素的总个数
list.stream().forEach(System.out::println); //内部迭代
list.stream().max(Integer::compareTo); // 返回流中最大值
Optional<Integer> min = list.stream().min(Integer::compareTo);//返回流中最小值
System.out.println("min "+min.get());

  

reduce (归约):将流中元素反复结合起来得到一个值

Integer reduce = list.stream()
.map(s -> (s + 1))
.reduce(0, (x, y) -> x + y); //归约:0为第一个参数x的默认值,x是计算后的返回值,y为每一项的值。
System.out.println(reduce); Optional<Integer> reduce1 = list.stream()
.map(s -> (s + 1))
.reduce((x, y) -> x + y); // x是计算后的返回值,默认为第一项的值,y为其后每一项的值。
System.out.println(reduce);

  

collect(收集):将流转换为其他形式。需要Collectors类的一些方法。

//转集合
Set<Integer> collect = list.stream()
.collect(Collectors.toSet()); List<Integer> collect2 = list.stream()
.collect(Collectors.toList()); HashSet<Integer> collect1 = list.stream()
.collect(Collectors.toCollection(HashSet::new)); //分组 {group=[444, 555, 666, 777, 555]}
Map<String, List<Integer>> collect3 = list.stream()
.collect(Collectors.groupingBy((x) -> "group"));//将返回值相同的进行分组
System.out.println(collect3); //多级分组 {group={777=[777], 666=[666], 555=[555, 555], 444=[444]}}
Map<String, Map<Integer, List<Integer>>> collect4 = list.stream()
.collect(Collectors.groupingBy((x) -> "group", Collectors.groupingBy((x) -> x)));
System.out.println(collect4); //分区 {false=[444], true=[555, 666, 777, 555]}
Map<Boolean, List<Integer>> collect5 = list.stream()
.collect(Collectors.partitioningBy((x) -> x > 500));
System.out.println(collect5); //汇总
DoubleSummaryStatistics collect6 = list.stream()
.collect(Collectors.summarizingDouble((x) -> x));
System.out.println(collect6.getMax());
System.out.println(collect6.getCount()); //拼接 444,555,666,777,555
String collect7 = list.stream()
.map(s -> s.toString())
.collect(Collectors.joining(","));
System.out.println(collect7);

java8 新特性 Stream流 分组 排序 过滤 多条件去重

public class Streams {

	private static List<User> list = new ArrayList<User>();

	public static void main(String[] args) {
list = Arrays.asList(
new User(1, "a", 10),
new User(4, "d", 19),
new User(5, "e", 13),
new User(2, "b", 14),
new User(3, "a", 10),
new User(6, "f", 16)
); long start = System.currentTimeMillis(); order(); println(String.format("耗时[%s]毫秒", (System.currentTimeMillis() - start))); } /**
* 多条件去重
* @param list
*/
public static void order() {
list.stream().collect(Collectors.collectingAndThen(
Collectors.toCollection(() -> new TreeSet<>(
Comparator.comparing(o -> o.getAge() + ";" + o.getName()))), ArrayList::new)).forEach(u -> println(u));
} public static void group() {
Map<Integer, List<User>> collect = list.stream().collect(Collectors.groupingBy(User::getAge));
System.out.println(collect);
} /**
* filter过滤
* @param list
*/
public static void filterAge() {
list.stream().filter(u -> u.getAge() == 10).forEach(u -> println(u));
} /**
* sorted排序
*/
public static void stord() {
list.stream().sorted(Comparator.comparing(u-> u.getAge())).forEach(u -> println(u)); }
/**
* limit方法限制最多返回多少元素
*/
public static void limit() {
list.stream().limit(2).forEach(u -> println(u));
}
/**
* 不要前多n个元素,n大于满足条件的元素个数就返回空的流
*/
public static void skip() {
list.stream().skip(2).forEach(u -> println(u));
}
// 最大值 最小值
public static void statistics() {
Optional<User> min = list.stream().min(Comparator.comparing(User::getUserId));
println(min);
Optional<User> max = list.stream().max(Comparator.comparing(User::getUserId));
println(max);
} // 统计
public static void summarizingInt(){
IntSummaryStatistics statistics = list.stream().collect(Collectors.summarizingInt(User::getAge));
double average = statistics.getAverage();
long count = statistics.getCount();
int max = statistics.getMax();
int min = statistics.getMin();
long sum = statistics.getSum();
println(average);
println(count);
println(min);
println(sum);
println(max); }
/**
* 转set
*/
public static void toSet() {
Set<User> collect = list.stream().collect(Collectors.toSet());
Iterator<User> iterator = collect.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next().getUserId());
}
} /**
* 转map
*/
public static void toMap() {
Map<Integer, User> collect = list.stream().collect(Collectors.toMap(User::getUserId, u -> u));
for (Integer in : collect.keySet()) {
User u = collect.get(in);//得到每个key多对用value的值
println(u);
}
}
/**
*map
*/
public static void map() {
list.stream().map(User::getUserId).forEach(userId -> println(userId));
list.stream().mapToInt(User::getAge).forEach(userId -> println(userId));
list.stream().mapToDouble(User::getUserId).forEach(userId -> println(userId));
list.stream().mapToLong(User::getUserId).forEach(userId -> println(userId));
} /**
* 查找与匹配
* allMatch方法与anyMatch差不多,表示所有的元素都满足才返回true。noneMatch方法表示没有元素满足
*/
public static void anyMatch() {
boolean anyMatch = list.stream().anyMatch(u -> u.getAge() == 100);
boolean allMatch = list.stream().allMatch(u -> u.getUserId() == 10);
boolean noneMatch = list.stream().noneMatch(u -> u.getUserId() == 10);
println(anyMatch);
println(allMatch);
println(noneMatch);
} /**
* reduce操作
*/
public static void reduce() {
Optional<Integer> sum = list.stream().map(User::getAge).reduce(Integer::sum);
Optional<Integer> max = list.stream().map(User::getAge).reduce(Integer::max);
Optional<Integer> min = list.stream().map(User::getAge).reduce(Integer::min);
println(sum);
println(max);
println(min);
}
/**
* 公共输出
* @param c
*/
public static void println(Object c) {
System.out.println(c.toString());
} static class User {
private Integer userId; private String name; private Integer age; User(Integer userId, String name, Integer age) {
super();
this.userId = userId;
this.name = name;
this.age = age;
} public Integer getUserId() {
return userId;
} public void setUserId(Integer userId) {
this.userId = userId;
} public String getName() {
return this.name;
} public void setName(String name) {
this.name = name;
} public Integer getAge() {
return age;
} public void setAge(Integer age) {
this.age = age;
} @Override
public String toString() {
return "[userId=" + userId + ", name=" + name + ", age=" + age + "]";
} }

  

 

Java8的lambda表达式和Stream API的更多相关文章

  1. 十分钟学会Java8:lambda表达式和Stream API

    Java8 的新特性:Lambda表达式.强大的 Stream API.全新时间日期 API.ConcurrentHashMap.MetaSpace.总得来说,Java8 的新特性使 Java 的运行 ...

  2. 十分钟学会Java8的lambda表达式和Stream API

    01:前言一直在用JDK8 ,却从未用过Stream,为了对数组或集合进行一些排序.过滤或数据处理,只会写for循环或者foreach,这就是我曾经的一个写照. 刚开始写写是打基础,但写的多了,各种乏 ...

  3. java8中Lambda表达式和Stream API

    一.Lambda表达式 1.语法格式 Lambda是匿名函数,可以传递代码.使用“->”操作符,改操作符将lambda分成两部分: 左侧:指定了 Lambda 表达式需要的所有参数 右侧:指定了 ...

  4. java8新特性-lambda表达式和stream API的简单使用

    一.为什么使用lambda Lambda 是一个 匿名函数,我们可以把 Lambda表达式理解为是 一段可以传递的代码(将代码像数据一样进行传递).可以写出更简洁.更灵活的代码.作为一种更紧凑的代码风 ...

  5. JDK1.8新特性(一) ----Lambda表达式、Stream API、函数式接口、方法引用

    jdk1.8新特性知识点: Lambda表达式 Stream API 函数式接口 方法引用和构造器调用 接口中的默认方法和静态方法 新时间日期API default   Lambda表达式     L ...

  6. Java8中Lambda表达式的10个例子

    Java8中Lambda表达式的10个例子 例1 用Lambda表达式实现Runnable接口 //Before Java 8: new Thread(new Runnable() { @Overri ...

  7. JDK1.8中的Lambda表达式和Stream

    1.lambda表达式 Java8最值得学习的特性就是Lambda表达式和Stream API,如果有python或者javascript的语言基础,对理解Lambda表达式有很大帮助,因为Java正 ...

  8. Java8 新特性学习 Lambda表达式 和 Stream 用法案例

    Java8 新特性学习 Lambda表达式 和 Stream 用法案例 学习参考文章: https://www.cnblogs.com/coprince/p/8692972.html 1.使用lamb ...

  9. java8的lambda表达式,将List<DTO> 转为 List<DO>

    将List<PhoneDTO>转为List<PhoneDO>,通过java8的lambda表达式来操作,比传统的for循环精简很多: /** * List<PhoneDT ...

随机推荐

  1. Unity3D连接本地或局域网MySQL数据库

    准备工作: 1.打开 Unity3D 安装目录,到这个路径下 Editor > Data > Mono > lib > mono > 2.0 拷贝出下图的五个动态链接库, ...

  2. (转载)RHEL7(RedHat 7)本地源的配置

    配置yum源 1.首先连接RedHat7的DVD再把挂载DVD光盘到/mnt   因为配置时候路径名里面不能有空格,否则不能识别  [root@ mnt]# mount /dev/cdrom /mnt ...

  3. Shell 中字符串变量的赋值注意点

    1. 变量赋值 语法:var="saaaa" PS: 等号两边不能有空格 2. 脚本示例如下: #!/bin/sh # Get bug activity info # usage ...

  4. InstallShield 创建 visual studio 工程的时候 指向 任意 visual studio 版本 方法 (修改 计算机 默认 visual studio shell 版本)

    这需要 修改 计算机 默认 visual studio shell 版本 注册表 HKEY_CLASSES_ROOT VisualStudio.DTE 配置节点 描述的是 默认的 visual stu ...

  5. [转]决策树在Kaldi中如何使用

    转自:http://blog.csdn.net/chenhoujiangsir/article/details/51613144 说明:本文是kaldi主页相关内容的翻译(http://kaldi-a ...

  6. Variables and Arithmetic Expression

    Notes from The C Programming Language A decimal point in a constant indicates that it is floating po ...

  7. Avoiding Common Networking Mistakes

    https://developer.apple.com/library/content/documentation/NetworkingInternetWeb/Conceptual/Networkin ...

  8. 用jQuery修改右键菜单

    概述 以前在网上找过屏蔽右键菜单的代码,也找过屏蔽F12的代码,今天无意之中看到别人的右键菜单很有意思,我也想来搞一个. 思路 建立一个菜单并且隐藏起来. 用window.oncontextmenu屏 ...

  9. 基于vue的颜色选择器vue-color-picker

    项目中有用到颜色选择器的童鞋们可以看过来了 关于color-picker的jquery的插件是有蛮多,不过vue组件没有吧,反正我没有找到, 虽然element-ui里面有这个,但是你愿意为了一个小功 ...

  10. Web开发经验谈之F12开发者工具/Web调试[利刃篇]

    引语:如今的整个Web开发行业甚至说整个软件开发行业,已经相当成熟,基本上已经很少找不到没有前人做过的东西了,或者换句话说,你想要实现的功能,你总能在某个地方搜索到答案,关键是你有没有这个时间精力去搜 ...