saz 发表于 2021-11-22 13:11

笔记 Java8 Stream API

# Stream API

## **Stream API 三部曲**

- **创建流**
- **中间操作**
- **中止操作**

## 1. 创建流

```java
/*
        * 1. 创建Stream的方式
        */
// collection.stream()
Stream<Employee> stream1 = employees.stream();

// Arrays.stream(T[] array)
Stream<Employee> stream2 = Arrays.stream(empArr);

// Stream.of(T[] arr)
Stream<Employee> empArr = Stream.of(this.empArr);

// Stream.iterate(seed,UnaryOperator<>)
Stream<Integer> stream4 = Stream.iterate(0, x -> x + 2);

// Stream.generate(Supplier<>)
Stream<Double> stream5 = Stream.generate(() -> Math.random());
```



## 2. 中间操作

### 2.0 一次加载,惰性求值

- 如果没有终止操作,中间操作不会有任何输出

```java
/**
* 无中止操作是么有任何输出的,因为Stream是一次加载,“惰性求值”
*/
@Test
public void test02() {
      employees.stream().filter(e -> {
          System.out.println("中间操作:" + e.getName() + "是" + e.getAge() + "岁");
          return e.getAge() > 35;
      });
}
```

### 2.1 筛选

```java
/**
   * 中间操作
   * 筛选
   * 筛选年纪大于35岁的人员
   * 内部迭代输出
   */
@Test
public void test01() {
    employees.stream()
      .filter(e -> {
              System.out.println("中间操作:" + e.getName() + "是" + e.getAge() + "岁");
              return e.getAge() > 35;
            })
      .forEach(System.out::println);
}
```

### 2.2 切片

#### 2.2.1 limit

**limit(Long num)—— 截断前num个元素,之后不会向后迭代**

```java
/**
   * 切片操作
   * 只取前两个
   */
@Test
public void test05() {
    employees.stream()
      .limit(2)
      .forEach(System.out::println);
}
```

**limit切片会导致短路,也即是不会继续向后迭代:**

```java
/**
   * 切片操作
   * limit会造成短路
   */
@Test
public void test05_2() {
    employees.stream()
      .filter(employee -> {
            System.out.println("短路");
            return employee.getSalary()>3000;
      })
      .limit(2)
      .forEach(System.out::println);
}
```

#### 2.2.2 skip

**Skip(Long num) —— 跳过前num个元素**

```java
/**
   * 切片操作
   * 跳过前两个
   */
@Test
public void test06() {
    employees.stream()
      .skip(2)
      .forEach(System.out::println);
}
```

### 2.3 排序

#### 2.3.1 自然排序

`Stream<T> sorted();`

```java
/**
   * 自然排序
   */
@Test
public void test03_1(){
    int[] arr = {1,5,9,3,2,7,6,9};
    Arrays.stream(arr).sorted().forEach(System.out::println);
}
```

#### 2.3.2 定制排序

**复杂的对象需要定制排序规则**

`Stream<T> sorted(Comparator<? super T> comparator);`

```java
/**
   * 排序操作
   * 按照工资正序排序
   */
@Test
public void test03() {
    employees.stream()
      .sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
      .forEach(System.out::println);
}
```

### 2.4 去重

#### 2.4.1 默认去重

`Stream<T> distinct();`

```java
/**
   * 去重操作
   * 通过流生成的hashCode和equals方法去重
   * 若是对象没有重写hashcode和equals方法,将无法成功去重
   */
@Test
public void test04() {
   employees.stream()
   .distinct()
   .forEach(System.out::println);
}
```

#### 2.4.4 定制去重

参考: https://blog.csdn.net/haiyoung/article/details/80934467

​                        https://www.concretepage.com/java/jdk-8/java-8-distinct-example

**a 重写Employee的hashCode 和 equals方法**

​        **因为sorted()是基于 hashCode() 和 equals() 方法进行工作,所以第一个方法是重写这两个方法。**

**b 借助filter过滤**

`Stream<T> filter(Predicate<? super T> predicate);`

- 创建去重的规则,返回Predicate类型

```java
static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {

      Map<Object, Boolean> employeeMap = new ConcurrentHashMap<>();
      return e -> employeeMap.putIfAbsent(keyExtractor.apply(e), Boolean.TRUE) == null;
    }
```

- 使用

```java
/**
   * 对象去重
   * 按照指定的方式去重
   */
@Test
public void test04_1() {
    employees.stream()
      .filter(distinctByKey(employee -> employee.getId()))
      .forEach(System.out::println);
}
```

### 2.5 映射

#### 2.5.1 map映射

**对流中每一个元素进行操作**

```java
/**
   * map
   * 对每一个元素进行操作
   */
@Test
public void test01(){
    employees.stream().map(employee -> employee.getName().toLowerCase()).forEach(System.out::println);
}
```

**提取对象的元素**

```java
/**
   * map
   * 提取元素
   */
@Test
public void test02(){
    employees.stream().map(Employee::getName).distinct().forEach(System.out::println);
}
```

狸猫换太子

需要遍历map ,找到所有value(对象列表)添加新对象

```java
// dailyMap,目前已有的map -- Map<String, List<CapitalDaily>>
Map<String, List<CapitalDaily>> map = dailyMap.entrySet().stream().map(entry -> {
            String key = entry.getKey();
            List<CapitalDaily> value = entry.getValue();
            List<CapitalDaily> dailies = value.stream().filter(li -> null != li.getDate() && li.getDate().equals(date)).filter(i -> i.getBalance() != null).collect(Collectors.toList());
            BigDecimal balance = dailies.get(dailies.size() - 1).getBalance();
            BigDecimal creditAmount = value.stream().filter(li -> null != li.getDate() && li.getDate().equals(date)).filter(i -> i.getCreditAmount() != null).map(CapitalDaily::getCreditAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal debitAmount = value.stream().filter(li -> null != li.getDate() && li.getDate().equals(date)).filter(i -> i.getDebitAmount() != null).map(CapitalDaily::getDebitAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
            CapitalDaily daily = new CapitalDaily();
//            daily.setDate(date);
            daily.setBalance(balance.equals(BigDecimal.valueOf(0)) ? null : balance);
            daily.setCreditAmount(creditAmount.equals(BigDecimal.valueOf(0)) ? null : creditAmount);
            daily.setDebitAmount(debitAmount.equals(BigDecimal.valueOf(0)) ? null : debitAmount);
            ArrayList<CapitalDaily> newDaily = new ArrayList<>(value);
            newDaily.add(daily);
            return new AbstractMap.SimpleEntry<>(key, newDaily);
      }).collect(Collectors.toMap(Map.Entry::<String, List<CapitalDaily>>getKey, Map.Entry::<String, List<CapitalDaily>>getValue));

      return map;
```





#### 2.5.2 flatMap映射

```java
/**
   * flatMap
   * 将字符串拆分打印
   */
@Test
public void test03(){
    Stream<Stream<Character>> streamStream = list.stream()
      .map(Demo03_map::departString);
    streamStream.forEach(s -> s.forEach(System.out::println));

    System.out.println("===============biu~~ =======================");

    Stream<Character> characterStream = list.stream()
      .flatMap(Demo03_map::departString);
    characterStream.forEach(System.out::println);
}

/**
   * 切割字符串的方法,返回一个流
   * @Param str 要切割的字符串
   * @Return characters.stream():Stream<Character>
   */
public static Stream<Character> departString(String str){
    List<Character> characters = new ArrayList<>();
    for (char c : str.toCharArray()) {
      characters.add(c);
    }
    return characters.stream();
}
```



## 3. 终止操作

### 3.1 查找与匹配

#### 3.1.1 allMatch 是否匹配所有元素

```java
/**
   * matchAll,是否匹配所有元素
   */
@Test
public void test01(){
    boolean b = employees.stream()
      .allMatch(employee -> employee.getAge() > 25);
    System.out.println(b); // false
}
```

#### 3.1.2 anyMatch 是否匹配至少一个元素

```java
/**
   *         anyMatch 是否匹配到至少一个元素
   */
@Test
public void test02(){
    boolean b = employees.stream()
      .anyMatch(employee -> employee.getAge() > 25);
    System.out.println(b); // true
}


```

#### 3.1.3 noneMatch 是否没有匹配所有元素

```java
/**
   *         noneMatch 是否没有匹配所有元素
   */
@Test
public void test03(){
    boolean b = employees.stream()
      .noneMatch(employee -> employee.getAge() > 25);
    System.out.println(b); // false
}

@Test
public void test03_1(){
    boolean b = employees.stream()
      .noneMatch(employee -> employee.getAge() > 125);
    System.out.println(b); // true
}
```

#### 3.1.4 findFirst 返回第一元素

```java
/**
   *         findFirst 返回第一元素
   */
@Test
public void test04(){
    Optional<Employee> first = employees.stream()
      .filter(employee -> employee.getAge() > 25)
      .findFirst();
    System.out.println(first); // false
}
```

#### 3.1.5 findAny 返回任意元素

```java
/**
   *findAny 返回任意元素
   */
@Test
public void test05(){
    Optional<Employee> first = employees.stream()
      .filter(employee -> employee.getAge() < 25)
      .findAny();
    System.out.println(first);
}
```

#### 3.1.6 count 返回流中元素总个数

```java
/**
   * count 返回流中元素总个数
   */
@Test
public void test06(){
    long count = employees.stream().count();
    System.out.println(count); // 7
}
```

#### 3.1.7 max 返回流中最大值

```java
/**
   * max 返回流中最大值
   */
@Test
public void test07(){
    Optional<Employee> max = employees.stream()
      .max((e1, e2) -> e1.getAge() - e2.getAge());
    System.out.println(max==null?"null":max.get().getAge()); // 93
}
```

#### 3.1.8 min 返回流中最小值

```java
/**
   * min 返回流中最小值
   */
@Test
public void test08(){
    Employee employee = employees.stream()
      .min((e1, e2) -> e1.getAge() - e2.getAge()).orElse(null);
    System.out.println(employee==null?"null":employee.getAge()); // 23
    // Employee【id=2, name='SI LI', salary=4500.0, age=23】
    System.out.println(employee);
}
```

### 3.2 归约和收集

#### 3.2.1 reduce

**reduce-- -- -- 将流中元素反复结合,得到一个新的值**

```java
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9);
/**
   * 归约,求和
   */
@Test
public void test01(){
    Integer sum = list.stream()
      .reduce(1, (t, s) -> t + s);// 初始为1
    System.out.println(sum); // 46
}
```

```java
/**
   * 获取员工工资总和
   *      map-reduce 模式
   */
@Test
public void test02(){
    Double salarySum = employees.stream()
      .map(Employee::getSalary)
      // .reduce((double) 0, (s1, s2) -> s1 + s2);
      .reduce(Double::sum).orElse(0.0D);
    System.out.println(salarySum); // 44500.0
}
```

#### 3.2.2 collectors

##### 1 toList()/toSet()

```java
/**
   * 收集名字到list/set里
   */
@Test
public void test01() {
    List<String> list = employees.stream()
      .map(Employee::getName)
      .collect(Collectors.toList());
    list.forEach(System.out::println);
    System.out.println("===========================");
    Set<String> set = employees.stream()
      .map(Employee::getName)
      .collect(Collectors.toSet());
    set.forEach(System.out::println);
}
```

##### 2 HashSet/LinkedHashSet

```java
/**
   * 收集名字到HashSet/LinkedHashSet里
   */
@Test
public void test02() {
    Set<String> set = employees.stream()
      .map(Employee::getName)
      .collect(Collectors.toCollection(LinkedHashSet::new));
    set.forEach(System.out::println);
}

ArrayList<IdtCorpBasic> disList = list.stream().collect(
                  Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(IdtCorpBasic::getUscc))), ArrayList::new)
            );
```

##### 3 计数 count

```java
/**
   * 总数
   */
@Test
public void test03() {
    Long count = employees.stream()
      .map(Employee::getName)
      .collect(Collectors.counting());
    // .count();
    System.out.println(count); // 7
}
```

##### 4 平均值 averagingDouble

```java
/**
   * 平均值
   */
@Test
public void test04() {
    double count = employees.stream()
      .collect(Collectors.averagingDouble(Employee::getSalary));
    System.out.println(count); // 6357.142857142857
    System.out.println(String.format("0.00", count)); //0.00

}
```

##### 5 最大值 maxBy

```java
/**
   * 最大值
   */
@Test
public void test05() {
    Employee employee = employees.stream()
      .collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))).orElse(null);
    System.out.println(employee.getSalary()); // 9000.0

```

##### 6 最小值 minBy

```java
/**
   * 最小值
   */
@Test
public void test06() {
    Double minSalary = employees.stream()
      .map(Employee::getSalary)
      .collect(Collectors.minBy(Double::compare)).orElse(null);
    System.out.println(minSalary); // 3500.0
}

```

##### 7 分组 groupingBy

```java
/**
   * 分组
   */
@Test
public void test07_1() {
    Map<Integer, List<Employee>> byAge = employees.stream()
      .collect(Collectors.groupingBy(Employee::getAge));
    for (Map.Entry<Integer, List<Employee>> age : byAge.entrySet()) {
      System.out.println(age.getKey() + ":" + age.getValue());
    }
}

/**
   * 多属性拼接为key
   */
@Test
public void test07_2() {
    Map<String, List<Employee>> byIdAndName = employees.stream()
      .collect(Collectors.groupingBy(e -> ((Employee) e).getId() + "--" + ((Employee) e).getName()));
    for (Map.Entry<String, List<Employee>> map : byIdAndName.entrySet()) {
      System.out.println(map.getKey() + ":" + map.getValue());
    }
}
```

```java
/**
   * 多级分组
   */
@Test
public void test08() {
    Map<String, Map<Object, List<Employee>>> levMap = employees.stream()
      .collect(Collectors.groupingBy(Employee::getName, Collectors.groupingBy(e -> {
            if (((Employee) e).getAge() <= 35) {
                return "青年";
            } else if (((Employee) e).getAge() <= 55) {
                return "中年";
            } else {
                return "老年";
            }
      })));

    System.out.println(levMap.getClass());// class java.util.HashMap

    for (Map.Entry<String, Map<Object, List<Employee>>> lev : levMap.entrySet()) {
      System.out.println(lev.getKey() + ":" + lev.getValue());
    }
}
```

##### 8 分区 partitioningBy

```java
/**
   * 分区
   *返回一个map,内部有key为true和false的两个对象列表
   */
@Test
public void test09(){
    Map<Boolean, List<Employee>> depart = employees.stream()
      .collect(Collectors.partitioningBy(e -> e.getAge() > 30)
                );
    for (Map.Entry<Boolean, List<Employee>> part : depart.entrySet()) {
      System.out.println(part.getKey() + ":" + part.getValue());
    }
    /*
false:
true:
         */
}
```

##### 9 拼接 joining

```java
/**
   * 拼接
   */
@Test
public void test11(){
    String names = employees.stream()
      .map(Employee::getName)
      // prefix和suffix是拼接之后,整体的前后缀
      .collect(Collectors.joining(",", "=", "="));
    System.out.println(names);
}
```

saz 发表于 2021-11-22 14:40

瞅瞅图片

unmask 发表于 2021-11-22 22:12

感谢分享,非常实用
页: [1]
查看完整版本: 笔记 Java8 Stream API