java基础之Stream

本文深入探讨了Java8中的Stream API,包括流的概述、创建方式、中间操作和终止操作。通过实例展示了如何利用Stream进行数据处理,如筛选、映射、排序和聚合。此外,还介绍了reduce方法及其在并行流中的应用,以及函数式接口在Stream API中的作用。
摘要由CSDN通过智能技术生成

Java基础之stream

1 流的概述

Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream 接口API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

stream对象的操作有两种,一种是中间操作,得到的仍是stream;一种是结束操作,可得到基本类型或对象类型。又可细分为以下几种stream对象的操作主要有以下几种

无状态:指元素的处理不受之前元素的影响;

有状态:指该操作只有拿到所有元素之后才能继续下去。

非短路操作:指必须处理所有元素才能得到最终结果;

短路操作:指遇到某些符合条件的元素就可以得到最终结果,如 A || B,只要A为true,则无需判断B的结果。

在这里插入图片描述

2 创建stream

2.1 使用Collection下的 stream() 和 parallelStream() 方法(集合)

List<String> list = new ArrayList<>();

Stream<String> stream = list.stream(); //获取一个顺序流

Stream<String> parallelStream = list.parallelStream(); //获取一个并行流

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

Integer[] nums = new Integer[10];

Stream<Integer> stream = Arrays.stream(nums);

2.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);

2.4 使用 BufferedReader.lines() 方法,将每行内容转成流(文件IO)

BufferedReader reader = new BufferedReader(new FileReader("F:\\test_stream.txt"));

Stream<String> lineStream = reader.lines();

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

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

Pattern pattern = Pattern.compile(",");

Stream<String> stringStream = pattern.splitAsStream("a,b,c,d");

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

3 stream的中间操作

中间操作完成后仍得到stream对象。按分类主要有以下几种

3.1 筛选与切片

​ filter:过滤流中的某些元素

​ limit(n):获取n个元素

​ skip(n):跳过n元素,配合limit(n)可实现分页

​ distinct():通过流中元素的 hashCode() 和 equals() 去除重复元素

​ concat(Stream a, Stream b): 合并两个Stream

从名字可以看出上面这几个操作类似于mysql的select语句的字句,比如where/limit/distinct。

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);

3.2 映射

​ 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

3.3 排序

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);

3.4 消费
peek:得到一个与当前流元素相同的流,而peek接收的是Consumer表达式(接收T对象,不返回值),对流中每个元素执行操作。

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); 

4 流的终止操作

4.1 聚合操作

这些操作类似于sql中的聚合函数,其实它们都可以用下文的reduce方法来实现。

​ allMatch:接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回true,否则返回false

​ noneMatch:接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回true,否则返回false

​ anyMatch:接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回true,否则返回false

​ findFirst:返回流中第一个元素

​ findAny:返回流中的任意元素

​ count:返回流中元素的总个数

​ max:返回流中元素最大值

​ min:返回流中元素最小值

// 判断文件名是否已存在
boolean isDuplicate = list.stream()
               .anyMatch(file->MyStringUtil.isEqual(fileName,file.getFileName()));

4.2 约简操作

约简是一种终结操作,它们会将流约简为可以在程序中使用的非流值。上文的max/min实际也是约简操作。

4.2.1 函数式接口

说到reduce方法,不得不提函数式接口,因为reduce接收函数式接口作为参数(实际上是接口的实现类作为参数)。java8有如下常用的函数式接口

nametypedescription
ConsumerConsumer< T >接收T对象,不返回值
PredicatePredicate< T >接收T对象并返回boolean
FunctionFunction< T, R >接收T对象,返回R对象
SupplierSupplier< T >提供T对象(例如工厂),不接收值
UnaryOperatorUnaryOperator接收T对象,返回T对象
BinaryOperatorBinaryOperator接收两个T对象,返回T对象

description中接收对象,返回值实际上说的是接口中的方法,比如下面几个接口

BiFunction

它是一个函数式接口,包含的函数式方法定义如下:

@FunctionalInterface
public interface BiFunction<T, U, R> {
  R apply(T t, U u);
}

BiFunction与Function不同点在于它接收两个输入返回一个输出; 而Function接收一个输入返回一个输出。注意它的两个输入、一个输出的类型可以是不一样的。

BinaryOperator

它实际上就是继承自BiFunction的一个接口,它的apply方法中3个泛型参数必须是同种类型。

public interface BinaryOperator<T> extends BiFunction<T,T,T>

4.2.2 reduce详细

Reduce中文含义为:减少、缩小;而Stream中的reduce方法干的正是这样的活:根据一定的规则将Stream中的元素进行计算后返回一个唯一的值。它有三个变种,输入参数分别是一个参数、二个参数以及三个参数;

Optional reduce(BinaryOperator accumulator):第一次执行时,accumulator函数的第一个参数为流中的第一个元素,第二个参数为流中元素的第二个元素;第二次执行时,第一个参数为第一次函数执行的结果,第二个参数为流中的第三个元素;依次类推。

// 求和
Integer sum = s.reduce(new BinaryOperator<Integer>() {
    @Override
    public Integer apply(Integer integer, Integer integer2) {
        return integer + integer2;
    }
}).get();

T reduce(T identity, BinaryOperator accumulator):流程跟上面一样,只是第一次执行时,accumulator函数的第一个参数为identity,而第二个参数为流中的第一个元素。

如要将一个String类型的Stream中的所有元素连接到一起并在最前面添加[value]后返回

 Stream<String> ss = Stream.of("test", "t1", "t2", "teeeee", "aaaa", "taaa");
/**
 * 也可以使用Lambda语法:
 * System.out.println(s.reduce("[value]", (s1, s2) -> s1.concat(s2)));
 */
        System.out.println(ss.reduce("[value]", new BinaryOperator<String>() {
            @Override
            public String apply(String s, String s2) {
                return s.concat(s2);
            }
        }));

U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator combiner):其中BiFunction的返回类型是U,在串行流(stream)中,该方法跟第二个方法一样,因为第三个参数combiner不会起作用。

在并行流(parallelStream)中,我们知道流被fork join出多个线程进行执行,此时每个线程的执行流程就跟第二个方法reduce(identity,accumulator)一样。而第三个参数combiner,则是将每个线程的执行结果当成一个新的流,进行汇总操作。

第三个参数combiner主要是使用在并行计算的场景下;如果Stream是非并行时,第三个参数实际上是不生效的。

非并行

/**
 * 以下reduce生成的List将会是[aa, ab, c, ad]
 * Lambda语法:
 *  System.out.println(s1.reduce(new ArrayList<String>(), (r, t) -> {r.add(t); return r; }, (r1, r2) -> r1));
 */

Stream<String> s1 = Stream.of("aa", "ab", "c", "ad");
System.out.println(s1.reduce(new ArrayList<String>(),

new BiFunction<ArrayList<String>, String, ArrayList<String>>() {
    @Override
    public ArrayList<String> apply(ArrayList<String> u, String s) {
	    u.add(s);
	    return u;
	}
}, new BinaryOperator<ArrayList<String>>() {

		@Override
		public ArrayList<String> apply(ArrayList<String> strings, ArrayList<String> strings2) {
			return strings;
			}
		}));
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值