程序员最近都爱上了这个网站  程序员们快来瞅瞅吧!  it98k网:it98k.com

本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长


+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

Java8新特性 - Stream流

发布于2021-06-12 13:55     阅读(676)     评论(0)     点赞(16)     收藏(3)


Stream流

Stream的三个操作步骤

  • 创建Stream
  • 中间操作
  • 终止操作(终端操作)

创建Stream

创建stream的四种方式

  • 通过Collection系列集合提供的stream()方法或parallelStream()
		List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();
		Stream<String> stream2 = list.parallelStream();
  • 通过静态方法获取一个数组流
		Employee[] employees = new Employee[10];
        Stream<Employee> stream2 = Arrays.stream(employees);
  • 通过Stream类静态类中的of方法
		Stream<String> stream3 = Stream.of("aa","bb","cc","dd");
  • 创建无限流:两种(迭代和生成)
		// 4.1迭代 : 传一个起始值和一个一元运算
        Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2);
        /// 中间操作  终止操作
        stream4.limit(10).forEach(System.out::println);

        // 4.2生成(无限生成随机数)
        Stream.generate(()->Math.random()).forEach(System.out::println);

中间操作

中间操作:不会执行任何操作,终止操作:一次性执行全部内容,即“惰性求值”

1. 筛选与切片

方法描述
filter接收Lambda,从流中排除某些元素
limit截断流,使其元素不超过给定数量
skip(n)跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补
distinct筛选,通过流所生成元素的hashCode() 和 equals() 去除重复元素

例子:

  • Employee 对象
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode()
public class Employee {

    private int id;
    private String name;
    private int age;
    private double salary;
    private Status status;

    public enum Status {
        BUSY,FREE,VOCATION;
    }

    public Employee(int id, String name, int age, double salary) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }
}

1.1 filter:过滤

	List<Employee> employees = Arrays.asList(
            new Employee(1, "张三", 18, 1111.11),
            new Employee(2, "李四", 49, 2222.22),
            new Employee(3, "赵六", 57, 3333.33),
            new Employee(4, "田七", 35, 4444.44),
            new Employee(4, "田七", 35, 4444.44)
    );	

    /**
     * 中间操作:不会执行任何操作,终止操作:一次性执行全部内容,即“惰性求值”
     * 1.筛选与切片:filter
     */
	@Test
    public void test1() {
        // 内部迭代:迭代操作由Stream API完成
        employees.stream()
                .filter(e -> e.getAge() > 35)
                .forEach(System.out::println);

        // 外部迭代
        Iterator<Employee> it = employees.iterator();

        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }

1.2 limit:截取

package cn.luis.stream.intermediate;

import cn.luis.stream.Employee;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

public class LimitTest {

    List<Employee> employees = Arrays.asList(
            new Employee(1, "张三", 18, 1111.11),
            new Employee(3, "赵六", 57, 3333.33),
            new Employee(4, "田七", 35, 4444.44),
            new Employee(2, "李四", 49, 2222.22),
            new Employee(5, "田七", 35, 4444.44)
    );

    /**
     * 中间操作:不会执行任何操作,终止操作:一次性执行全部内容,即“惰性求值”
     * 2.筛选与切片:limit
     */
    @Test
    public void test2() {
        // limit;短路,找到两个符合的就终止了
        employees.stream()
                .filter(e -> {
                    //System.out.println("不符合条件的或者直接抛弃了!" + e);
                    return e.getAge() > 20;
                })
                .limit(2)
                .forEach(System.out::println);
    }

}

1.3 skip:跳过

package cn.luis.stream.intermediate.shanxuanqiepian;

import cn.luis.stream.common.Employee;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.List;

public class SkipTest {

    List<Employee> employees = Arrays.asList(
            new Employee(1, "张三", 18, 1111.11),
            new Employee(2, "李四", 49, 2222.22),
            new Employee(3, "赵六", 57, 3333.33),
            new Employee(4, "田七", 35, 4444.44),
            new Employee(5, "田七", 35, 4444.44)
    );

    /**
     * 中间操作:不会执行任何操作,终止操作:一次性执行全部内容,即“惰性求值”
     * 1.筛选与切片:skip
     */
    @Test
    public void test3() {
        // limit;跳过前两个
        employees.stream()
                .filter(e -> e.getAge() > 18)
                .skip(2)
                .forEach(System.out::println);
    }
}

1.4 distinct:去重

package cn.luis.stream.intermediate.shanxuanqiepian;

import cn.luis.stream.common.Employee;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.List;

public class DistinctTest {

    List<Employee> employees = Arrays.asList(
            new Employee(1, "张三", 18, 1111.11),
            new Employee(2, "李四", 49, 2222.22),
            new Employee(3, "赵六", 57, 3333.33),
            new Employee(4, "田七", 35, 4444.44),
            new Employee(5, "田七", 35, 4444.44)
    );

    /**
     * 筛选与切片:distinct : 筛选,通过流所生成元素的hashCode() 和 equals() 去除重复元素
     */
    @Test
    public void test4() {
        // distinct;去重 【employees要重写hashcode和equals】
        employees.stream()
                .filter(e -> {
                    return e.getAge() > 25;
                })
                .distinct()
                .forEach(System.out::println);
    }

}

2. 映射

map里传入Function函数型接口,传入一个参数返回一个值

方法描述
map接收lambda,将元素转换成其他形式或提取信息,接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap接收一个函数作为参数,将该流中的每一个值都换成另一个流,然后把所有流连成一个流

例子:

package cn.luis.stream.intermediate.yingshe;

import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class MapTest {

    List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");

    /**
     * map
     */
    @Test
    public void test1() {
        list.stream()
                .map(s -> s.toUpperCase())
                .forEach(System.out::println);

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


        // 这种套娃式可以用flatMap替代 (见test2)
        Stream<Stream<Character>> stream = list.stream().map(MapTest::filterCharacter);
        stream.forEach(sm -> {
            sm.forEach(System.out::println);
        });
    }

    /**
     * flatMap: 扁平化map
     */
    @Test
    public void test2() {
        // 扁平化map:原来把流放入map流,现在是直接将流中的元素放入flatmap流中
        // 把{a,a,a},{b,b,b} ... 转换成了 {a,a,a,b,b,b,c,c,c}
        Stream<Character> characterStream = list.stream()
                .flatMap(MapTest::filterCharacter);

        characterStream.forEach(System.out::println);
    }

    /**
     * 将字符串拆分
     * @param str
     * @return
     */
    public static Stream<Character> filterCharacter(String str) {
        List<Character> list = new ArrayList<>();
        for (Character ch : str.toCharArray()) {
            list.add(ch);
        }
        return list.stream();
    }
    
}

3. 排序

方法描述
sorted()自然排序(Comparable)
sorted(Comparator com)定制排序(Comparator)

例子:

package cn.luis.stream.intermediate.sort;

import cn.luis.stream.common.Employee;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class SortedTest {

    List<Employee> employees = Arrays.asList(
            new Employee(1, "张三", 18, 1111.11),
            new Employee(2, "李四", 49, 2222.22),
            new Employee(3, "赵六", 57, 3333.33),
            new Employee(4, "田七", 35, 4444.44),
            new Employee(5, "田七", 35, 4444.44)
    );

    /**
     * 3. 排序
     * sorted() -- 自然排序(Comparable)
     * sorted(Comparator) -- 定制排序(Comparator)
     */
    @Test
    public void test1() {

        // 自然排序ba
        Stream<Employee> sorted = employees.stream().sorted();

        // 按年龄排序,年龄相同按名字排序
        employees.stream()
                .sorted((e1, e2) -> {
                    if (e1.getAge() == e2.getAge()) {
                        return e1.getName().compareTo(e2.getName());
                    } else {
                        Integer x = e1.getAge();
                        return x.compareTo(e2.getAge());
                    }
                }).forEach(System.out::println);
    }

}

终止操作

1. 查找与匹配

方法描述
allMatch检查是否匹配所有元素
anyMatch检查是否至少匹配一个元素
noneMatch检查是否没有匹配所有元素
findFirst返回第一个元素
findAny返回当前流中的任意元素
count返回流中元素的总个数
max返回流中的最大值
min返回流中的最小值

例子:

  • 查找的对象
List<Employee> employees = Arrays.asList(
            new Employee(1, "张三", 18, 1111.11, Status.BUSY),
            new Employee(2, "李四", 49, 2222.22, Status.FREE),
            new Employee(3, "赵六", 57, 3333.33, Status.BUSY),
            new Employee(4, "田七", 35, 4444.44, Status.VOCATION),
            new Employee(2, "李十四", 49, 2222.22, Status.FREE),
            new Employee(2, "李十四", 49, 2222.22, Status.FREE),
            new Employee(2, "李十四", 49, 2222.22, Status.FREE)
    );

1.1 match:匹配

	@Test
    public void matchTest() {

        // 检查是否匹配所有元素
        boolean b = employees.stream()
                .allMatch(e -> Employee.Status.BUSY.equals(e.getStatus()));
        System.out.println(b);

        // 检查是否至少匹配一个元素
        boolean b2 = employees.stream()
                .anyMatch(e -> Employee.Status.BUSY.equals(e.getStatus()));
        System.out.println(b2);

        // 检查是否没有匹配所有元素
        boolean b3 = employees.stream()
                .noneMatch(e -> Employee.Status.BUSY.equals(e.getStatus()));
        System.out.println(b3);
    }

1.2 find:查找

	@Test
    public void findTest() {

        // 返回第一个元素
        Optional<Employee> first = employees.stream()
                .sorted(Comparator.comparingDouble(Employee::getSalary))
                .findFirst();
        System.out.println(first.get());

        // 返回当前流中的任意元素
        Optional<Employee> any = employees.stream()
                .filter(e -> Employee.Status.FREE.equals(e.getStatus()))
                .findAny();
        System.out.println(any.get());

        // 【并行】 返回当前流中的任意元素
        Optional<Employee> any2 = employees.parallelStream()
                .filter(e -> Employee.Status.FREE.equals(e.getStatus()))
                .findAny();
        System.out.println(any2.get());
    }

1.3 最值

	@Test
    public void numTest() {

        // 返回流中元素的总个数
        long count = employees.stream()
                .count();
        System.out.println(count);

        // 返回流中的最大值
        Optional<Employee> max = employees.stream()
                .max(Comparator.comparingDouble(Employee::getAge));
        System.out.println(max.get());

        // 返回流中的最小值
        Optional<Double> min = employees.stream()
                .map(Employee::getSalary)
                .min(Double::compare);
        System.out.println(min.get());
    }

2. 规约

方法描述
reduce可以将流中的元素反复结合起来,得到一个值
  • 准备数据
public class EmpData {

    public static List<Employee> findAllEmployee() {
        return Arrays.asList(
                new Employee(1, "张三", 18, 1111.11, Employee.Status.BUSY),
                new Employee(2, "李四", 49, 2222.22, Employee.Status.FREE),
                new Employee(3, "赵六", 57, 3333.33, Employee.Status.BUSY),
                new Employee(4, "田七", 35, 4444.44, Employee.Status.VOCATION),
                new Employee(2, "李十四", 49, 2222.22, Employee.Status.FREE),
                new Employee(2, "李十四", 49, 2222.22, Employee.Status.FREE),
                new Employee(2, "李十四", 49, 2222.22, Employee.Status.FREE)
        );
    }
}
  • 例子:
/**
 * 规约
 */
public class ReduceTest {

    List<Employee> employees = EmpData.findAllEmployee();

    /**
     * reduce(起始值,二元运算): 规约
     */
    @Test
    public void test3() {

        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

        Integer a = list.stream()
                .reduce(0, (x, y) -> x + y);
        System.out.println(a);

        Integer b = list.stream()
                .reduce(0, Integer::sum);
        System.out.println(b);

        // 优化写法(map-reduce模式)
        Optional<Integer> c = list.stream()
                .reduce(Integer::sum);
        System.out.println(c.get());

        // 优化写法(map-reduce模式) + orElse
        Integer d = list.stream()
                .reduce(Integer::sum).orElse(0);
        System.out.println(d);
    }

}

3. 收集

包括集合、计算、分组、分区

方法描述
collect将流转换为其他形式,接收一个Collectot接口的实现,用于给Stream中的元素汇总的方法

3.1 集合

  • 集合操作
方法描述
Collectors.toList()转成 List
Collectors.toSet()转成 Set
Collectors.toCollection(HashSet::new)放到其他类型集合
	@Test
    public void test4() {
        List<String> nameList = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toList());

        nameList.forEach(System.out::println);

        System.out.println("-----------过滤重复数据--------------");
        Set<String> nameSet = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toSet());

        nameSet.forEach(System.out::println);

        System.out.println("-----------放到其他类型集合--------------");
        HashSet<String> nameHashSet = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toCollection(HashSet::new));

        nameHashSet.forEach(System.out::println);
    }

3.2 计算

  • 计算
方法描述
Collectors.counting()总数
Collectors.averagingDouble()平均值
Collectors.maxBy()最大值
Collectors.minBy()最小值
Collectors.summingDouble()总和
Collectors.summarizingDouble()得到的对象可以用来继续计算(getAverage(),getMax(),getCount())
	@Test
    public void test5() {
        // 总数
        Long collect = employees.stream()
                .collect(Collectors.counting());
        System.out.println(collect);

        // 平均值
        Double avg = employees.stream()
                .collect(Collectors.averagingDouble(Employee::getSalary));
        System.out.println(avg);

        // 最大值
        Optional<Double> max = employees.stream()
                .map(Employee::getSalary)
                .collect(Collectors.maxBy(Double::compare));
        System.out.println(max.get());

        // 最小值
        Optional<Double> min = employees.stream()
                .map(Employee::getSalary)
                .collect(Collectors.minBy(Double::compare));
        System.out.println(min.get());

        // 总和
        Double sum = employees.stream()
                .collect(Collectors.summingDouble(Employee::getSalary));
        System.out.println(sum);

        // Double:强大计算
        DoubleSummaryStatistics salary = employees.stream()
                .collect(Collectors.summarizingDouble(Employee::getSalary));
        System.out.println(salary.getAverage());
        System.out.println(salary.getMax());
        System.out.println(salary.getCount());
    }

3.3 分组、分区

  • 分区、分组
方法描述
Collectors.groupingBy()分组
Collectors.partitioningBy()分区
  • 分组
	List<Employee> employees = EmpData.findAllEmployee();

    /**
     * collect 之 分组
     */
    @Test
    public void groupTest1() {
        // 分组
        Map<Employee.Status, List<Employee>> statusListMap = employees.stream()
                .collect(Collectors.groupingBy(Employee::getStatus));
        statusListMap.keySet().forEach(System.out::println);
        statusListMap.entrySet().forEach(System.out::println);
        System.out.println(statusListMap);

    }
  • 结果:
VOCATION
BUSY
FREE
VOCATION=[Employee(id=4, name=田七, age=35, salary=4444.44, status=VOCATION)]
BUSY=[Employee(id=1, name=张三, age=18, salary=1111.11, status=BUSY), Employee(id=3, name=赵六, age=57, salary=3333.33, status=BUSY)]
FREE=[Employee(id=2, name=李四, age=49, salary=2222.22, status=FREE), Employee(id=2, name=李十四, age=49, salary=2222.22, status=FREE), Employee(id=2, name=李十四, age=49, salary=2222.22, status=FREE), Employee(id=2, name=李十四, age=49, salary=2222.22, status=FREE)]
{VOCATION=[Employee(id=4, name=田七, age=35, salary=4444.44, status=VOCATION)], BUSY=[Employee(id=1, name=张三, age=18, salary=1111.11, status=BUSY), Employee(id=3, name=赵六, age=57, salary=3333.33, status=BUSY)], FREE=[Employee(id=2, name=李四, age=49, salary=2222.22, status=FREE), Employee(id=2, name=李十四, age=49, salary=2222.22, status=FREE), Employee(id=2, name=李十四, age=49, salary=2222.22, status=FREE), Employee(id=2, name=李十四, age=49, salary=2222.22, status=FREE)]}
  • 多级分组
	List<Employee> employees = EmpData.findAllEmployee();
    @Test
    public void groupTest2() {
        // 多级分组
        Map<String, List<Employee>> emMap = employees.stream()
                .collect(Collectors.groupingBy((employee) -> {
                    if (employee.getAge() <= 35) {
                        return "青年";
                    } else if (employee.getAge() > 20) {
                        return "老年";
                    } else {
                        return "少年";
                    }
                }));

        // 查看分组(第一种方式)
        emMap.keySet().forEach(key -> {
            System.out.println("======" + key + "======");
            emMap.get(key).forEach(System.out::println);
        });

        // 查看分组(第二种方式)
        emMap.forEach((k, v) -> {
            System.out.println("======" + k + "======");
            v.forEach(map -> {
                System.out.println(map);
            });
        });
    }

  • 结果:
======青年======
Employee(id=1, name=张三, age=18, salary=1111.11, status=BUSY)
Employee(id=4, name=田七, age=35, salary=4444.44, status=VOCATION)
======老年======
Employee(id=2, name=李四, age=49, salary=2222.22, status=FREE)
Employee(id=3, name=赵六, age=57, salary=3333.33, status=BUSY)
Employee(id=2, name=李十四, age=49, salary=2222.22, status=FREE)
Employee(id=2, name=李十四, age=49, salary=2222.22, status=FREE)
Employee(id=2, name=李十四, age=49, salary=2222.22, status=FREE)

  • 分区
	/**
     * collect 之 分区
     */
    @Test
    public void area1() {

        // 分区: 满足条件的分一个区,不满足的分一个区
        Map<Boolean, List<Employee>> emMap = employees.stream()
                .collect(Collectors.partitioningBy(e -> e.getSalary() > 2000.0));

        // 查看分组(第二种方式)
        emMap.forEach((k, v) -> {
            System.out.println("======" + k + "======");
            v.forEach(map -> {
                System.out.println(map);
            });
        });
    }
  • 结果
======false======
Employee(id=1, name=张三, age=18, salary=1111.11, status=BUSY)
======true======
Employee(id=2, name=李四, age=49, salary=2222.22, status=FREE)
Employee(id=3, name=赵六, age=57, salary=3333.33, status=BUSY)
Employee(id=4, name=田七, age=35, salary=4444.44, status=VOCATION)
Employee(id=2, name=李十四, age=49, salary=2222.22, status=FREE)
Employee(id=2, name=李十四, age=49, salary=2222.22, status=FREE)
Employee(id=2, name=李十四, age=49, salary=2222.22, status=FREE)

3.4 字符串操作

  • 字符串操作
方法描述
Collectors.joining(",")字符串拼接
@Test
    public void str() {
        // 字符串拼接
        String str = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.joining(","));
        System.out.println(str);
    }
  • 结果
张三,李四,赵六,田七,李十四,李十四,李十四

原文链接:https://blog.csdn.net/qq_39720594/article/details/117747963



所属网站分类: 技术文章 > 博客

作者:狗蛋来了

链接:http://www.javaheidong.com/blog/article/222197/8b009a7c970cd6585b06/

来源:java黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

16 0
收藏该文
已收藏

评论内容:(最多支持255个字符)