Java流

Stream流

  • Stream流是一个来自数据源的元素队列并具有聚合操作的
  • Stream流包含了如下三个部分
  • 元素序列:一个流对外提供一个接口,可以访问到一串特定的数据。流本身是不存储元素的,但可以根据需要进行计算转化
  • 数据源:数据来源,如数据结构,数组,文件等
  • 聚合操作:流支持像SQL操作或者其他函数式语言的操作,如filter / map / reduce / find / match / sorted等
public static void main(String[] args) {
        String[] planets = {
                "Mercury", "Venus", "Earth", "Mars",
                "Jupiter", "Saturn", "Uranus", "Neptune"
        };
        // 数组转化成List
        List<String> list = Arrays.asList(planets);
        // 采用流的方式里处理数组中长度大于5的元素个数
        Long count = list.stream().filter((planet) -> planet.length() > 5).count();
        System.out.println("统计结果: " + count);
        // 采用并行流
        Long countParall = list.parallelStream().filter((planet) -> planet.length() > 5).count();
        System.out.println("并行流处理结果: " + countParall);
    }
Stream流的特点
  • 很多流操作也是返回一个流。
  • 流操作进行迭代,用户感知不到循环遍历。

流的工作流程

  • 创建一个流
  • 指定将流转化为其他流的中间操作,可包括多个步骤(惰性操作)
  • 应用终止操作,从而产生结果。这个操作会强制执行之前的惰性操作。这个步骤以后,流就再也不用了
  • 注意:一个流,一次只能一个用途,不能多个用途,用了不能再用了

Stream流的创建

使用Collection接口的stream方法
  • 还有其他的子类,如LinkedList , LinkedSet , TreeSet , Vector等
public static void main(String[] args) throws IOException {
    Stream<String> as = new ArrayList<String>().stream();
    Stream<String> hs = new HashSet<String>().stream();
    Stream<String> b1 = Arrays.stream("a,b,c,e".split(","), 3, 5);
}
使用Stream类进行转化
  • of方法,直接将数组转化
Stream<Integer> c1 = Stream.of(new Integer[5]);
Stream<String> c2 = Stream.of("a,b,c".split(","));
Stream<String> c3 = Stream.of("a", "b", "c");
  • empty方法,产生一个空流
Stream<String> d1 = Stream.empty();
使用generate方法
  • 接收一个Lambda表达式或者一个方法引用
Stream<String> e1 = Stream.generate(() -> "hello");
Stream<Double> e2 = Stream.generate(Math::random);
使用iterrate方法
Stream<BigInteger> e3 = Stream.iterate(BigInteger.ZERO, n -> n.add(BigInteger.ONE));
基本类型流 (只有三种)
  • IntStream, LongStream, DoubleStream
IntStream s1 = IntStream.of(1, 2, 3, 4);
IntStream s2 = Arrays.stream(new int[] {1, 2, 3, 4});
IntStream s3 = IntStream.generate(() -> (int) (Math.random() * 100));
// range(startNumber, length); 默认步长为1,不包括结束
IntStream s4 = IntStream.range(1, 5);
// 包括结束
IntStream s5 = IntStream.rangeClosed(1, 5);

// Stream->基本对象流
IntStream s6 = IntStream.of(1, 2, 3);
Stream<Integer> s7 = s6.boxed();
IntStream s8 = s7.mapToInt(Integer::intValue);
并行流
  • 使得所有中间转换操作都将被并行化
  • Collections.parallelStream()将任何集合转为并行流
  • Stream.parallel()方法,产生一个并行流
IntStream s1 = IntStream.range(1, 10);
long evenNum = s1.paralled().filter(n -> n % 2 ==0).count();

注意:需要保证传给并行流的操作不存在竞争

Files.lines方法创建流
Stream<String> contents = Files.lines(Paths.get("C:/hello.txt"));
Patten的splitAsStream方法创建流
Stream<String> works = Pattern.compile(",").splitAsStream("a,b,c");

Stream流的转换

  • Stream流的转化是从一种流到另外一种流
  • 过滤(去重)、排序、转化、抽取 / 跳过 / 连接
过滤filter
  • 接收一个Lambda表达式,对每个元素进行判定,符合条件的留下
Stream<Integer> s1 = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> s2 = s1.filter(n -> n > 2);
s2.forEach(System.out::println); // 结果为3 4 5
去重distinct
  • 对流的元素进行过滤、去除重复,只留下不重复元素
  • 对象的判定,先调用hashCode方法,再调用equals方法
Stream<Integer> s1 = Stream.of(1, 2, 3, 3, 2, 1);
Stream<Integer> s2 = s1.distinct();
s2.forEach(System.out::println); // 结果为1 2 3
ArrayList<Student> students = new ArrayList<Student>();
students.add(new Student("Tom", 20));
students.add(new Student("Tom", 20));
students.add(new Student("Jerry", 20));
students.add(new Student("Jerry", 18));

// 先对象的hasCode再调用equals方法进行判断
// hashCode和equals则判定为两个对象相同
Stream<Student> s3 = students.stream().distinct();
s3.forEach(System.out::println);
排序sorted
  • 对流的基本类型包装类元素进行排序
  • 提供Comparator,对流的元素进行排序
  • 对流的自定义对象元素进行排序,调用对象的compareTo方法
Stream<Integer> s1 = Stream.of(3, 2, 4, 1, 5);
// 默认升序
Stream<Integer> s2 = s1.sorted();
s2.forEach(System.out.println); // 结果: 1 2 3 4 5
String[] planets = new String[] {
    "Mercury", "Venus", "Earth",
    "Mars", "Jupiter", "Saturn",
    "Uranus", "Neptune"
};
// 对字符串的长度进行排序,默认升序
Stream<String> s3 = Stream.of(planets).sorted(
	Comparator.Comparing(String::length)
);
s3.forEach(System.out::println);
public class Cat implements Comparable<Cat> {
    private int size;
    public Cat(int size) {
        this.size = size;
    }
    public int compareTo(Cat cat) {
        Cat c = new Cat(5);
        return this.size - c.size;
    }
    public String toString() {
        return "Size: " + size;
    }
}
public static void main(String[] args) {
   		Stream<Cat> cats = Stream.of(new Cat[] {});
    	// 自定义排序规则调用compareTo方法
        Stream<Cat> s4 = cats.sorted();
        s4.forEach(System.out::println);
}
流的转化
  • 转为map
  • 利用方法引用对流每个元素进行函数计算
Stream<Double> s1 = Stream.of(-1.5, 2.5, -3.5);
Stream<Double> s2 = s1.map(Math::abs);
s2.forEach(System.out::println);
  • 利用Lambda表达式对流每个元素进行函数计算
Stream<Integer> s1 = Stream.of(1, 2, 3, 4);
Stream<Integer> s2 = s1.map(n -> n * n);
s2.forEach(System.out::println); // 输出 1 4 9 16
public static void main(String[] args) {
    String[] planets = new String[] { "Mercury", "Venus", "Earth" };
    Stream<Stream<String>> allLetters = Stream.of(planets).map(word -> letters(word));
    // 遍历输出
    allLetters.forEach((words) -> words.forEach((word) -> {
        System.out.print(word + " ");
    }));
}
public static Stream<String> letters(String word) {
    List<String> result = new ArrayList<>();
    for (int i = 0; i < word.length(); i++) {
        result.add(word.substring(i, i+1));
    }
    return result.stream();
}
运行结果:

M e r c u r y V e n u s E a r t h

抽取limit
  • 限定了流的元素个数
Stream<Integer> s1 = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> s2 = s1.limit(3);
s2.forEach(System.out::print); // 结果为123
跳过skip
  • 跳过前面n个元素
Stream<Integer> s1 = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> s2 = s1.skip(3);
s2.forEach(System.out::print); // 结果为45
连接concat
public static void main(String[] args) {
    Stream<String> s5 = Stream.concat(letters("hello"), letters("world"));
    s5.forEach(System.out::print);
}
public static Stream<String> letters(String word) {
    List<String> result = new ArrayList<>();
    for (int i = 0; i < word.length(); i++) {
        result.add(word.substring(i, i+1));
    }
    return result.stream();
}
调试peek
  • s1.forEach输出时会额外的执行peek中的函数功能
public static void main(String[] args) {
    Stream<Double> s1 = Stream.iterate(1.0, (n) -> n * 2)
                              .peek((n) -> System.out.println("number: " + n)).limit(5);
    s1.forEach(System.out::println); 
}
运行结果:

number: 1.0
1.0
number: 2.0
2.0
number: 4.0
4.0
number: 8.0
8.0
number: 16.0
16.0

Optional类型

  • Optional<T>类型
  • Optional<T>是一个包装器对象
  • 要么包装了类型T对象,要么没有包装任何对象(还是null)
  • 如果T有值,那么直接返回T的对象,如果T是null,那么可以返回一个替代物
  • Optional<T>类型可以解决空指针异常问题
public static void main(String[] args) {
        // Optaional类型的基本使用
        Optional<String> s1 = Optional.of(new String("abc"));
        // 获取Optional中的值
        String s2 = s1.get();
        System.out.println("s2: " + s2);

        Optional<String> s3 = Optional.empty();
        // 如果s3里没有值就会使用s3.orElse方法的字符串作为替代物
        // 如果s3有值则返回原来的值
        String s4 = s3.orElse("Optional is empty");
        System.out.println("s4: " + s4);
    }
运行结果:

s2: abc
s4: Optional is empty

Optional的创建
  • of方法
  • empty方法
  • ofNullable方法,对于对象有可能是null情况下,安全创建
public static void main(String[] args) {
        Optional<String> str = Optional.of(new String("abc"));
        Optional<String> str1 = Optional.empty();
        String str2 = null;
    	// str2不为null,str3就是str2,否则str3就为Optional.empty()
        Optional<String> str3 = Optional.ofNullable(str2);
        System.out.println(str3);
    }
Optional类型的使用
  • get方法,获取值,不安全的用法
  • 注意:直接使用get,很容易引发NoSuchElementException异常
  • orElse方法,获取值,如果为null,采用替代物的值
  • orElseGet方法,获取值,如果为null,采用Lambda表达式值返回
  • orElseThrow方法,获取值,如果为null,抛出异常
  • ifPresent方法,判断是否为null,不为null返回true
  • isPresent (Consumer),判断是否为null,如果不为null,则进行后续的Consumer操作,如果为null,则不做任何操作
  • 使用isPresent判断值是否存在和判断null一样低效
  • map (Function),将值传递给Function函数还进行计算。如果为null,则不计算

流的计算

流的计算类型
  • 聚合函数: count / max / min / …
  • 自定义约简: reduce
  • 查看 / 遍历元素:iterator / forEach
  • 存放到数据结构中
聚合函数
  • count(),计数
  • max(Comparator),最大值,需要比较器
  • min(Comparator),最小值,需要比较器
  • findFirst(),找到第一个元素
  • findAny(),找到任意一个元素
  • anyMatch(Predicate),如有任意一个元素满足Predicate,返回true
  • allMatch(Predicate),如所有元素满足Predicate,返回true
  • noneMatch(Predicate),如没有任何元素满足Predicate,返回true
自定义约简
  • reduce,传递一个二元函数BinaryOperator,对流元素进行计算
  • 如求和、求积、字符串连接等
public static void main(String[] args) {
        Integer[] a = new Integer[] { };
        Stream<Integer> s1 = Stream.of(a);
        // 数组求和
        Optional<Integer> sum = s1.reduce(Integer::sum);
        System.out.println(sum.orElse(-1));

        Stream<Integer> s2 = Stream.of(a);
        // 数组乘积
        Optional<Integer> product = s2.reduce((x, y) -> x * y);
        System.out.println(product.orElse(-1));

        Stream<Integer> s3 = Stream.of(a);
        // 默认值为1,当a为空数组是输出1
        Integer product2 = s3.reduce(1, (x, y) -> x * y);
        System.out.println(product2);
    }
查看 / 遍历元素
  • iterator,遍历元素
  • forEach(Consumer),应用一个函数到每个元素上
public static void main(String[] args) {
        Integer[] a = new Integer[] { 2, 4, 6, 8 };
        Stream<Integer> s1 = Stream.of(a);
        Iterator<Integer> it1 = s1.filter(number -> number > 2).iterator();
        while (it1.hasNext()) {
            System.out.print(it1.next() + " ");
        }
    
        System.out.println();
    
        Stream<Integer> s2 = Stream.of(a);
        s2.filter(numbers -> numbers > 2).forEach((number) -> System.out.print(number + " "));
    }
存储到数据结构中
  • toArray(),将结果转化成数组
  • collect(Collectors.toList()),将结果转化成List
  • collect(Collectors.toSet()),将结果转化成Set
  • collect(Collectors.toMap()),将结果转化成Map
  • collect(Collectors.joining()),将结果连接起来
流的高阶计算
  • 分组groupingBy和分区partitionBy
  • 分组后的约简 (相当于SQL中的having)
  • counting
  • summing
  • maxBy
  • minBy

流的注意事现

  • 一个流只能使用一次
  • 避免创建无限流
  • 注意操作顺序
无限流
// 无限流
IntStream.iterate(0, i -> i + 1).forEach(System.out::println);
// 无限流
IntStream.iterate(0, i -> (i + 1) % 2).distinct().limit(10).forEach(System.out::println);
操作顺序的不同结果不同
// 先limit后skip
IntStream.iteracte(0, i -> i + 1);
         .limit(10)
         .skip(5)
         .forEach(System.out::println); // 结果为5-9

// 先skip后limit
IntStream.iteracte(0, i -> i + 1)
         .skip(5)
         .limit(10)
         .forEach(System.out::println) // 结果为5-14
并行流的使用建议
  • 由于并行流底层是使用Fork-Join Pool,处理计算密集型任务,所以数据量过小不用
  • 数据结构不容易分解的时候不用,如LinkedList等
  • 数据频繁拆箱装箱不用
  • 涉及到findFirst或者limit的时候不用
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值