Java8 Stream流式处理

什么是Java 8 Stream?
Java stream API定义了数据元素离散序列上的函数式操作。这些数据元素通常由一些标准数据结构 (ADT) 提供,例如java.util.collection中提供的数据。这些数据结构通常称为Stream源。
Java 8增强了现有的API,例如集合,数组等,以添加新方法来创建流对象实例。这个API本身提供了一些静态的方法来生成有限/无限的数据元素流。
Stream是功能性的,它们在提供的源上运行并产生结果,而不是修改源。
Stream生命周期可以分为三种类型的操作

  1. 从数据源获取Stream的实例。数据源可能是数组、集合、生成器函数、I/O通道等
  2. 将Stream转换为另一个Stream的0…N个中间操作,例如过滤,排序,元素转换 (映射)
  3. 产生结果的终端操作,例如计数,汇总或新的集合。
    在这里插入图片描述
    在这里插入图片描述
Employee[] arrayOfEmps = {
    new Employee(1, "Jeff Bezos", 100000.0),
    new Employee(2, "Bill Gates", 200000.0),
    new Employee(3, "Mark Zuckerberg", 300000.0)
};
List<Employee> empList = Arrays.asList(arrayOfEmps);

//获取按英文名称首字母(小写)分组下,字母最多的
Comparator<Employee> byNameLength = Comparator.comparing(Employee::getName);
Map<Character, Optional<Employee>> longestNameByAlphabet = empList.stream() //得到stream对象
    .peek(employee -> employee.setName(employee.getName().toLowerCase())) //中间操作
    .collect( //结束操作
        Collectors.groupingBy(e -> new Character(e.getName().charAt(0)),
                              Collectors.reducing(BinaryOperator.maxBy(byNameLength)))
    );
{b=Optional[Id: 2 Name:bill gates Price:200000.0], 
 j=Optional[Id: 1 Name:jeff bezos Price:100000.0], 
 m=Optional[Id: 3 Name:mark zuckerberg Price:300000.0]}

如何得到Stream对象

Comparator<Employee> byNameLength = Comparator.comparing(Employee::getName);

        Map<Character, Optional<Employee>> longestNameByAlphabet = empList.stream()
            .sort
            .collect(
                Collectors.groupingBy(e -> new Character(e.getName().charAt(0)),
                        Collectors.reducing(BinaryOperator.maxBy(byNameLength))));

Java 8修改了现有的集合和其他数据结构API来创建/生成Stream实例:
收集接口添加了新的默认方法,如stream() 和parallelStream(),它们返回Stream实例。

List<String> list = Arrays.asList("1", "2", "3");
Stream<String> stream = list.stream();

Stream.generate()自定义流

Stream.generate(Math::random);

递归生成

Stream<Integer> infiniteStream = Stream.iterate(2, i -> i * 2); //生成2的N次方数据

中间操作
数据源

private static Employee[] arrayOfEmps = {
            new Employee(1, "Jeff Bezos", 100000.0),
            new Employee(2, "Bill Gates", 200000.0),
            new Employee(3, "Mark Zuckerberg", 300000.0)
    };

private static List<Employee> empList = Arrays.asList(arrayOfEmps);
private static EmployeeRepository employeeRepository = new EmployeeRepository(empList);

peek()对元素内部调整

/**
 * peek()函数用法,对元素内部数据进行调整时使用
 * 如每个员工涨薪10%
 */
@Test
public void whenIncrementSalaryUsingPeek_thenApplyNewSalary() {
    Employee[] arrayOfEmps = {
            new Employee(1, "Jeff Bezos", 100000.0),
            new Employee(2, "Bill Gates", 200000.0),
            new Employee(3, "Mark Zuckerberg", 300000.0)
    };

    List<Employee> empList = Arrays.asList(arrayOfEmps);

    empList.stream()
            .peek(System.out::println) //调整前
            .peek(e -> e.salaryIncrement(10.0))
            .peek(System.out::println)//调整后
            //将前面操作后的对象重新包装为新的List集合返回
            .collect(Collectors.toList());
}

运行结果:

Id: 1 Name:Jeff Bezos Price:100000.0
Id: 1 Name:Jeff Bezos Price:110000.0
Id: 2 Name:Bill Gates Price:200000.0
Id: 2 Name:Bill Gates Price:220000.0
Id: 3 Name:Mark Zuckerberg Price:300000.0
Id: 3 Name:Mark Zuckerberg Price:330000.0

map()映射转换

/**
* map函数的使用,用于将元素本身进行加工或类型转换
* 例如:遍历所有id对象,并在数据库查询导数据Employee对象,返回的是List<Employee>集合
* map()与peek()最大的区别是:
* peek()不改变对象本身,如进入的原始数据是List<Integer>,返回的还是List<Integer>
* map()则用于转换工作,如进入的原始数据是List<Integer>,返回的数据类型被转为List<Employee>
*/
@Test
    public void whenMapIdToEmployees_thenGetEmployeeStream() {
    Integer[] empIds = {1, 2, 3};
    List<Employee> employees = Stream.of(empIds)
        .map(employeeRepository::findById)
        .collect(Collectors.toList());
    assertEquals(employees.size(), empIds.length);
}

flatMap()展平结果

/**
 * flatMap()用于展平结果,将多级结构转换为平铺结构
 */
@Test
public void whenFlatMapEmployeeNames_thenGetNameStream() {
    List<List<String>> namesNested = Arrays.asList(
            Arrays.asList("Jeff", "Bezos"),
            Arrays.asList("Bill", "Gates"),
            Arrays.asList("Mark", "Zuckerberg"));

    List<String> namesFlatStream = namesNested.stream()
            .flatMap(Collection::stream)
            .collect(Collectors.toList());

    System.out.println(namesFlatStream);
}
[Jeff, Bezos, Bill, Gates, Mark, Zuckerberg]

filter()过滤流数据

/**
 * filter()用于过滤数据,符合条件的数据保留,向后流处理,不符合条件的数据被筛选掉
 * 这里得到薪资>200000的所有员工对象
 */
@Test
public void whenFilterEmployees_thenGetFilteredStream() {
    Integer[] empIds = {1, 2, 3, 4};

    List<Employee> employees = Stream.of(empIds)
            .map(employeeRepository::findById)
            .filter(e -> e != null)
            .filter(e -> e.getSalary() > 200000)
            .collect(Collectors.toList());

    System.out.println(employees);
}

偏移量运算,skip()跳过三个,limit()向后获取5个

/**
 * 偏移量运算,skip跳过三个,limit向后获取5个
 */
@Test
public void whenLimitInfiniteStream_thenGetFiniteElements() {
    //iterate迭代器方法用于生成流的规则,生成以为2的N次方数据
    Stream<Integer> infiniteStream = Stream.iterate(2, i -> i * 2);
    System.out.println(infiniteStream);
    List<Integer> collect = infiniteStream
            .skip(3)//跳过2^1,2^2,2^3
            .limit(5)//保留2^4、2^5、2^6、2^7、2^8
            .collect(Collectors.toList());//转换为数字

    System.out.println(collect);
}
[16, 32, 64, 128, 256]

sorted()数据排序

/**
 * sorted()数据排序,e1、e2比较。e1-e2>0升序排列,e2-e1<0降序排列
 */
@Test
public void whenSortStream_thenGetSortedStream() {
    List<Employee> employees = empList.stream()
            .sorted((e1, e2) -> e1.getName().compareTo(e2.getName()))
            .collect(Collectors.toList());
    assertEquals(employees.get(0).getName(), "Bill Gates");
    assertEquals(employees.get(1).getName(), "Jeff Bezos");
    assertEquals(employees.get(2).getName(), "Mark Zuckerberg");
}

distinct()去重

/**
 * distinct()去重函数
 */
@Test
public void whenApplyDistinct_thenRemoveDuplicatesFromStream() {
    List<Integer> intList = Arrays.asList(2, 5, 3, 2, 4, 3);
    List<Integer> distinctIntList = intList.stream().distinct().collect(Collectors.toList());

    System.out.println(distinctIntList);
}

match()规则匹配返回Boolean

/**
 * allMatch()判断给定的流数据是否全部符合判断要求,全部满足返回true
 * anyMatch()判断给定的流数据是否存在符合要求的数据,只要有一个满足则返回true
 * noneMatch()与anyMatch()相反,只要有一个满足要求则返回false
 */
@Test
public void whenApplyMatch_thenReturnBoolean() {
    List<Integer> intList = Arrays.asList(2, 4, 5, 6, 8);

    boolean allEven = intList.stream().allMatch(i -> i % 2 == 0);
    boolean oneEven = intList.stream().anyMatch(i -> i % 2 == 0);
    boolean noneMultipleOfThree = intList.stream().noneMatch(i -> i % 3 == 0);

    assertEquals(allEven, false);
    assertEquals(oneEven, true);
    assertEquals(noneMultipleOfThree, false);
}

parallel()并行处理

/**
 * parallel()并行处理
 */
@Test
public void whenParallelStream_thenPerformOperationsInParallel() {
    Employee[] arrayOfEmps = {
            new Employee(1, "Jeff Bezos", 100000.0),
            new Employee(2, "Bill Gates", 200000.0),
            new Employee(3, "Mark Zuckerberg", 300000.0)
    };

    List<Employee> empList = Arrays.asList(arrayOfEmps);

    empList.stream().parallel().forEach(e -> e.salaryIncrement(10.0));

    assertThat(empList, contains(
            hasProperty("salary", equalTo(110000.0)),
            hasProperty("salary", equalTo(220000.0)),
            hasProperty("salary", equalTo(330000.0))
    ));
}

结束操作
forEach()替代for循环

/**
 * 利用forEach替代for循环进行遍历
 * 为每一位员工增加涨薪10%
 */
@Test
public void whenIncrementSalaryForEachEmployee_thenApplyNewSalary() {
    Employee[] arrayOfEmps = {
            new Employee(1, "Jeff Bezos", 100000.0),
            new Employee(2, "Bill Gates", 200000.0),
            new Employee(3, "Mark Zuckerberg", 300000.0)
    };

    List<Employee> empList = Arrays.asList(arrayOfEmps);
    empList.stream().forEach(e -> e.salaryIncrement(10.0));
}

findFirst()获取首个流数据

/**
 * findFirst()获取第一个符合规则的流数据
 * 如果一个都没有则走orElse()返回Null
 */
@Test
public void whenFindFirst_thenGetFirstEmployeeInStream() {
    Integer[] empIds = {1, 2, 3, 4};

    Employee employee = Stream.of(empIds)
            .map(employeeRepository::findById)
            .filter(e -> e != null)
            .filter(e -> e.getSalary() > 100000)
            .findFirst()
            .orElse(null);

    System.out.println(employee);
}

collect()用于集合类型转换

/**
 * collect()结尾函数,用于集合类型转换
 * 例如将前面的数据转换为Set集合
 */
@Test
public void whenCollectStreamToList_thenGetList() {
    Set<Employee> employees = empList.stream().collect(Collectors.toSet());

    System.out.println(employees);
}

toArray(),将流式数据转换为数组

/**
 * toArray(),结尾函数,将流式数据转换为数组
 */
@Test
public void whenStreamToArray_thenGetArray() {
    Employee[] employees = empList.stream().toArray(Employee[]::new);

    for (Employee employee: employees){
        System.out.println(employee);
    }
}

count()统计满足条件数据的总量

/**
 * count()结尾函数,统计满足条件数据的总量
 */
@Test
public void whenStreamCount_thenGetElementCount() {
    Long empCount = empList.stream()
            .filter(e -> e.getSalary() > 200000)
            .count();

    System.out.println(empCount);
}

max()/min()取最大值与最小值

/**
 * 对于数字类型,不必给条件,如下max()
 */
@Test
public void whenFindMaxOnIntStream_thenGetMaxInteger() {
    Integer latestEmpId = empList.stream()
            .mapToInt(Employee::getId)//只获取Id类型,取最大的id号
            .max()
            .orElseThrow(NoSuchElementException::new);

    System.out.println(latestEmpId);
}
/**
 * max()获取流中最大值
 * orElseThrow()代表没有符合的结果时抛出NoSuchElementException异常
 */
@Test
public void whenFindMax_thenGetMaxElementFromStream() {
    Employee maxSalEmp = empList.stream()
            .max(Comparator.comparing(Employee::getSalary))
            .orElseThrow(NoSuchElementException::new);

    System.out.println(maxSalEmp);
}
/**
 * min()获取流中最小值
 * orElseThrow()代表没有符合的结果时抛出NoSuchElementException异常
 */
@Test
public void whenFindMin_thenGetMinElementFromStream() {
    Employee minEmp = empList.stream()
            .min((e1, e2) -> e1.getId() - e2.getId())
            .orElseThrow(NoSuchElementException::new);

    System.out.println(minEmp);
}

average()求平均数

/**
 * average()求平均数运算,要求是流数据类型为数字类型
 * 没有匹配抛出NoSuchElementException异常
 */
@Test
public void whenApplySumOnIntStream_thenGetSum() {
    Double avgSal = empList.stream()
            .mapToDouble(Employee::getSalary)//只获取工资数据
            .average()
            .orElseThrow(NoSuchElementException::new);

    System.out.println(avgSal);
}

reduce()按规则聚合结果

public class Main {
    public static void main(String[] args) {
        int sum = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9).reduce(0, (acc, n) -> acc + n);
        System.out.println(sum); // 45
    }
}

翻译过来的伪代码

//stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9)
int sum = 0;
for (n : stream) {
    sum = sum + n;
}
@Test
public void whenApplyReduceOnStream_thenGetValue() {
    Double sumSal = empList.stream()
        .map(Employee::getSalary)
        .reduce(0.0, Double::sum);//起始值为0,每次对所有流元素进行累加

    assertEquals(sumSal, new Double(600000));
}

Collectors.joining()创建长字符串

/**
 * collect(Collectors.joining(", ")) 将流数据转为一个长字符串
 */
@Test
public void whenCollectByJoining_thenGetJoinedString() {
    String empNames = empList.stream()
            .map(Employee::getName)
            .collect(Collectors.joining(", "))
            .toString();

    assertEquals(empNames, "Jeff Bezos, Bill Gates, Mark Zuckerberg");
}

Collectors.summarizing…(聚合运算)

/**
 * 流数据为对象时,聚合运算.
 */
@Test
public void whenApplySummarizing_thenGetBasicStats() {
    DoubleSummaryStatistics stats = empList.stream()
            .collect(Collectors.summarizingDouble(Employee::getSalary));

    assertEquals(stats.getCount(), 3); //数量
    assertEquals(stats.getSum(), 600000.0, 0);//求和
    assertEquals(stats.getMin(), 100000.0, 0);//最小值
    assertEquals(stats.getMax(), 300000.0, 0);//最大值
    assertEquals(stats.getAverage(), 200000.0, 0);//平均值
}
/**
 * 流数据为数值时,直接聚合运算
 */
@Test
public void whenApplySummaryStatistics_thenGetBasicStats() {
    DoubleSummaryStatistics stats = empList.stream()
            .mapToDouble(Employee::getSalary)
            .summaryStatistics();

    assertEquals(stats.getCount(), 3);
    assertEquals(stats.getSum(), 600000.0, 0);
    assertEquals(stats.getMin(), 100000.0, 0);
    assertEquals(stats.getMax(), 300000.0, 0);
    assertEquals(stats.getAverage(), 200000.0, 0);
}

Collectors.partitioningBy()分区运算

/**
 * partitioningBy()分区运算,类似于SQL中的Group By ,并将其放入各自的Map's value中
 */
@Test
public void whenStreamPartition_thenGetMap() {
    List<Integer> intList = Arrays.asList(2, 4, 5, 6, 8);
    Map<Boolean, List<Integer>> isEven = intList.stream().collect(
            Collectors.partitioningBy(i -> i % 2 == 0));

    assertEquals(isEven.get(true).size(), 4);//偶数组
    assertEquals(isEven.get(false).size(), 1);//奇数组
}
/**
 * 按自定义条件进行分区(Group By)
 */
@Test
public void whenStreamGroupingBy_thenGetMap() {
    //按首字母进行分区
    Map<Character, List<Employee>> groupByAlphabet = empList.stream().collect(
            Collectors.groupingBy(e -> new Character(e.getName().charAt(0))));

    assertEquals(groupByAlphabet.get('B').get(0).getName(), "Bill Gates");
    assertEquals(groupByAlphabet.get('J').get(0).getName(), "Jeff Bezos");
    assertEquals(groupByAlphabet.get('M').get(0).getName(), "Mark Zuckerberg");
}
/**
 * groupingBy高级玩法,先按首字母分区,然后将分区内数据转换为ID'List
 */
@Test
public void whenStreamMapping_thenGetMap() {
    Map<Character, List<Integer>> idGroupedByAlphabet = empList.stream().collect(
            Collectors.groupingBy(e -> new Character(e.getName().charAt(0)),
                    Collectors.mapping(Employee::getId, Collectors.toList())));

    assertEquals(idGroupedByAlphabet.get('B').get(0), new Integer(2));
    assertEquals(idGroupedByAlphabet.get('J').get(0), new Integer(1));
    assertEquals(idGroupedByAlphabet.get('M').get(0), new Integer(3));
}
@Test
public void whenStreamReducing_thenGetValue() {
    Double percentage = 10.0;
    Double salIncrOverhead = empList.stream().collect(Collectors.reducing(
            0.0, e -> e.getSalary() * percentage / 100, (s1, s2) -> s1 + s2));

    assertEquals(salIncrOverhead, 60000.0, 0);
}
@Test
public void whenStreamGroupingAndReducing_thenGetMap() {
    Comparator<Employee> byNameLength = Comparator.comparing(Employee::getName);

    Map<Character, Optional<Employee>> longestNameByAlphabet = empList.stream().collect(
            Collectors.groupingBy(e -> new Character(e.getName().charAt(0)),
                    Collectors.reducing(BinaryOperator.maxBy(byNameLength))));

    assertEquals(longestNameByAlphabet.get('B').get().getName(), "Bill Gates");
    assertEquals(longestNameByAlphabet.get('J').get().getName(), "Jeff Bezos");
    assertEquals(longestNameByAlphabet.get('M').get().getName(), "Mark Zuckerberg");
}

其他与异常处理orElse、orElseThrow

@Test
public void whenFindFirst_thenGetFirstEmployeeInStream() {
    Integer[] empIds = {1, 2, 3, 4};

    Employee employee = Stream.of(empIds)
            .map(employeeRepository::findById)
            .filter(e -> e != null)
            .filter(e -> e.getSalary() > 100000)
            .findFirst()
            .orElse(null);

    System.out.println(employee);
}
@Test
public void whenApplySumOnIntStream_thenGetSum() {
    Double avgSal = empList.stream()
            .mapToDouble(Employee::getSalary)//只获取工资数据
            .average()
            .orElseThrow(NoSuchElementException::new);

    System.out.println(avgSal);
}
  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奋斗的老史

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值