Java1.8新特性 - Stream流

一、课程大纲

01、Stream流概述
02、Stream流的应用

二、Stream流概述

01、为什么要使用Stream流(白话文)

jdk1.8之前java定义数据结构:数组,集合,map set等,但是对这些数据结构操作的API比较的单一和缺乏

jdk1.8提出Stream的新特性,就是为了去弥补这个缺陷,它参考:javascript,go等借鉴,用java进行实现。
集合的过滤,
集合元素元素的改造
集合元素的去重
多集合中元素的:差集,并集、交集
集合元素的聚合操作(min,max,count,累加)

02、官方解释

概念:Stream 是Java8 提出的一个新概念,不是输入输出的 Stream 流,而是一种用函数式编程方式在集合类上进行复杂操作的工具。简而言之,是以内部迭代的方式处理集合数据的操作,内部迭代可以将更多的控制权交给集合类。Stream 和 Iterator 的功能类似,只是 Iterator 是以外部迭代的形式处理集合数据的操作。

在Java8以前,对集合的操作需要写出处理的过程,如在集合中筛选出满足条件的数据,需要一 一遍历集合中的每个元素,再把每个元素逐一判断是否满足条件,最后将满足条件的元素保存返回。而Stream 对集合筛选的操作提供了一种更为便捷的操作,只需将实现函数接口的筛选条件作为参数传递进来,Stream会自行操作并将合适的元素同样以Stream 的方式返回,最后进行接收即可。

  • 集合和数组在遍历元素的时候十分冗余,受到函数式编程以及流水线思想的启发,我们可以将常见的操作封装成比较简单的方法,比如遍历的时候直接调用一个方法即可,而不用写冗余的循环程序。这就是Stream流对象的由来。
  • Stream是一个接口,有两种方式来进行创建流对象。
    一是调用 Stream.接口中的of方法。
    二是调用集合或者数组中的strain方法来获取 Stream.流对象
  • Stream对象中常用的方法有:遍历元素,筛选元素,跳过元素,截取元素,计数,把流对象拼接
    对流对象中的数据元素进行转换。

三、如何学习Stream

  • Stream体系非常的庞大,思维的学习方式不能够强迫自己立马都掌握,因为每个使用场景都不一样,
  • 学习方案:一定总结,边用边学边积累,把能够掌握的立马掌握。

01、Stream的API

Stream流是java API中的新的成员,它可以让你用声明式的方式处理集合,简单点说,可以看成遍历数据的一个高级点的迭代器,也可以看做一个工厂,数据处理的工厂,当然,Stream流还天然的支持并行操作;也就不用去写复杂的多线程的代码,下面我先来看下Stream的接口定义

反正就是:你需要处理集合,就想一想Stream流

Stream是处理集合的一套高级的API的解决方案!

一下是其中部分方法

public interface Stream<T> extends BaseStream<T, Stream<T>> {
 
	Stream<T> filter(Predicate<? super T> predicate);
 
	<R> Stream<R> map(Function<? super T, ? extends R> mapper);
 
	IntStream mapToInt(ToIntFunction<? super T> mapper);
 
	LongStream mapToLong(ToLongFunction<? super T> mapper);
 
	DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);
 
	<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
 
	IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper);
 
	LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper);
 
	DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper);
 
	Stream<T> distinct();
 
	Stream<T> sorted();
 
	Stream<T> sorted(Comparator<? super T> comparator);
 
	Stream<T> peek(Consumer<? super T> action);
 
	Stream<T> limit(long maxSize);
 
	Stream<T> skip(long n);
 
	void forEach(Consumer<? super T> action);
 
	void forEachOrdered(Consumer<? super T> action);
 
	Object[] toArray();
 
	<A> A[] toArray(IntFunction<A[]> generator);
 
	T reduce(T identity, BinaryOperator<T> accumulator);
 
	Optional<T> reduce(BinaryOperator<T> accumulator);
 
	<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);
 
	<R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);
 
	<R, A> R collect(Collector<? super T, A, R> collector);
 
	Optional<T> min(Comparator<? super T> comparator);
 
	Optional<T> max(Comparator<? super T> comparator);
 
	long count();
 
	boolean anyMatch(Predicate<? super T> predicate);
 
	boolean allMatch(Predicate<? super T> predicate);
 
	boolean noneMatch(Predicate<? super T> predicate);
 
	Optional<T> findFirst();
 
	Optional<T> findAny();
 
	public static <T> Builder<T> builder() {
		return new Streams.streamBuilderImpl<>();
	}
 
	public static <T> Stream<T> empty() {
		return StreamSupport.stream(Spliterators.<T> emptySpliterator(), false);
	}
 
	public static <T> Stream<T> of(T t) {
		return StreamSupport.stream(new Streams.streamBuilderImpl<>(t), false);
	}
 
	@SafeVarargs
	@SuppressWarnings("varargs") // Creating a Stream from an array is safe
	public static <T> Stream<T> of(T... values) {
		return Arrays.stream(values);
	}
 
	public static <T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {
		Objects.requireNonNull(f);
		final Iterator<T> iterator = new Iterator<T>() {
			@SuppressWarnings("unchecked")
			T t = (T) Streams.NONE;
 
			@Override
			public boolean hasNext() {
				return true;
			}
 
			@Override
			public T next() {
				return t = (t == Streams.NONE) ? seed : f.apply(t);
			}
		};
		return StreamSupport.stream(
				Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED | Spliterator.IMMUTABLE), false);
	}
 
	public static <T> Stream<T> generate(Supplier<T> s) {
		Objects.requireNonNull(s);
		return StreamSupport.stream(new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s),
				false);
	}
 
	public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) {
		Objects.requireNonNull(a);
		Objects.requireNonNull(b);
 
		@SuppressWarnings("unchecked")
		Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>((Spliterator<T>) a.spliterator(),
				(Spliterator<T>) b.spliterator());
		Stream<T> Stream = StreamSupport.stream(split, a.isParallel() || b.isParallel());
		return Stream.onClose(Streams.composedClose(a, b));
	}
 
	public interface Builder<T> extends Consumer<T> {
		@Override
		void accept(T t);
 
		default Builder<T> add(T t) {
			accept(t);
			return this;
		}
 
		Stream<T> build();
 
	}
}

通过接口定义,可以看到,抽象方法,有30多个,这些方法一共分为三类,里面还有一些其他的接口;后续,我会慢慢给大家介绍,每个抽象方法的作用,以及用法

首先,我们需要学习如何创建Stream流,就是如何将集合变为Stream流

不同的数据结构创建Stream的步骤是不一样的

02、Stream-流的常用创建方法

前面(《java8 Stream接口简介》),我们已经对Stream这个接口,做了简单的介绍,下面,我们用几个案例,来看看流的几种创建方式

02-01、Collection集合

1.1 使用Collection下的 stream() 和 parallelStream() 方法

List<String> list = new ArrayList<>();
Stream<String> Stream = list.stream(); //获取一个顺序流
Stream<String> parallelStream = list.parallelStream(); //获取一个并行流

02-02、Arrays数组

1.2 使用Arrays 中的 stream() 方法,将数组转成流

Integer[] nums = new Integer[10];
Stream<Integer> Stream = Arrays.stream(nums);

02-03、Stream中的静态方法

1.3 使用Stream中的静态方法:of()、iterate()、generate()

//合并相同数据类型的数据结构
Stream<Integer> Stream = Stream.of(1,2,3,4,5,6);
  
//迭代器
Stream<Integer> Stream2 = Stream.iterate(0, (x) -> x + 2).limit(6);
Stream2.forEach(System.out::println); // 0 2 4 6 8 10
  
//随机数
Stream<Double> Stream3 = Stream.generate(Math::random).limit(2);
Stream3.forEach(System.out::println);

实例:

package com.zhangfushuai;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
 * Description:
 * Author: zhangfushuai
 * Version: 1.0
 * Create Date Time: 2021/12/9 7:37 下午.
 */
public class StreamDemo {
    public static void main(String[] args) {
        List<User> userList1 = new ArrayList<>();
        userList1.add(new User(1, "yykk", "111111", 34, 1, 34600d));
        userList1.add(new User(2, "祈福", "2222222", 24, 0, 883600d));
      
        List<User> userList2 = new ArrayList<>();
        userList2.add(new User(6, "yykk", "111111", 34, 1, 34600d));
        userList2.add(new User(7, "祈福", "2222222", 24, 0, 883600d));

        List<User> collect = Stream.of(userList1, userList2).flatMap(users -> users.stream()).collect(Collectors.toList());
        collect.forEach(System.out::println);

        //-------------------------------------------------------------------------------------
      
        String a[] = {"a", "b", "c"};
        String b[] = {"d", "e", "f"};

        List<String> collect1 = Stream.of(a, b).flatMap(strings -> Arrays.stream(strings).sorted()).collect(Collectors.toList());
        collect1.forEach(System.out::print);

        System.out.println();

        collect1.forEach(s -> {
            System.out.print(s);
        });
        System.out.println();

        collect1.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.print(s);
            }
        });

        System.out.println();

        Stream<Integer> Stream2 = Stream.iterate(0, (x) -> x + 1).limit(4);
        Stream2.forEach(System.out::print);

        System.out.println();

        Stream<Double> Stream3 = Stream.generate(Math::random).limit(3);
        Stream3.forEach(System.out::println);
    }
}

运行结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oqOw8dxI-1658301947714)(asserts/image-20211210132347566.png)]

02-04、BufferedReader读取文本文件

1.4 使用 BufferedReader.lines() 方法,将每行内容转成流

BufferedReader reader = new BufferedReader(new FileReader("F:\\test_Stream.txt"));
Stream<String> lineStream = reader.lines();
lineStream.forEach(System.out::println);

02-05、字符串类型

1.5 使用 Pattern.splitAsStream() 方法,将字符串分隔成流

Pattern pattern = Pattern.compile(",");
Stream<String> stringStream = pattern.splitAsStream("a,b,c,d");
stringStream.forEach(System.out::println);

创建Stream流之后,就要开始进行数据的筛选了

03、Stream两种操作(中间、终止)

  • 中间操作

    intermediate operation 中间操作:中间操作的结果是刻画、描述了一个Stream,并没有产生一个新集合,这种操作也叫做惰性求值方法。

    对应的方法如下:

    这是所有Stream中间操作的列表:
    过滤()==>filter()
    地图()==>map()
    转换()==>flatMap()
    不同()==>distinct() 
    排序()==>sorted()
    窥视()==>peek()
    限制()==>limit()
    跳跃()==>skip()
    叠加()==>reduce()
    
  • 终止操作

    terminal operation 终止操作:最终会从Stream中得到值。说白了:就是可以直接得到结果

    循环()==>foreach()
    总计()==>count()
    收集()==>collect()---放
    andMatch()
    noneMatch()
    allMatch()
    findAny()
    findFirst()
    min()
    max()
    
  • 总结如图

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TKcnMBHF-1658301947716)(04%E3%80%81Java%E5%9F%BA%E7%A1%80_Stream%E6%B5%81.assets/70)]

如何区分这2种操作呢?可以根据操作的返回值类型判断,如果返回值是Stream,则该操作是中间操作,如果返回值是其他值或者为空,则该操作是终止操作。

如果运行终止操作,那么这个流就会被关闭,不能再使用了,如下

Pattern pattern = Pattern.compile(",");
Stream<String> stringStream = pattern.splitAsStream("a,b,c,d");
long count = stringStream.count();
System.out.println(count);

stringStream.forEach(System.out::print);

运行结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EftWw3WO-1658301947716)(asserts/image-20211210133626374.png)]

如下图的前2个操作是中间操作,只有最后一个操作是终止操作。

img

可以形象地理解Stream的操作是对一组粗糙的工艺品原型(即对应的 Stream 数据源)进行加工成颜色统一的工艺品(即最终得到的结果),第一步筛选出合适的原型(即对应Stream的 filter 的方法),第二步将这些筛选出来的原型工艺品上色(对应Stream的map方法),第三步取下这些上好色的工艺品(即对应Stream的 collect(toList())方法)。在取下工艺品之前进行的操作都是中间操作,可以有多个或者0个中间操作,但每个Stream数据源只能有一次终止操作,否则程序会报错。

03-01、准备工作

package com.streamdemo;

/**
 * @author 飞哥
 * @Title: 学相伴出品
 * @Description: 飞哥B站地址:https://space.bilibili.com/490711252
 * 记得关注和三连哦!
 * @Description: 我们有一个学习网站:https://www.kuangstudy.com
 * @date 2021/10/12 14:33
 */
public class User {

    private Integer id;
    private Integer age;
    private Integer sex;
    private Double shenjia;
    private String username;
    private String password;

    public User(Integer id, String username, String password, Integer age, Integer sex, Double shenjia) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.age = age;
        this.sex = sex;
        this.shenjia = shenjia;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Integer getSex() {
        return sex;
    }

    public void setSex(Integer sex) {
        this.sex = sex;
    }

    public Double getShenjia() {
        return shenjia;
    }

    public void setShenjia(Double shenjia) {
        this.shenjia = shenjia;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", age=" + age +
                ", sex=" + sex +
                ", shenjia=" + shenjia +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
package com.streamdemo;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 飞哥
 * @Title: 学相伴出品
 * @Description: 飞哥B站地址:https://space.bilibili.com/490711252
 * 记得关注和三连哦!
 * @Description: 我们有一个学习网站:https://www.kuangstudy.com
 * @date 2021/10/12 14:41
 */
public class StreamDemo {

    public static void main(String[] args) {
        List<User> userList = new ArrayList<>();
        userList.add(new User(1, "yykk", "111111", 34, 1, 34600d));
        userList.add(new User(2, "祈福", "2222222", 24, 0, 883600d));
        userList.add(new User(3, "小王", "3333333", 24, 1, 734090d));
        userList.add(new User(4, "小楠", "4444444", 14, 0, 33400d));
        userList.add(new User(5, "小张", "55555", 29, 1, 140000d));

    }
}

04、Stream的中间操作

04-01、筛选与切片

把条件满足的,过滤匹配出来。

filter:过滤流中的某些元素
limit(n):获取n个元素
skip(n):跳过n元素,配合limit(n)可实现分页
distinct:通过流中元素的 hashCode()equals() 去除重复元素
Stream<Integer> Stream = Stream.of(6, 4, 6, 7, 3, 9, 8, 10, 12, 14, 14);
  
Stream<Integer> newStream = Stream.filter(s -> s > 5) //6 6 7 9 8 10 12 14 14
.distinct() //6 7 9 8 10 12 14
.skip(2) //9 8 10 12 14
.limit(2); //9 8
newStream.forEach(System.out::println);
04-01-01、分析:filter语法
Stream<T> filter(Predicate<? super T> predicate);

这个方法,传入一个Predicate的函数接口,关于Predicate函数接口定义,可以查看《JAVA8 Predicate接口》,这个接口传入一个泛型参数T,做完操作之后,返回一个boolean值;filter方法的作用,是对这个boolean做判断,返回true判断之后的对象,下面一个案例,可以看到怎么使用

04-01-02、实操:筛选案例
package com.streamdemo;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 飞哥
 * @Title: 学相伴出品
 * @Description: 飞哥B站地址:https://space.bilibili.com/490711252
 * 记得关注和三连哦!
 * @Description: 我们有一个学习网站:https://www.kuangstudy.com
 * @date 2021/10/12 14:41
 */
public class StreamDemo {

    public static void main(String[] args) {
        List<User> userList = new ArrayList<>();
        userList.add(new User(1, "yykk", "111111", 34, 1, 34600d));
        userList.add(new User(2, "祈福", "2222222", 24, 0, 883600d));
        userList.add(new User(3, "小王", "3333333", 24, 1, 734090d));
        userList.add(new User(4, "小楠", "4444444", 14, 0, 33400d));
        userList.add(new User(5, "小张", "55555", 29, 1, 140000d));

        // 1: filter过滤
        List<User> collect = userList.stream().filter(res -> res.getSex()==1).collect(Collectors.toList());
      
       //本来应该是: List<User> collect = userList.stream().filter(User res -> res.getSex()==1).collect(Collectors.toList());
      //但是它会根据流中的数据,自动去判断数据类型,所以可以省略
        collect.forEach(System.out::println);

    }
}

也可以不使用箭头表达式,如下操作也可以实现过滤

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BbqEmSh6-1658301947716)(asserts/image-20211213152739237.png)]运行结果

User{id=1, age=34, sex=1, shenjia=34600.0, username='yykk', password='111111'}
User{id=3, age=24, sex=1, shenjia=734090.0, username='小王', password='3333333'}
User{id=5, age=29, sex=1, shenjia=140000.0, username='小张', password='55555'

这个箭头函数和js中的差不多一样,可以参考着理解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MOJQPj5c-1658301947716)(asserts/image-20211213153943842.png)]

04-02、映射方法

04-02-01、map和flatMap的举例

map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

List<String> list = Arrays.asList("a,b,c", "1,2,3");
  
//将每个元素转成一个新的且不带逗号的元素
Stream<String> s1 = list.stream().map(s -> s.replaceAll(",", ""));
s1.forEach(System.out::println); // abc 123
  
Stream<String> s3 = list.stream().flatMap(s -> {
//将每个元素转换成一个Stream
String[] split = s.split(",");
Stream<String> s2 = Arrays.stream(split);
return s2;
});
s3.forEach(System.out::println); // a b c 1 2 3
04-02-02、map方法语法
<R> Stream<R> map(Function<? super T, ? extends R> mapper);

这个方法传入一个Function的函数式接口,接口定义可以查看《JAVA8 Function接口》,这个接口,接收一个泛型T,返回泛型R,map函数的定义,返回的流,表示的泛型是R对象,这个表示,调用这个函数后,可以改变返回的类型,先看下面的案例

04-02-03、map案例
package com.streamdemo;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * @author 飞哥
 * @Title: 学相伴出品
 * @Description: 飞哥B站地址:https://space.bilibili.com/490711252
 * 记得关注和三连哦!
 * @Description: 我们有一个学习网站:https://www.kuangstudy.com
 * @date 2021/10/12 14:41
 */
public class StreamDemo {

    public static void main(String[] args) {
        List<User> userList = new ArrayList<>();
        userList.add(new User(1, "yykk", "111111", 34, 1, 34600d));
        userList.add(new User(2, "祈福", "2222222", 24, 0, 883600d));
        userList.add(new User(3, "小王", "3333333", 24, 1, 734090d));
        userList.add(new User(4, "小楠", "4444444", 14, 0, 33400d));
        userList.add(new User(5, "小张", "55555", 29, 1, 140000d));

        // 1: 使用map获取集合中username列
        List<String> userNameList = userList.stream().map(res -> res.getUsername()).collect(Collectors.toList());
        userNameList.forEach(System.out::println);

        // 2: 使用map获取集合中的id,username,age,sex,shenjia,排除password
        List<Map<String, Object>> collect = userList.stream().map(user -> {
            Map<String, Object> map = new HashMap<>();
            map.put("id",user.getId());
            map.put("age",user.getAge());
            map.put("sex",user.getSex());
            map.put("shenjia",user.getShenjia());
            return map;
        }).collect(Collectors.toList());
        collect.forEach(System.out::println);
        
        /*
         // 4: 使用map + min/max/count/sum快速求所有用户
        List<Map<String, Object>> mapList = userList.stream().map(new Function<User, Map<String, Object>>() {
            @Override
            public Map<String, Object> apply(User user) {
                Map<String, Object> map = new HashMap<>();
                map.put("id", user.getId());
                map.put("age", user.getAge());
                map.put("sex", user.getSex());
                map.put("shenjia", user.getShenjia());
                return map;
            }
        }).collect(Collectors.toList());
        mapList.forEach(System.out::println);
        */
        

        // 3: 快速清空password
        List<User> userList1 = userList.stream().map(user -> {
            user.setPassword("");
            return user;
        }).collect(Collectors.toList());
        userList1.forEach(System.out::println);

        // 4: 使用map + min/max/count/sum快速求所有用户
        Integer maxage = userList.stream().map(user -> user.getAge()).max((a,b)->a-b).get();
        Integer minage = userList.stream().map(user -> user.getAge()).min((a,b)->a-b).get();
        long count = userList.stream().map(user -> user.getAge()).count();
        long sumcount1 = userList.stream().map(user -> user.getAge()).reduce((a,b)->a+b).get();
        long sumcount2 = userList.stream().mapToInt(user -> user.getAge()).sum();
        System.out.println(maxage);
        System.out.println(minage);
        System.out.println(count);
        System.out.println(sumcount1);
        System.out.println(sumcount2);
        System.out.println(sumcount1/count);
    }
}

运行结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gPXufd60-1658301947717)(asserts/image-20211213114121054.png)]

可以看到,我们把Integer,变成了String输出,把map对象里的name字符串,单独输出;现在,我们只看到了一个forEach的终端操作,后面,我们会看到,更多的终端操作,把map操作后,改变的对象类型,返回各种类型的集合,或者对数字类型的,返回求和,最大,最小等的操作;

04-02-04、flatMap方法语法

参考:https://www.cnblogs.com/xfyy-2020/p/13289066.html

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

这个接口,跟map一样,接收一个Fucntion的函数式接口,不同的是,Function接收的泛型参数,第二个参数是一个Stream流;方法,返回的也是泛型R,具体的作用是把两个流,变成一个流返回,下面,我们看一个案例,来详细解答,怎么把两个流的内容,变成一个流的内容

package com.streamdemo;

import java.util.Objects;

/**
 * @author 飞哥
 * @Title: 学相伴出品
 * @Description: 飞哥B站地址:https://space.bilibili.com/490711252
 * 记得关注和三连哦!
 * @Description: 我们有一个学习网站:https://www.kuangstudy.com
 * @date 2021/10/12 14:33
 */
public class User {

    private Integer id;
    private Integer age;
    private Integer sex;
    private Double shenjia;
    private String username;
    private String password;

    public User(Integer id, String username, String password, Integer age, Integer sex, Double shenjia) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.age = age;
        this.sex = sex;
        this.shenjia = shenjia;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Integer getSex() {
        return sex;
    }

    public void setSex(Integer sex) {
        this.sex = sex;
    }

    public Double getShenjia() {
        return shenjia;
    }

    public void setShenjia(Double shenjia) {
        this.shenjia = shenjia;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", age=" + age +
                ", sex=" + sex +
                ", shenjia=" + shenjia +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(id, user.id) &&
                Objects.equals(age, user.age) &&
                Objects.equals(sex, user.sex) &&
                Objects.equals(shenjia, user.shenjia) &&
                Objects.equals(username, user.username) &&
                Objects.equals(password, user.password);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, age, sex, shenjia, username, password);
    }
}

04-02-05、flatMap案例
package com.streamdemo;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.stream;

/**
 * @author 飞哥
 * @Title: 学相伴出品
 * @Description: 飞哥B站地址:https://space.bilibili.com/490711252
 * 记得关注和三连哦!
 * @Description: 我们有一个学习网站:https://www.kuangstudy.com
 * @date 2021/10/12 14:41
 */
public class StreamDemo2 {

    public static void main(String[] args) {
        List<User> userList = new ArrayList<>();
        userList.add(new User(1, "yykk", "111111", 34, 1, 34600d));
        userList.add(new User(2, "祈福", "2222222", 24, 0, 883600d));
        userList.add(new User(3, "小王", "3333333", 24, 1, 734090d));
        userList.add(new User(4, "小楠", "4444444", 14, 0, 33400d));
        userList.add(new User(5, "小张", "55555", 29, 1, 140000d));

        List<User> userList2 = new ArrayList<>();
        userList2.add(new User(1, "yykk", "111111", 34, 1, 34600d));
        userList2.add(new User(7, "祈福", "2222222", 24, 0, 883600d));
        userList2.add(new User(8, "小王", "3333333", 24, 1, 734090d));
        userList2.add(new User(9, "小楠", "4444444", 14, 0, 33400d));
        userList2.add(new User(10, "小张", "55555", 29, 1, 140000d));


        //flatmap一般用于:
        // 使用Java8实现集合的并、交、差操作
        // 具体的作用是把两个流,变成一个流返回

        //案例一:合并两个集合
        System.out.println("============案例一 合并==============");
        List<User> collect = userList.stream().flatMap(user->userList2.stream()).collect(Collectors.toList());
        collect.forEach(System.out::println);

        System.out.println("============案例二取交集==============");
        // 案例二取交集
        List<User> userList1 = userList.stream().filter(userList2::contains).collect(Collectors.toList());
        userList1.forEach(System.out::println);

        System.out.println("===========案例三取并集===============");
        // 案例三取并集
        List<User> userList3= Stream.of(userList,userList2).flatMap(Collection::Stream).distinct().collect(Collectors.toList());
        userList3.forEach(System.out::println);

        System.out.println("=========== 案例四取差集===============");
        // 案例四 取差集
        List<User> userList4= userList.stream().filter(user -> !userList2.contains(user)).collect(Collectors.toList());
        userList4.forEach(System.out::println);

        int sum = Stream.of(userList,userList2).flatMapToInt(user -> user.stream().mapToInt(u->u.getAge())).sum();
        System.out.println(sum);

    }
}

运行结果


04-02-06、排序

语法

sorted():自然排序,流中元素需实现Comparable接口
sorted(Comparator com):定制排序,自定义Comparator排序器

案例

List<String> list = Arrays.asList("aa", "ff", "dd");
//String 类自身已实现Compareable接口
list.stream().sorted().forEach(System.out::println);// aa dd ff
  
Student s1 = new Student("aa", 10);
Student s2 = new Student("bb", 20);
Student s3 = new Student("aa", 30);
Student s4 = new Student("dd", 40);
List<Student> studentList = Arrays.asList(s1, s2, s3, s4);
  
//自定义排序:先按姓名升序,姓名相同则按年龄升序
studentList.stream().sorted(
(o1, o2) -> {
if (o1.getName().equals(o2.getName())) {
return o1.getAge() - o2.getAge();
} else {
return o1.getName().compareTo(o2.getName());
}
}
).forEach(System.out::println); 

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wC6NhanP-1658301947717)(asserts/image-20211213155137109.png)]

04-02-07、 peek

peek:如同于map,能得到流中的每一个元素。但map接收的是一个Function表达式,有返回值;而peek接收的是Consumer表达式,没有返回值。

案例

Student s1 = new Student("aa", 10);
Student s2 = new Student("bb", 20);
List<Student> studentList = Arrays.asList(s1, s2);
  
studentList.stream()
.peek(o -> o.setAge(100))
.forEach(System.out::println);
  
//结果:
Student{name='aa', age=100}
Student{name='bb', age=100}
04-02-08、 其他的中间操作-改造变换(重要)

下面,我们来看其他的剩余的一些中间操作,各自的作用,我也通过注释,做了解析,方法定义如下;

语法

//去重复
Stream<T> distinct();
//排序
Stream<T> sorted();
//根据属性排序
Stream<T> sorted(Comparator<? super T> comparator);
//对对象的进行操作
Stream<T> peek(Consumer<? super T> action);
//截断--取先maxSize个对象
Stream<T> limit(long maxSize);
//截断--忽略前N个对象
Stream<T> skip(long n);

下面,我们用一些案例,对这些操作,做一些综合的演示

案例

package com.taihao;
 
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.stream;
 
public class TestJava8 {
	public static List<Emp> list = new ArrayList<>();
	static {
		list.add(new Emp("xiaoHong1", 20, 1000.0));
		list.add(new Emp("xiaoHong2", 25, 2000.0));
		list.add(new Emp("xiaoHong3", 30, 3000.0));
		list.add(new Emp("xiaoHong4", 35, 4000.0));
		list.add(new Emp("xiaoHong5", 38, 5000.0));
		list.add(new Emp("xiaoHong6", 45, 9000.0));
		list.add(new Emp("xiaoHong7", 55, 10000.0));
		list.add(new Emp("xiaoHong8", 42, 15000.0));
	}
 
	public static void println(Stream<Emp> Stream) {
		Stream.forEach(emp -> {
			System.out.println(String.format("名字:%s,年纪:%s,薪水:%s", emp.getName(), emp.getAge(), emp.getSalary()));
		});
	}
 
	public static void main(String[] args) {
		// 对数组流,先过滤重复,在排序,再控制台输出 1,2,3
		Arrays.asList(3, 1, 2, 1).stream().distinct().sorted().forEach(str -> {
			System.out.println(str);
		});
		// 对list里的emp对象,取出薪水,并对薪水进行排序,然后输出薪水的内容,map操作,改变了Strenm的泛型对象
		list.stream().map(emp -> emp.getSalary()).sorted().forEach(salary -> {
			System.out.println(salary);
		});
		// 根据emp的属性name,进行排序
		println(list.stream().sorted(Comparator.comparing(Emp::getName)));
 
		// 给年纪大于30岁的人,薪水提升1.5倍,并输出结果
		Stream<Emp> Stream = list.stream().filter(emp -> {
			return emp.getAge() > 30;
		}).peek(emp -> {
			emp.setSalary(emp.getSalary() * 1.5);
		});
		println(Stream);
		// 数字从1开始迭代(无限流),下一个数字,是上个数字+1,忽略前5个 ,并且只取10个数字
		// 原本1-无限,忽略前5个,就是1-5数字,不要,从6开始,截取10个,就是6-15
		Stream.iterate(1, x -> ++x).skip(5).limit(10).forEach(System.out::println);
	}
 
	public static class Emp {
		private String name;
 
		private Integer age;
 
		private Double salary;
 
		public Emp(String name, Integer age, Double salary) {
			super();
			this.name = name;
			this.age = age;
			this.salary = salary;
		}
 
		public String getName() {
			return name;
		}
 
		public void setName(String name) {
			this.name = name;
		}
 
		public Integer getAge() {
			return age;
		}
 
		public void setAge(Integer age) {
			this.age = age;
		}
 
		public Double getSalary() {
			return salary;
		}
 
		public void setSalary(Double salary) {
			this.salary = salary;
		}
 
	}
}

运行结果


每个例子,也都加了注释,大家看例子,自己get吧

05、Stream 流的终止操作

01、匹配、聚合操作

把条件满足的,过滤匹配出来。

语法

allMatch:接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回true,否则返回false
noneMatch:接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回true,否则返回false
anyMatch:接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回true,否则返回false
findFirst:返回流中第一个元素
findAny:返回流中的任意元素
count:返回流中元素的总个数
max:返回流中元素最大值
min:返回流中元素最小值

案例

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
  
boolean allMatch = list.stream().allMatch(e -> e > 10); //false
boolean noneMatch = list.stream().noneMatch(e -> e > 10); //true
boolean anyMatch = list.stream().anyMatch(e -> e > 4); //true
  
Integer findFirst = list.stream().findFirst().get(); //1
Integer findAny = list.stream().findAny().get(); //1
  
long count = list.stream().count(); //5
Integer max = list.stream().max(Integer::compareTo).get(); //5
Integer min = list.stream().min(Integer::compareTo).get(); //1  

运行结果


02、规约操作

Optional reduce(BinaryOperator accumulator):第一次执行时,accumulator函数的第一个参数为流中的第一个元素,第二个参数为流中元素的第二个元素;第二次执行时,第一个参数为第一次函数执行的结果,第二个参数为流中的第三个元素;依次类推。
  T reduce(T identity, BinaryOperator accumulator):流程跟上面一样,只是第一次执行时,accumulator函数的第一个参数为identity,而第二个参数为流中的第一个元素。
  U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator combiner):在串行流(Stream)中,该方法跟第二个方法一样,即第三个参数combiner不会起作用。在并行流(parallelStream)中,我们知道流被fork join出多个线程进行执行,此时每个线程的执行流程就跟第二个方法reduce(identity,accumulator)一样,而第三个参数combiner函数,则是将每个线程的执行结果当成一个新的流,然后使用第一个方法reduce(accumulator)流程进行规约。

案例
//经过测试,当元素个数小于24时,并行时线程数等于元素个数,当大于等于24时,并行时线程数为16
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24);
  
Integer v = list.stream().reduce((x1, x2) -> x1 + x2).get();
System.out.println(v); // 300
  
Integer v1 = list.stream().reduce(10, (x1, x2) -> x1 + x2);
System.out.println(v1); //310
  
Integer v2 = list.stream().reduce(0,
(x1, x2) -> {
System.out.println("Stream accumulator: x1:" + x1 + " x2:" + x2);
return x1 - x2;
},
(x1, x2) -> {
System.out.println("Stream combiner: x1:" + x1 + " x2:" + x2);
return x1 * x2;
});
System.out.println(v2); // -300
  
Integer v3 = list.parallelStream().reduce(0,
(x1, x2) -> {
System.out.println("parallelStream accumulator: x1:" + x1 + " x2:" + x2);
return x1 - x2;
},
(x1, x2) -> {
System.out.println("parallelStream combiner: x1:" + x1 + " x2:" + x2);
return x1 * x2;
});
System.out.println(v3); //197474048

运行结果


03、收集操作

collect:接收一个Collector实例,将流中元素收集成另外一个数据结构。
Collector<T, A, R> 是一个接口,有以下5个抽象方法:
Supplier<A> supplier():创建一个结果容器A
BiConsumer<A, T> accumulator():消费型接口,第一个参数为容器A,第二个参数为流中元素T。
BinaryOperator<A> combiner():函数接口,该参数的作用跟上一个方法(reduce)中的combiner参数一样,将并行流中各 个子进程的运行结果(accumulator函数操作后的容器A)进行合并。
Function<A, R> finisher():函数式接口,参数为:容器A,返回类型为:collect方法最终想要的结果R。
Set<Characteristics> characteristics():返回一个不可变的Set集合,用来表明该Collector的特征。有以下三个特征:
CONCURRENT:表示此收集器支持并发。(官方文档还有其他描述,暂时没去探索,故不作过多翻译)
UNORDERED:表示该收集操作不会保留流中元素原有的顺序。
IDENTITY_FINISH:表示finisher参数只是标识而已,可忽略。

3.3.1 Collector 工具库:Collectors

Student s1 = new Student("aa", 10,1);
Student s2 = new Student("bb", 20,2);
Student s3 = new Student("cc", 10,3);
List<Student> list = Arrays.asList(s1, s2, s3);
  
//装成list
List<Integer> ageList = list.stream().map(Student::getAge).collect(Collectors.toList()); // [10, 20, 10]
  
//转成set
Set<Integer> ageSet = list.stream().map(Student::getAge).collect(Collectors.toSet()); // [20, 10]
  
//转成map,注:key不能相同,否则报错
Map<String, Integer> studentMap = list.stream().collect(Collectors.toMap(Student::getName, Student::getAge)); // {cc=10, bb=20, aa=10}
  
//字符串分隔符连接
String joinName = list.stream().map(Student::getName).collect(Collectors.joining(",", "(", ")")); // (aa,bb,cc)
  
//聚合操作
//1.学生总数
Long count = list.stream().collect(Collectors.counting()); // 3
//2.最大年龄 (最小的minBy同理)
Integer maxAge = list.stream().map(Student::getAge).collect(Collectors.maxBy(Integer::compare)).get(); // 20
//3.所有人的年龄
Integer sumAge = list.stream().collect(Collectors.summingInt(Student::getAge)); // 40
//4.平均年龄
Double averageAge = list.stream().collect(Collectors.averagingDouble(Student::getAge)); // 13.333333333333334
// 带上以上所有方法
DoubleSummaryStatistics statistics = list.stream().collect(Collectors.summarizingDouble(Student::getAge));
System.out.println("count:" + statistics.getCount() + ",max:" + statistics.getMax() + ",sum:" + statistics.getSum() + ",average:" + statistics.getAverage());
  
//分组
Map<Integer, List<Student>> ageMap = list.stream().collect(Collectors.groupingBy(Student::getAge));
//多重分组,先根据类型分再根据年龄分
Map<Integer, Map<Integer, List<Student>>> typeAgeMap = list.stream().collect(Collectors.groupingBy(Student::getType, Collectors.groupingBy(Student::getAge)));
  
//分区
//分成两部分,一部分大于10岁,一部分小于等于10岁
Map<Boolean, List<Student>> partMap = list.stream().collect(Collectors.partitioningBy(v -> v.getAge() > 10));
  
//规约
Integer allAge = list.stream().map(Student::getAge).collect(Collectors.reducing(Integer::sum)).get(); //40  

3.3.2 Collectors.toList() 解析

//toList 源码
public static <T> Collector<T, ?, List<T>> toList() {
return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
(left, right) -> {
left.addAll(right);
return left;
}, CH_ID);
}
  
//为了更好地理解,我们转化一下源码中的lambda表达式
public <T> Collector<T, ?, List<T>> toList() {
Supplier<List<T>> supplier = () -> new ArrayList();
BiConsumer<List<T>, T> accumulator = (list, t) -> list.add(t);
BinaryOperator<List<T>> combiner = (list1, list2) -> {
list1.addAll(list2);
return list1;
};
Function<List<T>, List<T>> finisher = (list) -> list;
Set<Collector.Characteristics> characteristics = Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));
  
return new Collector<T, List<T>, List<T>>() {
@Override
public Supplier supplier() {
return supplier;
}
  
@Override
public BiConsumer accumulator() {
return accumulator;
}
  
@Override
public BinaryOperator combiner() {
return combiner;
}
  
@Override
public Function finisher() {
return finisher;
}
  
@Override
public Set<Characteristics> characteristics() {
return characteristics;
}
};
}

参考文献

参考网址:

https://blog.youkuaiyun.com/qq_28410283/article/details/80642786

https://www.cnblogs.com/owenma/p/12207330.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小张develop

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

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

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

打赏作者

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

抵扣说明:

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

余额充值