Java Stream操作

122 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情

自1.8有Stream已经很久了,不过之前因为各种原因并没有系统的去学习和了解这一块,最近在项目中又使用到了,借此机会来总计和记录自己学习的过程。如有理解错误或者不正确的地方请务必纠正我。

什么是Stream流

官方给的解释为: A sequence of elements supporting sequential and parallel aggregate operations. 这是什么东西??翻译过来就是支持顺序和并行聚合操作的元素序列。 看了之后貌似明白了什么又貌似什么都没明白,然后我有去查了一下资料,比较好的解释为流是从支持数据处理操作的源生成的元素序列,源可以是数组、文件、集合、函数。流不是集合元素,它不是数据结构并不保存数据,它的主要目的在于计算。

Stream流生成

流是从支持数据处理操作的源生成的元素序列,源可以是数组、文件、集合、函数。流不是集合元素 既然流是由源生成的元素序列,那我们就从数组、文件、集合、函数这几个方面来生成。

集合生成

List<Integer> list = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
    list.add(i);
}
//这个地方直接使用stream方法转换成stream流
Stream<Integer> stream = list.stream();

文件生成

Stream<String> lines = Files.lines(Paths.get("stream.txt"));

数组生成

IntStream intStream = Arrays.stream(new int[]{1, 2 });
LongStream longStream = Arrays.stream(new long[]{1, 5, 6});

Stream API的方式

//其他类型的与之类似
Stream<String> a = Stream.of("A", "B", "C", "D");

//这个提供不限流的函数操作 其中第一个参数是初始值,第二个参数是Lambda表达式函数 limit是用来进行次数限制的 与之类似的还有generate
Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(5);
Stream<Integer> generate = Stream.generate(() -> 1);

操作类型

  • 中间操作:如果调用方法之后返回的结果是Stream对象的操作是一个中间操作。
  • 终止操作:一个流有且只能有一个终止操作,当这个操作执行后,流就被关闭了,无法再被操作。

中间操作

  1. 过滤操作
//过滤掉所有的奇数,并把过滤结果转换成列表
Stream<Integer> stream = Stream.iterate(0, n -> n + 1).limit(20);
Stream<Integer> integerStream = stream.filter(item -> item % 2 == 0);

2.去重操作

【注意】如果使用的类型不是基本类型,必须要重写equals() 方法与 hashCode() 方法。

List<String> ls = Arrays.asList("1", "2", "2", "4", "2", "2", "8", "9");
Stream<String> distinct = ls.stream().distinct();

如果要对列表对象的某个属性进行过滤的话,可以用一下的方式

List<Person> peoples = new ArrayList<>();
peoples.add(new Person("zhangsan","23"));
peoples.add(new Person("zhangsan","23"));
peoples.add(new Person("zhangsan","23"));
peoples.add(new Person("lisi","23"));
peoples.add(new Person("lisi","23"));
peoples.add(new Person("lisi","23"));
List<Person> result = peoples.stream()
        .collect(
                collectingAndThen(
                        toCollection(() -> new TreeSet<>(comparing(Person::getName))),
                        ArrayList::new
                )
        );
result.forEach(System.out::println);

输出结果

MyMain.Person(name=lisi, age=23)
MyMain.Person(name=zhangsan, age=23)

也可以使用TreeSet来实现去重操作

TreeSet<Person> treeSet = new TreeSet<>(Comparator.comparing(Person::getName));
treeSet.addAll(peoples);
List<Person> result2 =  new ArrayList<>(treeSet);
result2.forEach(System.out::println);

其实也就是上面方式的拆分,只不过看起来比较明了,算是函数编程的弊端吧,对于API熟悉的人会很容易理解,但是对于不熟悉的就显得很晦涩

3.获取列表元素

Stream<Person> limit = peoples.stream().limit(3);

4.跳过N个元素

Stream<Person> skip = peoples.stream().skip(3);

limit和skip搭配起来可以实现内存分页

5.对列进行排序(默认为升序)

//简单的排序
List<String> strings = Arrays.asList("1", "3", "2", "6", "5");
List<String> collect = strings.stream().sorted().collect(Collectors.toList());
//如果需要倒序可以这样
List<String> strings = Arrays.asList("1", "3", "2", "6", "5");
List<String> collect = strings.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
//或者对某个属性进行排序
List<Person> collect1 = peoples.stream().sorted(Comparator.comparing(Person::getAge).reversed()).collect(Collectors.toList());

6.map操作

//对年龄做处理
List<Person> streamMap = peoples.stream().map(item -> {
    item.setAge("1");
    return item;
}).collect(Collectors.toList());

//实现某个逻辑返回全新的数据
List<Integer> streamMap1 = peoples.stream().map(item -> item.getName().length()).collect(Collectors.toList());

7.mapToInt操作,另外mapToLong、mapToDouble两个方法与之类似

IntStream intStream = peoples.stream().mapToInt(item -> Integer.parseInt(item.age));

8.flatMap操作

List<String> ls = Arrays.asList("A B", "C D", "E F");
List<String> collect = ls.stream().flatMap(item -> Arrays.stream(item.split(" "))).collect(Collectors.toList());

输出

["A","B","C","D","E","F"]

map和flatMap区别在于,map在处理数据的时候输入一个输出一个,而flatMap是输入一个可以转换成多个输出。

9.peek

Stream.of("one", "two", "three", "four")
        .filter(e -> e.length() > 3)
        .peek(e -> System.out.println("Filtered value: " + e))
        .map(String::toUpperCase)
        .peek(e -> System.out.println("Mapped value: " + e))
        .collect(Collectors.toList());

终止操作

1.forEach

//遍历输出集合中的每个元素
peoples.forEach(System.out::println);
peoples.stream().forEach(System.out::println);

peek和forEach的区别是peek属于中间操作,而forEach是终止操作 还有一个最主要的区别是peek可以返回处理结果,forEach不能返回输出结果 2.reduce

//用来进行数据累加
Integer sum = integers.reduce(0, (a, b) -> a+b);
Integer sum = integers.reduce(0, Integer::sum);

3.min

//获取最小值
List<Integer> numList = Arrays.asList(1, 2, 3, 0);
Comparator<Integer> comparator = Comparator.comparing(Integer::intValue);
Optional<Integer> minOptional = numList.stream().min(comparator);
minOptional.ifPresent(e -> System.out.println("Min: " + e));

4.max

//获取最大值
List<Integer> numList = Arrays.asList(1, 2, 3, 0);
Comparator<Integer> comparator = Comparator.comparing(Integer::intValue);
Optional<Integer> minOptional = numList.stream().max(comparator);
minOptional.ifPresent(e -> System.out.println("max: " + e));

5.count

List<Integer> data = Arrays.asList(1, 2, 3, 0);
long count = data.stream().filter(item->item>1).count();
System.out.println(count);

6.anyMatch 判断数据列表中是否存在任意一个元素符合设置的predicate条件,如果是就返回true,否则返回false。

//判断是否有name为张三或age为50的数据
peoples.stream().anyMatch(item->item.getName().equals("zhangsan") || item.getAge().equals("50"));

7.allMatch 判断数据列表中全部元素都符合设置的predicate条件,如果是就返回true,否则返回false,流为空时总是返回true。

List<String> data = Arrays.asList("1", "2", "3");
boolean b = data.stream().allMatch(a -> a.equals("1") || a.equals("2")|| a.equals("3"));
System.out.println(b);

8.noneMatch 判断数据列表中全部元素都不符合设置的predicate条件,如果是就返回true,否则返回false,流为空时总是返回true。

List<String> data = Arrays.asList("1", "2", "3");
boolean b = data.stream().noneMatch(a -> a.equals("4") || a.equals("7"));
System.out.println(b);

9.collect 它可以支持生成如下类型的结果数据:

  • 一个集合类,比如List、Set或者HashMap等,这个是最常用的
  • StringBuilder对象,支持将多个字符串进行拼接处理并输出拼接后结果
  • 一个可以记录个数或者计算总和的对象(数据批量运算统计
// collect成list
List<Integer> data = Arrays.asList(1, 2, 3, 0);
List<Integer> collect = data.stream().filter(item -> item > 0).collect(Collectors.toList());

// collectset
Set<Integer> collect1 = data.stream().filter(item -> item > 0).collect(Collectors.toSet());

// collect成map
Map<String, Person> collect2 = peoples.stream().filter(item -> item.getAge().equals("21")).collect(Collectors.toMap(Person::getName, item -> item));

拼接字符串

List<String> da = Arrays.asList("1","2","2","2","2");
String collect3 = da.stream().collect(Collectors.joining("-"));
System.out.println(collect3);
//也可以这样用
List<String> da = Arrays.asList("1","2","2","2","2");
String collect3 = String.join("-", da);
System.out.println(collect3);

数据批量数学运算

List<Integer> ids = Arrays.asList(10, 20, 30, 40, 50); 
// 计算平均值 
Double average = ids.stream().collect(Collectors.averagingInt(value -> value)); System.out.println("平均值:" + average); 
// 数据统计信息 
IntSummaryStatistics summary = ids.stream().collect(Collectors.summarizingInt(value -> value)); System.out.println("数据统计信息: " + summary);