目录

java8新特性之Stream

目录

流用过好多次了,每次都感叹它的简洁方便,这回抽时间总结一下。

一、什么是Stream?

Stream是Java8中用于简洁高效地操作集合类的工具,是对集合(Collection)对象功能的增强,比如对集合元素的遍历、过滤、转换等。使用Stream用户不需要关心如何遍历元素,只需要关心对每个集合元素进行什么操作。

二、获取Stream

  • 从 Collection 和数组
    • Collection.stream()
    • Collection.parallelStream()
    • Arrays.stream(T array) or Stream.of()
  • 从 BufferedReader
    • java.io.BufferedReader.lines()
  • 静态工厂
    • java.util.stream.IntStream.range()
    • java.nio.file.Files.walk()
  • 自己构建
    • java.util.Spliterator
  • 其它
    • Random.ints()
    • BitSet.stream()
    • Pattern.splitAsStream(java.lang.CharSequence)
    • JarFile.stream()
  • 例子:
// 1. Individual values
Stream stream = Stream.of("a", "b", "c");

// 2. Arrays
String [] strArray = new String[] {"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);

// 3. Collections
List<String> list = Arrays.asList(strArray);
stream = list.stream();

三、流转换为其他数据结构

// 1. Array
String[] strArray1 = stream.toArray(String[]::new);

// 2. Collection
List<String> list1 = stream.collect(Collectors.toList());
List<String> list2 = stream.collect(Collectors.toCollection(ArrayList::new));
Set set1 = stream.collect(Collectors.toSet());
Map<Long, UserDTO> map= stream.collect(Collectors.toMap(userDO::getUserId, userDO -> userDO));
Stack stack1 = stream.collect(Collectors.toCollection(Stack::new));

// 3. String
String str = stream.collect(Collectors.joining()).toString();

四、Stream流处理

  • forEach
//返回值为void,若要继续链式流处理,可以用peek
forEach(p -> System.out.println(p.getName()));
forEach(System.out::println)
  • peek
//修改对象属性值
peek(i -> {
            if(OtherMap.containsKey(i.getUserId())){
                i.setPhone(OtherMap.get(i.getUserId()).getPhone());
                i.setNickname(OtherMap.get(i.getUserId()).getNickname());
            }
        }).collect(Collectors.toList());
  • filter
filter(n -> n%2 == 0)
filter(word -> word.length() > 0).
  • map
map(String::toUpperCase) 
map(n -> n * n)
  • flatMap
    • 1
//flatMap 把 input Stream 中的层级结构扁平化,就是将最底层元素抽出来放到一起,
//最终 output 的新 Stream 里面已经没有 List 了,都是直接的数字。
Stream<List<Integer>> inputStream = Stream.of(
    Arrays.asList(1),
    Arrays.asList(2, 3),
    Arrays.asList(4, 5, 6)
);

Stream<Integer> outputStream = inputStream.
flatMap((childList) -> childList.stream());
+ 2 :同时获取对象中两个字段
list.stream().flatMap(a -> Stream.of(a.getNumber1(),a.getNumber2())).distinct()
  • sorted
sorted((p1, p2) -> p1.getName().compareTo(p2.getName()))
  • distinct(去重)

  • limit、skip

//取前10个元素,之后再舍弃前3个,跳过前 3 个元素后只有后面 7 个返回
limit(10).skip(3)
  • reduce
//第一个参数(空白字符)即为起始值,第二个参数(String::concat)为 BinaryOperator
reduce("", String::concat);
Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);
Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
  • collect
// 1. Collection
List<String> list1 = stream.collect(Collectors.toList());
List<String> list2 = stream.collect(Collectors.toCollection(ArrayList::new));
Set set1 = stream.collect(Collectors.toSet());
Map<Long, UserDTO> map= stream.collect(Collectors.toMap(userDO::getUserId, userDO -> userDO));
Stack stack1 = stream.collect(Collectors.toCollection(Stack::new));
// 2. String
String str = stream.collect(Collectors.joining()).toString();
  • match
    • Stream 有三个 match 方法,从语义上说:
      • allMatch:Stream 中全部元素符合传入的 predicate,返回 true
      • anyMatch:Stream 中只要有一个元素符合传入的 predicate,返回 true
      • noneMatch:Stream 中没有一个元素符合传入的 predicate,返回 true
    • 例:
boolean isAllAdult = persons.stream().
allMatch(p -> p.getAge() > 18);
System.out.println("All are adult? " + isAllAdult);

boolean isThereAnyChild = persons.stream().
anyMatch(p -> p.getAge() < 12);
System.out.println("Any child? " + isThereAnyChild);
  • min、max、count

五、进阶:自己生成流

  1. Stream.generate 通过实现 Supplier 接口,你可以自己来控制流的生成。这种情形通常用于随机数、常量的 Stream,或者需要前后元素间维持着某种状态信息的 Stream。把 Supplier 实例传递给 Stream.generate() 生成的 Stream,默认是串行(相对 parallel 而言)但无序的(相对 ordered 而言)。由于它是无限的,在管道中,必须利用 limit 之类的操作限制 Stream 大小。
    • 生成 10 个随机整数
Random seed = new Random();
Supplier<Integer> random = seed::nextInt;
Stream.generate(random).limit(10).forEach(System.out::println);
//Another way
IntStream.generate(() -> (int) (System.nanoTime() % 100)).
limit(10).forEach(System.out::println);
  • 自实现 Supplier
Stream.generate(new PersonSupplier()).
limit(10).
forEach(p -> System.out.println(p.getName() + ", " + p.getAge()));
private class PersonSupplier implements Supplier<Person> {

    private int index = 0;

    private Random random = new Random();

    @Override
    public Person get() {
        return new Person(index++, "StormTestUser" + index, random.nextInt(100));
    }
}
  1. Stream.iterate iterate 跟 reduce 操作很像,接受一个种子值,和一个 UnaryOperator(例如 f)。然后种子值成为 Stream 的第一个元素,f(seed) 为第二个,f(f(seed)) 第三个,以此类推。
    • 生成一个等差数列
Stream.iterate(0, n -> n + 3).limit(10). forEach(x -> System.out.print(x + " "));
//0 3 6 9 12 15 18 21 24 27
  1. 用 Collectors 来进行 reduction 操作:groupingBy/partitioningBy
    • 按照年龄归组
Map<Integer, List<Person>> personGroups = Stream.generate(new PersonSupplier()).
    limit(100).
    collect(Collectors.groupingBy(Person::getAge));
Iterator it = personGroups.entrySet().iterator();
while (it.hasNext()) {
    Map.Entry<Integer, List<Person>> persons = (Map.Entry) it.next();
    System.out.println("Age " + persons.getKey() + " = " + persons.getValue().size());
}
//输出:
//Age 0 = 2
//Age 1 = 2
//Age 5 = 2
//Age 8 = 1
//Age 9 = 1
//Age 11 = 2
  • 按照未成年人和成年人归组:partitioningBy 其实是一种特殊的 groupingBy
Map<Boolean, List<Person>> children = Stream.generate(new PersonSupplier()).
limit(100).collect(Collectors.partitioningBy(p -> p.getAge() < 18));
System.out.println("Children number: " + children.get(true).size());
System.out.println("Adult number: " + children.get(false).size());
//输出:
//Children number: 23
//Adult number: 77

六、基本数值类型的流

  1. 对于基本数值型,目前有三种对应的包装类型 Stream: IntStream、LongStream、DoubleStream。当然我们也可以用 Stream、Stream、Stream,但是 boxing 和 unboxing 会很耗时,所以特别为这三种基本数值型提供了对应的 Stream。
    • 数据流的构造
IntStream.of(new int[]{1, 2, 3}).forEach(System.out::println);
IntStream.range(1, 3).forEach(System.out::println);
IntStream.rangeClosed(1, 3).forEach(System.out::println);

参考

← 返回技术