# Java8-Lambda

# 一、Lambda表达式

基本格式:(参数列表) -> {方法体}

Stream流,结合Lambda提供了对集合的流水线操作,使得代码具有非常高的可读性,优雅性!

  1. Stream提供了对集合的便捷化处理方式
  2. 声明式编程的思想
  3. lambda只能简化函数式接口(接口中,有且只有一个抽象方法)
  4. Lambda使得代码更加优雅,简洁,不信你往下看→_→

# 1.1 函数式接口

Java内置的函数式接口 java.util.function 包下,常见的如下:

项目 描述 备注
Consumer<T> void accept(T t) 消费型接口,无返回值仅消费参数
Function<T, R> R apply(T t) 计算转换类,将参数转换为其他对象返回
Predicate<T> boolean test(T t) 参数进行条件判断
Supplier<T> T get() 生产型接口,返回一个对象

# 二、Stream基础和操作

# 2.1 Stream相关概念

Stream的所有操作分为三个阶段,【创建】>> 【中间操作】>> 【终端操作】

注意:创建的默认都是串行流,可以通过parallel()改成并行流

1.创建

//直接通过指定的元素创建
Stream<String> stream = Stream.of("1", "2", "3");
//通过list构建
List<String> list = Arrays.asList("1", "2", "3");
Stream<String> stream = list.stream();
//数组构建
Integer[] arr = {1,2,3,4}
Stream<Integer> stream = Arrays.stream(arr);
//Map
Map<String, Object> map = new HashMap<>();
Stream<Map.Entry<String, Object>> stream = map.entrySet().stream();
1
2
3
4
5
6
7
8
9
10
11

2.中间操作

Stream Operation Goal Input
filter filter 方法用于通过设置的条件过滤出元素。 条件(返回布尔值)Predicate
map map 方法用于映射每个元素到对应的结果 可以是一个功能 Function
skip skip(5) 跳过5个元素 long值
limit limit(5) 方法用于获取5个元素 long值
sorted sorted 方法用于对流进行排序 Comparator
distinct 去除重复(注意要重写equals和hashcode) --
parallel parallelStream 是流并行处理程序的代替方法 --

例子

Random random = new Random();
random.ints(0, 10).limit(5).map(i -> (i * i)).distinct().sorted().forEach(System.out::println);
//根据属性名排序,比如创建时间
List<Student> stuList = studentList.stream().sorted((s1, s2) -> s2.getAge() - s1.getAge()).collect(Collectors.toList());
List<Student> stuList = studentList.stream().sorted(Comparator.comparing(Student::getCreateTime)).collect(Collectors.toList());
1
2
3
4
5

3.终端操作

Stream Operation Goal Input Or Output
forEach 遍历元素 其他操作
count 统计元素个数 --
collect 聚合操作 --

# 2.2 collect聚合

Collectors 提供了一系列的静态方法供我们使用,toList/toSet/toMap/toCollection/toConcurrentMap

  1. 转List或者Set
// ArrayList
List<Integer> stuIds = studentList.stream().map(Student::getId).collect(Collectors.toList());
//HashSet
Set<Integer> stuIds = studentList.stream().map(Student::getId).collect(Collectors.toSet());
1
2
3
4

要转LinkedList类型,或者TreeSet等其他类型,用 toCollection

//LinkedList
List<Integer> stuIds = studentList.stream().map(Student::getId).collect(Collectors.toCollection(LinkedList::new));
1
2
  1. 转Map

有重复的数据,会报错 duplicateKeyException,解决办法,在toMap中添加第三个参数mergeFunction,(o1, o2) -> o1 即 如果有重复的key,则保留key1,舍弃key2 来解决冲突的问题

Map<Integer, Student> stuMap = studentList.stream().collect(Collectors.toMap(Student::getId, Function.identity()));
//解决重复key
Map<Integer, Student> stuMap = studentList.stream().collect(Collectors.toMap(Student::getId, Function.identity(), (o1, o2) -> o1));
1
2
3
  1. collectingAndThen

先进行结果集的收集,然后将收集到的结果集进行下一步的处理

先归纳处理Collectors.toCollection()转成TreeSet集合,用到这个() -> new TreeSet<>(Comparator.comparing(Student::getName)))策略,即把Student放到TreeSet根据名称去重,再交给 Function R apply(T t),也就是将结果设置成t 处理,即创建 ArrayList<Student>(TreeSet) 返回

//根据学生姓名去重,这里看起来笔记复杂,拆开看
List<Student> stu = studentList.stream().collect(Collectors.collectingAndThen(
        Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Student::getName))), ArrayList::new));
1
2
3
  1. joining

Joining用来以某种规则连接stream中的元素,三种重载

  • joining()
  • joining(CharSequence delimiter)
  • joining(CharSequence delimiter,CharSequence prefix,CharSequence suffix)
List<String> list = Arrays.asList("Tomcat", "Jetty", "Nginx");
//TomcatJettyNginx
list.stream().collect(Collectors.joining());
//Tomcat Jetty Nginx
list.stream().collect(Collectors.joining(" "));
//[Tomcat,Jetty,Nginx]
list.stream().collect(Collectors.joining(",", "[", "]"));
1
2
3
4
5
6
7
  1. 统计
  • Collectors.counting 用来统计stream中元素的个数
  • Collectors.summingDouble/Long/Int 对stream中的元素做sum操作
  • Collectors.maxBy/minBy 根据提供的Comparator,返回最大或者最小值
  • Collectors.summarizingDouble/Long/Int 统计类,对以上三个做统计整合
  1. 分组 Collectors.groupingBy

根据某些属性进行分组,并返回 HashMap(原来集合里的数据顺序会打乱重排列)

Map<Integer, List<Student>> group = studentList.stream().collect(Collectors.groupingBy(Student::getStudentNo));
//解决办法,使用LinkedHashMap分组按之前List的顺序
Map<Integer, List<Student>> group = studentList.stream().collect(Collectors.groupingBy(Student::getStudentNo, LinkedHashMap::new, Collectors.toList()));
1
2
3

考虑线程安全,下面或者groupingByConcurrent

Supplier<Map<Integer, Set<String>>> mapSupplier = () -> Collections.synchronizedMap(new HashMap<>());
Map<Integer, Set<String>> collect = servers.stream.collect(Collectors.groupingBy(String::length, mapSupplier, Collectors.toSet()));
1
2

实测例子

public static void main(String[] args) {
    List<String> lists = new ArrayList<String>() {{
        add("A");
        add("D");
        add("C");
        add("B");
        add("A");
    }};
    System.out.println("原始列表数据 :" + lists);

    Map<String, List<String>> collect1 = lists.stream().collect(Collectors.groupingBy(x -> x));
    System.out.println("JAVA Stream分组处理完,默认处理:" + collect1);
    Map<String, List<String>> collect = lists.stream().collect(Collectors.groupingBy(x -> x, LinkedHashMap::new, Collectors.toList()));
    System.out.println("JAVA Stream分组处理完,LinkedHashMap:" + collect);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  1. Collectors.mapping

先对元素使用 Function 进行再加工操作,然后用另一个Collector 归纳

 strList.stream.collect(Collectors.mapping(s -> s.substring(1), Collectors.toList()));
1
  1. Collectors.reducing

元素两两之间进行比较根据策略淘汰一个,随着轮次的进行元素个数就是 reduce 的

# 2.3 Optional

可以为null的容器对象,Optional可以很方便的帮助我们来解决NPE的问题

  • orElseThrow
  • orElseGet

如下代码,判空头皮发麻吧,手动狗头

public static void main(String[] args) {
  Student student = getStudent();
  if (null != student) {
      Address address = student.getAddress();
      if (null != address) {
          Province province = address.getProvince();
          if (null != province) {
              System.out.println(province.getName());
          }
      }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12

接下来看神奇操作,秀不秀

String name = Optional.ofNullable(getStudent())
        .map(o -> o.getAddress())
        .map(o -> o.getProvince())
        .map(o -> o.getName())
        .orElseThrow(NullPointerException::new);
1
2
3
4
5