前言

​ 由于项目中用到了比较多有关于 Java8 中新的东西,一开始自己只是会写,但是写起来不太顺,然后就在网上找到了一个很好的关于Java8新特性的视频,所以就进行了学习了一下,以下是自己对 lambda 表达式和 Stream API 的笔记和相应的理解。 视频地址,有兴趣的可以自行观看。

Java8 新特性

  1. 速度更快 更换了数据结构,内存结构(JVM)
  2. *代码更少了(Lambda表达式) *
  3. *强大的Stream API *
  4. 便于并行 fork join (串行切换并行)
  5. 最大化减少空指针异常 Optional

Lambda表达式

​ 在说 Lambda 之前,首先要说的是函数式接口。这个可以说是为了 Lambda 表达式而存在的一个东西。那么什么是函数式接口?

函数式接口

定义: 接口中只有一个抽象接口。

像 java.util.function 下的所有接口都是函数式接口。Java1.8提供@FunctionalInterface检测一个接口是否是一个函数式接口。

eg: java.util.function 包下的 Consumer 接口代码

@FunctionalInterface
public interface Consumer<T> { void accept(T t); // jdk 1.8 接口可以有默认实现
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}

了解了什么是函数式接口后,lambda 表达式就很好理解了。

​ "->" 是 lambda 表达式的符号 左侧表示函数式接口中抽象方法的参数列表,右侧表示你对这个方法的实现

举个例子eg:

public class Test{
public static void main(String[] args){
Consumer consumer = x-> System.out.println(x);
consumer.accept(1);
}
}

输出 1;

四大函数式接口

我们一般对函数式接口的使用的时候,都会对其进行封装。

消费性接口

​ Consumer 只有一个抽象方法名为 accept,参数列表只有一个泛型t,无返回值。参数的数据类型有类决定

eg:

/**
* @ClassName ConsumerTest
* @Description 消费型接口, 消费字符串字段 打印输出
* @Author ouyangkang
* @Date 2019-02-18 15:46
**/
public class ConsumerTest { public static void main(String[] args) {
test("hello",x-> System.out.println(x));
} public static <T> void test(T t, Consumer<T> consumer) {
consumer.accept(t);
}
}

输出:hello

​ 如果需要多个参数列表的话,也可以在 java.util.function 包下找到相应的函数式接口 比如 ObjLongConsumer。其他的可以自行查看

供给型接口

Supplier 只有一个抽象方法名为 get,参数列表为空,有返回值,返回值得数据类型为T。

/**
* @ClassName SupplerTest
* @Description 供给型接口 字符串拼接
* @Author ouyangkang
* @Date 2019-02-18 15:53
**/
public class SupplerTest { public static void main(String[] args) {
String hello = test("hello ", () -> "word!");
System.out.println(hello);
} public static String test(String str,Supplier<String> supplier){
return str + supplier.get();
}
}

输出为:hello word!

​ 如果需要返回得数据为基本数据类型,可以在 java.util.function 包下找到相应的函数式接口 比如:getAsLong 其他的可以自行查看

函数型接口

​ Function<T, R> 只有一个抽象方法名为 apply,参数列表只有一个参数为T,有返回值,返回值的数据类型为R。

/**
* @ClassName FunctionTest
* @Description 函数式接口 将字符串转换成大写的
* @Author ouyangkang
* @Date 2019-02-18 16:01
**/
public class FunctionTest { public static void main(String[] args) {
String test = test("hello", x -> x.toUpperCase());
System.out.println(test);
} public static String test(String str , Function<String,String> function){
return function.apply(str);
}
}

输出为:HELLO

​ 如果需要多个入参,然后又返回值的话,可以在 java.util.function 包下找到相应的函数式接口 比如 BiFunction。其他的可以自行查看

断言型接口

​ 断言型又名判断型。 Predicate 只有一个抽象方法名为 test,参数列表只有一个参数为 T,有返回值,返回值类型为 boolean。

/**
* @ClassName PredicateTest
* @Description 断言型接口,判断字符串大小是否大于6
* @Author ouyangkang
* @Date 2019-02-18 16:16
**/
public class PredicateTest {
public static void main(String[] args) {
boolean hello = test("hello", x -> x.length() > 6);
System.out.println(hello);
}
public static boolean test(String str, Predicate<String> predicate){
return predicate.test(str);
}
}

输出为: false

Stream API

​ Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念。Stream中间操作,多个中间操作可以连接起来形成一个流水线,除非流水线上触发了终止操作,否则中间不会执行任何处理!而终止操作时会一次性全部处理,称为惰性处理。要进行流操作首先要获取流。有4中方法可以获取流。

  1. 获取流 通过集合系列提供的stream方法和 parallelStream()(并行流)方法获取流
    public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
// 常用获取流的方式
Stream<Integer> stream = list.stream();
}
  1. 通过Arrays.stream() 将数组转换成流
    public static void main(String[] args) {
int[] a = new int[]{1,2,3,4};
IntStream stream = Arrays.stream(a); }
  1. 通过Stream.of今天方法创建流
    public static void main(String[] args) {
Stream<Integer> stream = Stream.of(1, 2, 3);
}
  1. 创建无限流
    public static void main(String[] args) {
Stream<Integer> iterate = Stream.iterate(0, x -> x + 2);
}

所有的对流的操作可以分为4种,分别为筛选与分片,映射,排序,终结(归约,收集)

筛选与分片

操作有filter,distant,limit,skip。

filter : 过滤操作,方法参数为断言型接口

eg:

 public static void main(String[] args) {
Stream<Integer> stream = Stream.of(1, 2, 3);
stream.filter(x->x != 2).forEach(x-> System.out.println(x));
}

输出:

1
3

distinct : 去重操作,方法无参数

limit : 获取前几条数据,方法参数为long

skip : 跳过前多少条数据,然后获取后面所有的。 方法参数为long

映射

常用操作有 map ,flatMap。

map: 对原数据进行处理,并返回处理后的数据。 方法参数为函数型接口。

eg:

  public static void main(String[] args) {
Stream<Integer> stream = Stream.of(1, 2, 3);
stream.map(x->x*2).forEach(System.out::println);
}

输出:

2
4
6

flatMap : 使原来流种的原有数据一个一个整合在另一个流中。方法参数为函数型接口,但是返回值为流。

eg:

    public static void main(String[] args) {
List<String> list = Arrays.asList("a", "b", "c");
List<String> list2 = Arrays.asList("f","d");
list.stream().flatMap(x->list2.stream().map(y-> x + y)).forEach(System.out::println);
}

排序

常用操作有sort自然排序,合sort参数为排序器的定制排序

自然排序eg:

public static void main(String[] args) {
Stream<Integer> stream = Stream.of(1, 2, 3);
stream.sorted().forEach(System.out::println);
}

输出:

1
2
3

定制排序

    public static void main(String[] args) {
Stream<Integer> stream = Stream.of(1, 2, 3);
stream.sorted((x,y)->-Integer.compare(x,y)).forEach(System.out::println);
}

输出:

3
2
1

终止操作

  • allMatch 检查是否匹配所有元素 方法参数为断言型接口
  • anyMatch 检查是否匹配所有元素 方法参数为断言型接口
  • noneMatch 检查是否没有匹配所有元素 方法参数为断言型接口
  • findFirst 返回第一个元素 无方法参数
  • findAny 返回当前流的任意元素 无方法参数
  • count 返回流中的元素总个数 无方法参数
  • max 返回流的最大值 无方法参数
  • min 返回流中的最小值 无方法参数

归约

reduce : 归约 -- 可以将流中的元素反复结合起来,得到一个值。

eg:

 public static void main(String[] args) {
List<Integer> list1 = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer reduce = list1.stream().reduce(11, (x, y) -> x + y);
System.out.println(reduce);
}

输出 : 66

收集

​ 这个是非常常用的一个操作。 将流装换为其他形式。接收到一个Collector接口的实现,用于给Stream中的元素汇总的方法。用collect方法进行收集。方法参数为Collector。Collector可以由Collectors中的toList(),toSet(),toMap(Function(T,R) key,Function(T,R) value)等静态方法实现。

  • toList() 返回一个 Collector ,它将输入元素 List到一个新的 List 。
  • toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper) 返回一个 Collector ,它将元素累加到一个 Map ,其键和值是将所提供的映射函数应用于输入元素的结果。
  • toSet() 返回一个 Collector ,将输入元素 Set到一个新的 Set 。

    eg:

User类

@Data
@ToString
public class User {
private String name; private Integer age; private Integer salary;
}
   public static void main(String[] args) {
List<User> users = Arrays.asList(new User("张三", 19, 1000),
new User("张三", 58, 2000),
new User("李四", 38, 3000),
new User("赵五", 48, 4000)
);
List<String> collect = users.stream().map(x -> x.getName()).collect(Collectors.toList());
Set<String> collect1 = users.stream().map(x -> x.getName()).collect(Collectors.toSet());
Map<Integer, String> collect2 = users.stream().collect(Collectors.toMap(x -> x.getAge(), x -> x.getName()));
System.out.println(collect);
System.out.println(collect1);
System.out.println(collect2);
}

输出:

[张三, 张三, 李四, 赵五]
[李四, 张三, 赵五]
{48=赵五, 19=张三, 38=李四, 58=张三}

分组

​ Collectors.groupingBy()方法是 返回 Collector “由基团”上的类型的输入元件操作实现 T ,根据分类功能分组元素。这个是非常常用的操作。

比如你要对名字相同的进行分组。

groupingBy(Function<? super T,? extends K> classifier)

eg:

    public static void main(String[] args) {
List<User> users = Arrays.asList(new User("张三", 19, 1000),
new User("张三", 58, 2000),
new User("李四", 38, 3000),
new User("赵五", 48, 4000)
);
Map<String, List<User>> collect3 = users.stream().collect(Collectors.groupingBy(x -> x.getName()));
System.out.println(collect3);
}

输出:{李四=[User{name='李四', age=38, salary=3000}], 张三=[User{name='张三', age=19, salary=1000}, User{name='张三', age=58, salary=2000}], 赵五=[User{name='赵五', age=48, salary=4000}]}

当然还有其他的一些比较复杂的分组操作,实际代码看业务来进行实现。

总结

​ java8中的lambda表达式可能一开始用的时候还不是很熟悉,但是只要熟悉了,你会发现非常的好用,而且lambda表达式结合stream API可以进行编写自己的工具类。在平常项目中可以非常的省时间,提高写代码的效率。我现在给出一个List转Map的工具类。

public class CollectionStream {
public static void main(String[] args) {
List<User> users = Arrays.asList(new User("张三", 19, 1000),
new User("张三", 58, 2000),
new User("李四", 38, 3000),
new User("赵五", 48, 4000)
);
Map<Integer, Integer> map = listToMap(users, x -> x.getAge(), x -> x.getSalary());
System.out.println(map);
} /**
* @Author ouyangkang
* @Description list 转map key不能相同 ,如果相同会报错。 方法对 源数据,key,value过滤null。
* @Date 9:27 2019/2/19
* @param source 源数据
* @param key
* @param value
* @return java.util.Map<K,V>
**/
public static <DTO, K, V> Map<K, V> listToMap(List<DTO> source, Function<? super DTO, ? extends K> key, Function<? super DTO, ? extends V> value) {
Objects.requireNonNull(source, "source not null");
Objects.requireNonNull(key, "key not null");
Objects.requireNonNull(value, "value not null");
Map<K, V> map = source.stream()
.filter(dto -> dto != null)
.filter(dto -> key.apply(dto) != null)
.filter(dto -> value.apply(dto) != null)
.collect(Collectors.toMap(key, value));
return map;
}
}

Java8中的 lambda 和Stream API的更多相关文章

  1. Java8新特性之三:Stream API

    Java8的两个重大改变,一个是Lambda表达式,另一个就是本节要讲的Stream API表达式.Stream 是Java8中处理集合的关键抽象概念,它可以对集合进行非常复杂的查找.过滤.筛选等操作 ...

  2. 一篇文章教会你使用Java8中的Lambda表达式

    简介 Java 8为开发者带来了许多重量级的新特性,包括Lambda表达式,流式数据处理,新的Optional类,新的日期和时间API等.这些新特性给Java开发者带来了福音,特别是Lambda表达式 ...

  3. java8中的日期和时间API

    一.背景 jdk 1.8 之前, Java 时间使用java.util.Date 和 java.util.Calendar 类. Date today = new Date(); System.out ...

  4. JavaSE | Lambda| Optional| Stream API

    JDK1.8新特性 1.接口:默认方法. 静态方法 2.Lambda表达式和StreamAPI 3.Optional类 4.新的日期时间API Lambda表达式:为了简化代码,使得Java支持 St ...

  5. java8新特性Lambda和Stream

    Java8出来已经4年,但还是有很多人用上了jdk8,但并没用到里面的新东西,那不就等于没用?jdk8有许多的新特性,详细可看下面脑图 我只讲两个最重要的特性Lambda和Stram,配合起来用可以极 ...

  6. Java8中的Lambda表达式

    Lambda是什么 Lambda表达式,也可称为闭包,是java8的新特性,作用是取代大部分内部类,优化java代码结构,让代码变得更加简洁紧凑. Lambda的基本语法 (expression)-& ...

  7. Java8 中的时间和日期 API

    1. 日期和时间概述 LocalDate,LocalTime,LocalDateTime类的实例是不可变的对象,分别表示使用 ISO-8601 日历系统 的日期,时间,日期和时间;它们提供了简单的日期 ...

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

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

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

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

随机推荐

  1. 关于vue的基础概念

    vue-cli相当于脚手架 给你自动生成模板工程vue-router是 vue路由插件 支持你单页应用的vue-loader是webpack下loader插件 可以把.vue文件 输出成组件

  2. (六)STL仿函数functor

    1.仿函数为算法服务,特点是重载操作符() 2.一共分为3大类,包括算术类,逻辑运算类,相对关系(比较大小):都继承了binary_function 3.仿函数的一些调用示例,其中右边的仿函数没有继承 ...

  3. SpringCloud服务提供者

    服务提供者就是提供一个服务暴露出来给别人调用,在springcloud中需要注册服务到服务中心 搭建服务提供者项目(ProduceDemo) 1.创建pom.xml <project xmlns ...

  4. php实现备份数据库

    public function dataBackup(){ $doc_root=$_SERVER['DOCUMENT_ROOT']; $file_path_name=$doc_root.'/sqlba ...

  5. python(10): xlsxwriter模块

    import xlsxwriter as writer 注意: xlsxwriter 只能创建新文件,不可以修改原有文件.如果创建新文件时与原有文件同名,则会覆盖原有文件. import xlsxwr ...

  6. java----AOP框架理解

    面向切面编程: 通过动态代理+加配置文件 目的解耦 给主逻辑添加一些修饰功能,但是不在主逻辑代码中进行修改,有点类似python中的装饰器,调用方法还是是通过接口的那个类来调用: import jav ...

  7. bzoj 2669 题解(状压dp+搜索+容斥原理)

    这题太难了...看了30篇题解才整明白到底咋回事... 核心思想:状压dp+搜索+容斥 首先我们分析一下,对于一个4*7的棋盘,低点的个数至多只有8个(可以数一数) 这样的话,我们可以进行一个状压,把 ...

  8. 如果IDEA右上角的tomcat消失了,解决办法

    看了很多博客都没有找到解决办法,还是老师帮我解决的

  9. ubuntu下使用matplotlib绘图无法显示中文label

    原因是字体导致的.大家的做法基本都是搞一个windows上的字体文件(simhei.ttf, 点我fq下载)然后刷新一下缓存文件. 只不过百度搜到第一篇CSDN的博客,写的很不靠谱(不是所有的CSDN ...

  10. 关于The specified Android SDK Build Tools version (26.0.2) is ignored, as it is below the minimum...

    今天将项目迁移到另一台笔记本,进行build出现以下问题,导致build失败 The specified Android SDK Build Tools version (26.0.2) is ign ...