Java8-Stream API

Java8-Stream API

Stream是java8推出的一个新的抽象的流,可以使得想操作数据库一样操作java中的集合。

创建Stream

共有四种方法创建Stream

//1. 通过 Collection 系列集合的 stream() <串行流> 或 paralleStream() <并行流>
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();


//2. 通过 Arrays 静态方法 stream() 获取数组流
String[] ss = {"1","2","3"};
Stream<String> stream2 = Arrays.stream(ss);


//3. 通过 Stream 的静态方法 of()
Stream<String> stream3 = Stream.of("a","b","c");

//4. 创建无限流
// 迭代
Stream.iterate(0, (x) -> x + 2)
  .limit(5) // 限制 5 个
  .forEach(System.out::println); // 调用一个函数式接口
System.out.println("------------------");

// 生成器
Stream.generate(()->Math.random())
  .limit(5)
  .forEach(System.out::println);

Stream的中间操作

创建了Stream之后要对Stream进行一些中间操作,例如排序,选择,切割之类,官方给我们提供了很多的工具类来进行这些操作。

切片和筛选

  • filter : 接收 Lambda ,从流中排除某些元素

  • limit : 截断流,使其元素不超过给定数量

  • skip(n) : 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。
  • distinct : 筛选,通过流生成的元素的 hashCode() 和 equals() 去除重复的元素

先创建一个测试类,并初始化一些集合数据:

public class Employee {
    private Integer id;
    private String name;
    private Double salary;
    public Employee(){}
    public Employee(Integer id){
        this.id = id;
    }
    public Employee(Integer id,String name){
        this.id = id;
        this.name =name;
    }
    public Employee(Integer id,String name,Double salary){
        this.id = id;
        this.name = name;
        this.salary = salary;
    }
    // getter 和 setter 和 toString 和 equals 和 hashcode 方法省略 ... 

测试类中初始化一些数据

List<Employee> emps = Arrays.asList(
            new Employee(1,"bart",13000D),
            new Employee(2,"lisa",19000D),
            new Employee(2,"lisa",19000D),
            new Employee(2,"lisa2",19000D),
            new Employee(3,"maggie",16000D),
            new Employee(3,"maggie",16000D),
            new Employee(3,"maggie2",16000D),
            new Employee(4,"homer",23000D),
            new Employee(5,"marge",16000D),
            new Employee(5,"marge",16000D),
            new Employee(6,"marge",16000D)
                                    );
filter

按照指定规则过滤掉一些不符合的元素

@Test
public void test2() {
  emps.stream()
    .filter((e)-> e.getId() > 4) // 过滤掉一个年龄大于 4 的元素
    .forEach(System.out::println);
}

输出结果:

Employee [id=5, name=marge, salary=16000.0]
Employee [id=5, name=marge, salary=16000.0]
Employee [id=6, name=marge, salary=16000.0]
distinct

初始化的测试集合中有重复数据

@Test
public void test2() {
  emps.stream()
    .distinct()
    .forEach(System.out::println);
}

输出:

Employee [id=1, name=bart, salary=13000.0]
Employee [id=2, name=lisa, salary=19000.0]
Employee [id=2, name=lisa2, salary=19000.0]
Employee [id=3, name=maggie, salary=16000.0]
Employee [id=3, name=maggie2, salary=16000.0]
Employee [id=4, name=homer, salary=23000.0]
Employee [id=5, name=marge, salary=16000.0]
Employee [id=6, name=marge, salary=16000.0]

可以发现已经去掉重复值了。

skip

跳过若干元素:

@Test
public void test2() {
  emps.stream()
    .skip(emps.size()-2)
    .forEach(System.out::println);
}

输出:

Employee [id=5, name=marge, salary=16000.0]
Employee [id=6, name=marge, salary=16000.0]
limit

限制若干个元素

@Test
public void test2() {
  emps.stream()
    .limit(3)
    .forEach(System.out::println);
}

输出了前三个元素:

Employee [id=1, name=bart, salary=13000.0]
Employee [id=2, name=lisa, salary=19000.0]
Employee [id=2, name=lisa, salary=19000.0]

映射

  • map : 接受一个 lambda 将元素转化为其他形式或提取信息,接受一个函数作为参数,该函数会应用到每一个元素上面,并将其映射为一个新的元素。
  • flatMap:接受一个函数作为参数,将流中的每个值都转化为另外一个流,然后把所有的流连接成一个新的流
map
List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","eee");       
// map
list.stream()
  .map((s) -> s.toUpperCase()) // 将每个元素转化为大写
  .forEach(System.out::println); // 输出

输出:

AAA
BBB
CCC
DDD
EEE
flatMap

还是上面的list的集合,我们现在想把每个字符串元素转化为字符元素,然后把转化好的字符元素集合转化为Stream流,然后遍历输出。

/** 将一个字符串转化为一个 Character 的 Stream  */
public static Stream<Character> convertStream(String str) {
  List<Character> list = new ArrayList<>(str.length());
  for(Character c:str.toCharArray()) {
    list.add(c);
  }
  return list.stream();
}

测试方法:

/* 使用flatMap 将每个字符元素放到一个新的 Character 流中 */
list.stream()
  .flatMap(TestStreamCreate::convertStream) // TestStreamCreate 类是当前的测试类
  .forEach(System.out::println);

输出:

a
a
... ... ...
e
e
e

排序

  • sorted() : 自然排序 Comparable

  • sorted(Comparator com):定制排序

自然排序:
List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","eee");
        list.stream().sorted().forEach(System.out::println);

输出:

aaa
bbb
ccc
ddd
eee
定制排序:
emps.stream().sorted((x,y) -> {
    if(x.getName().equals(y.getName())) {
      return x.getName().compareTo(y.getName()); // 比较姓名
    }else {
      return x.getSalary().compareTo(y.getSalary()); // 比较工资
    }
}).forEach(System.out::println);

输出:

Employee [id=1, name=bart, salary=13000.0]
Employee [id=3, name=maggie, salary=16000.0]
Employee [id=3, name=maggie, salary=16000.0]
Employee [id=3, name=maggie2, salary=16000.0]
Employee [id=5, name=marge, salary=16000.0]
Employee [id=5, name=marge, salary=16000.0]
Employee [id=6, name=marge, salary=16000.0]
Employee [id=2, name=lisa, salary=19000.0]
Employee [id=2, name=lisa, salary=19000.0]
Employee [id=2, name=lisa2, salary=19000.0]
Employee [id=4, name=homer, salary=23000.0]

查找

  • allMatch : 检查是否匹配所有元素
  • anyMatch : 检查是否至少匹配一个元素
  • noneMatch : 检查是否没有匹配所有元素
  • findFirst : 返回第一个元素
  • findAny : 返回当前流中的任意元素
  • count : 返回流中元素的总个数
  • max : 返回流中的最大值
  • min : 返回流中的最小值
@Test
public void test5() {

  boolean b1 = emps.stream().allMatch((e) -> e.getSalary() >10000); //所有的工资大于10000
  System.out.println(b1);//true

  boolean b2 = emps.stream().anyMatch((e) -> e.getName().length() > 5); // 至少有一个姓名长度大于 5
  System.out.println(b2);//true

  boolean b3 = emps.stream().noneMatch((e) -> e.getName().length() > 9); // 没有人的姓名长度大于 9
  System.out.println(b3);//true

  Optional<Employee> op1 = emps.stream().findFirst(); 
  System.out.println(op1.get());

  Optional<Employee> op2 = emps
    .parallelStream() // 并行流,利用多线程
    //              .stream() // 串行流
    .findAny(); 
  System.out.println(op2.get());

  long count = emps.stream().count();
  System.out.println(count);

  // 返回工资最该的那个 Employee 对象
  Optional<Employee> op3 = emps.stream().max((x,y) -> Double.compare(x.getSalary(), y.getSalary()));
  System.out.println(op3.get()); //Employee [id=4, name=homer, salary=23000.0]

  // 返回工资最少的那个人的工资
  Optional<Double> op4 = emps.stream()
    .map((e) -> e.getSalary()) // 将每个元素转化为工资
    .min((x,y) -> Double.compare(x,y)); // 按照工资排序
  System.out.println(op4.get()); // 13000.0
}

输出:

true
true
true
Employee [id=1, name=bart, salary=13000.0]
Employee [id=3, name=maggie2, salary=16000.0]
11
Employee [id=4, name=homer, salary=23000.0]
13000.0

规约

  • reduce(T identity,BinaryOperator)

  • reduce(BinaryOperator)可以将流中的元素反复结合起来,得到一个值

@Test
public void test6() {
  List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9);
  Integer sum = list.stream()
    .reduce(0,(x,y) -> x+y); // 第一个是默认值,第二个是lambda表达式求和
  System.out.println(sum); // 45

  System.out.println("--------------------");

  Optional<Double> op = emps.stream()
    .map((e) -> e.getSalary())
    .reduce(Double::sum); // 求所有 employee 的工资总和
  System.out.println(op.get()); // 119000.0

  System.out.println("--------------------");

}

收集

Collectors 的 API

方法含义
Collectors.toList()转化为 list 集合
Collectors.toSet()转化为Set集合
Collectors.toCollection(Lambda表达式)转化为任意类型集合,使用构造函数的引用确定类型
Collectors.counting()计数
Collectors.averagingDouble(Lambda表达式)平均值
Collectors.summingDouble(Lambda表达式)总和
Collectors.minBy(Lambda表达式)最小值,由Lambda表达式确定排序规则
Collectors.groupingBy(Lambda表达式)按照Lambda表达式规则分组
Collectors.summarizingDouble(Lambda表达式)返回 DoubleSummaryStatistics 对象直接获得分组函数结果值
Collectors.joining(Lambda表达式)连接字符串方法
@Test
public void test7() {
  List<Integer> list = emps.stream()
    .map((e) -> e.getId()) // 获得所有的 ID
    .collect(Collectors.toList());// 转化为 list 集合
  System.out.println(list);//[1, 2, 3, 4, 5, 5, 5]

  System.out.println("----------------------------");

  Set<String> set = emps.stream()
    .map((e) -> e.getName())
    .collect(Collectors.toSet());// 转化为 set 集合
  System.out.println(set);//[marge, maggie, lisa, homer, bart]

  System.out.println("----------------------------");

  HashSet<String> hashSet = emps.stream()
    //          .map((e) -> e.getName()
    .map(Employee::getName)
    .collect(Collectors.toCollection(HashSet::new));// 转化为 set 集合
  System.out.println(hashSet);//[marge, maggie, lisa, homer, bart]
}
@Test
public void test8() {
  //1. count 计数
  Long collect = emps.stream()
    .collect(Collectors.counting());
  System.out.println("总共: "+collect);//总共: 11

  //2. 平均值
  Double avgSalary = emps.stream()
    .collect(Collectors.averagingDouble(Employee::getSalary));
  System.out.println("工资平均值:"+avgSalary);//工资平均值:17181.81818181818

  //3. 总和
  Double collect2 = emps.stream()
    .collect(Collectors.summingDouble(Employee::getSalary));
  System.out.println("工资总和:"+collect2);//工资总和:189000.0

  //4. 最小值
  Optional<Employee> op = emps.stream()
    .collect(Collectors.minBy((x,y) -> Double.compare(x.getSalary(), y.getSalary())));
  System.out.println(op.get()); // Employee [id=1, name=bart, salary=13000.0]

}
@Test
public void test9() {
  // 按照姓名分组
  Map<String, List<Employee>> collect = emps.stream()
    .collect(Collectors.groupingBy(Employee::getName));
  for (Map.Entry<String, List<Employee>> entry : collect.entrySet()) {
    System.out.println(entry.getKey()+" : "+entry.getValue());
  }
  System.out.println("++++++++++++++++++++++++++++++++ss");
  // 先按照姓名分组,再按照id分组
  Map<String, Map<Integer, List<Employee>>> collect2 = emps.stream()
    .collect(
    Collectors.groupingBy(
      Employee::getName,Collectors.groupingBy(Employee::getId)));

  for(Map.Entry<String, Map<Integer, List<Employee>>> entry : collect2.entrySet()) {
    String k1 = entry.getKey();
    System.out.println(k1);
    Map<Integer, List<Employee>> v1 = entry.getValue();
    for(Map.Entry<Integer, List<Employee>> e : v1.entrySet()) {
      System.out.println("\t"+e.getKey());
      for(Employee emp : e.getValue()) {
        System.out.println("\t\t"+emp);
      }
    }
    System.out.println("~~~~~~~~~~~~~~~~~~~~~~");
  }
}

输出:

marge : [Employee [id=5, name=marge, salary=16000.0], Employee [id=5, name=marge, salary=16000.0], Employee [id=6, name=marge, salary=16000.0]]
lisa2 : [Employee [id=2, name=lisa2, salary=19000.0]]
maggie : [Employee [id=3, name=maggie, salary=16000.0], Employee [id=3, name=maggie, salary=16000.0]]
lisa : [Employee [id=2, name=lisa, salary=19000.0], Employee [id=2, name=lisa, salary=19000.0]]
maggie2 : [Employee [id=3, name=maggie2, salary=16000.0]]
homer : [Employee [id=4, name=homer, salary=23000.0]]
bart : [Employee [id=1, name=bart, salary=13000.0]]
++++++++++++++++++++++++++++++++ss
marge
    5
        Employee [id=5, name=marge, salary=16000.0]
        Employee [id=5, name=marge, salary=16000.0]
    6
        Employee [id=6, name=marge, salary=16000.0]
​~~~~~~~~~~~~~~~~~~~~~~
lisa2
    2
        Employee [id=2, name=lisa2, salary=19000.0]
​~~~~~~~~~~~~~~~~~~~~~~
maggie
    3
        Employee [id=3, name=maggie, salary=16000.0]
        Employee [id=3, name=maggie, salary=16000.0]
​~~~~~~~~~~~~~~~~~~~~~~
lisa
    2
        Employee [id=2, name=lisa, salary=19000.0]
        Employee [id=2, name=lisa, salary=19000.0]
​~~~~~~~~~~~~~~~~~~~~~~
maggie2
    3
        Employee [id=3, name=maggie2, salary=16000.0]
​~~~~~~~~~~~~~~~~~~~~~~
homer
    4
        Employee [id=4, name=homer, salary=23000.0]
​~~~~~~~~~~~~~~~~~~~~~~
bart
    1
        Employee [id=1, name=bart, salary=13000.0]
​~~~~~~~~~~~~~~~~~~~~~~
// 返回 DoubleSummaryStatistics  对象直接获得分组函数结果值 
@Test
public void test10() {
  DoubleSummaryStatistics dss = emps.stream()
    .collect(Collectors.summarizingDouble(Employee::getSalary));
  System.out.println(dss.getCount());//11
  System.out.println(dss.getSum());//189000.0
  System.out.println(dss.getAverage());//17181.81818181818
  System.out.println(dss.getMax());//23000.0
  System.out.println(dss.getMin());//13000.0
}
@Test
public void test11() {
  System.out.println(
    emps.stream()
    .map(Employee::getName)
    .distinct()
    // 参数1:连接符, 参数2:前缀, 参数3:后缀
    .collect(Collectors.joining(" , ", "begin ->\n" , "\n<- end"))
  );
}

输出:

begin ->
bart , lisa , lisa2 , maggie , maggie2 , homer , marge
<- end

并行流和串行流

并行流和串行流,顾名思义,一个并行,一个串行,前者充分利用CPU的多核心优势,后者为单线程运行。在Java8中的并行流实际上就是Java7中的ForkJoin框架的再封装。

小例子:

@Test
public void test2() {
  OptionalLong res = LongStream.range(0, 100000000L)
    .parallel() // 转化为并行流
    .reduce(Long::sum);
  System.out.println(res.getAsLong());
}
@Test
public void test2() {
  OptionalLong res = LongStream.range(0, 100000000L)
    .sequential() // 转化为串行流
  System.out.println(res.getAsLong());
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值