Java Stream 流解读

java.util.Stream 可以对元素列表进行一次或多次操作。Stream操作可以是中间值也可以是最终结果。最后的操作返回的是某种类型结果,而中间操作返回的是stream本身。因此你可以在一行代码链接多个方法调用。Streams被创建于java.util.Collection ,比如 list or set (map 并不支持)。Stream可以顺序执行,也可以并行执行。

•中间操作:filter、map、mapToInt、mapToLong、mapToDouble、flatMap、sorted、distinct、limit、skip、of、iterate•终止操作:forEach、count、collect、reduce、toArray、anyMatch、allMatch、noneMatch、findAny、findFirst、max、min•原始类型特化流:IntStream、LongStream、DoubleStream

过滤 Filter

Filter通过predicate判断函数来过滤所有的元素。这个操作是中间操作,需要通过终止操作才会触发执行。

代码:com.winterbe.java8.samples.stream.Stream_filter
stringCollection
    .stream()
    .filter((s) -> s.startsWith("a"))
    .forEach(System.out::println);


// "aaa2", "aaa1"

映射 Map

map是一种中间过程操作,借助函数表达式将元素转换成另一种形式。下面的例子将每个字符串转换成大写的字符串。但你也可以使用map将每个对象转换为另一种类型。最终输出的结果类型依赖于你传入的函数表达式。

stringCollection
    .stream()
    .map(String::toUpperCase)
    .sorted((a, b) -> b.compareTo(a))  //由大到小
    .forEach(System.out::println);


// "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"

映射 flatMap

如果涉及到一对多映射,需要将映射结果放入Stream中。使用flatMap方法的效果是,转换后的多个结果并不是分别映射成一个流,而是映射成流的内容。

代码:com.winterbe.java8.samples.stream.Stream_flatMap


List<String> lists = Arrays.asList("Hello", "World");
lists.stream().flatMap(word-> Stream.of(word.split("")))
        .distinct()
        .forEach(System.out::println);

排序 Sorted

Sorted是一个中间态操作,它返回流的有序视图。除非你传递自定义的Comparator,否则元素按默认的由小到大排序。

代码:com.winterbe.java8.samples.stream.Stream_sorted


//默认排序
stringCollection.stream().sorted().forEach(System.out::println);
System.out.println(stringCollection);
特别注意:`sorted`只是创建流的排序视图,并没有改变原始集合的顺序。所以说`stringCollection`的顺序并没有改变。
//自定义排序,按字符串,由大到小
stringCollection
        .stream()
        .map(String::toUpperCase)
        .sorted((a, b) -> b.compareTo(a))
        .forEach(System.out::println);

归约 Reduce

终止型操作,通过给定的函数表达式来处理流中的前后两个元素,或者中间结果与下一个元素。Lambda 反复结合每一个元素,直到流被归约成一个值。例如求和或查找最大元素。

代码:com.winterbe.java8.samples.stream.Stream_reduce


// 将流数据列表拆分多批,sum初始为0,每批都执行 (sum, p) -> sum = sum + p.age,得到局部的sum总和。并行计算思想
// 最后通过 (sum1, sum2) -> sum1 + sum2 ,计算最终的总和
// (sum1, sum2) -> sum1 + sum2,主要适用于并行,parallelStream(),单线程是无效的。


private static void test3(List<Person> persons) {
    Integer ageSum = persons.parallelStream().reduce(0, (sum, p) -> sum += p.age, (sum1, sum2) -> sum1 + sum2);
    System.out.println(ageSum);
}

更多reduce用法可参考:https://blog.csdn.net/io_field/article/details/54971679

计数 Count

Count是一个终止型操作,返回一个long类型的元素列表总数。

代码:com.winterbe.java8.samples.stream.Stream_count
long startsWithB =
    stringCollection
        .stream()
        .filter((s) -> s.startsWith("b"))
        .count();


System.out.println(startsWithB);    // 3

匹配 Match

各种匹配操作用于判断是否满足stream条件。所有的操作都完成后,返回一个boolean类型结果。

代码:com.winterbe.java8.samples.stream.Stream_match
List<String> stringCollection = new ArrayList<>();
stringCollection.add("ddd2");
stringCollection.add("aaa2");
stringCollection.add("bbb1");
stringCollection.add("aaa1");
stringCollection.add("bbb3");
stringCollection.add("ccc");
stringCollection.add("bbb2");
stringCollection.add("ddd1");


// 只需要一个条件满足
boolean anyStartsWithA = stringCollection.stream().anyMatch((s) -> s.startsWith("a"));
System.out.println("anyMatch:" + anyStartsWithA); // true


// 所有条件都要满足
boolean allStartsWithA = stringCollection.stream().allMatch((s) -> s.startsWith("a"));
System.out.println("allMatch:" + allStartsWithA); // false


// 所有的条件都要不满足
boolean noneStartsWithZ = stringCollection.stream().noneMatch((s) -> s.startsWith("z"));
System.out.println("noneMatch:" + noneStartsWithZ); // true


// 返回任意一个元素
Optional<String> anyE = stringCollection.stream().findAny();
System.out.println("findAny:" + anyE.get());


//返回第一个元素
Optional<String> firstE = stringCollection.stream().findFirst();
System.out.println("findFirst:" + firstE.get());

跳过 skip

返回一个扔掉前n个元素的流

代码:com.winterbe.java8.samples.stream.Stream_skip
// 扔掉前三个元素
stringCollection
    .stream()
    .skip(3)
    .forEach(System.out::println);

输出 limit

只取前N个结果

代码:com.winterbe.java8.samples.stream.Stream_limit
// 取前三个元素
stringCollection
    .stream()
    .limit(3)
    .forEach(System.out::println);

输出 collect

接受各种做法作为参数,将流中的元素累积成一个汇总结果

常见例子:

•对一个交易列表按货币分组,获得该货币的所有交易额总和(返回一个Map<Currency,Integer>)•将交易列表分成两组,贵的和不贵的(返回一个Map<Boolean,List<Transaction>>)•创建多级分组,比如按城市对交易分组,然后进一步按照贵的或不贵分组

Collectors常见方法:

•Collectors.toList,得到List列表•Collectors.toSet,得到Set集合•Collectors.joining ,通过连接符拼接字符串•Collectors.groupingBy(Function<? super T,? extends K>) ,按K值分组,返回Map<K,List>•Collectors.groupingBy(Function<? super T,? extends K>, Collector<? super T,A,D>),二级分组,得到两级Map•Collectors.partitioningBy(Predicate<? super T> predicate) ,分区是分组的特殊情况,返回一个布尔值,意味着得到的分组Map的key只能是Boolean,于是它最多可以分为两组•Collectors.maxBy,求最大值,需要传一个自定义的Comparator•Collectors.reducing,广义的归约汇总。

代码:com.winterbe.java8.samples.stream.Stream_collect


// 将字符串换成大写,并用逗号连接起来
List<String> citys = Arrays.asList("USA", "Japan", "France");
String cityS = citys.stream().map(x -> x.toUpperCase()).collect(Collectors.joining(", "));


// 按性别分组
Map<String, List<Student>> maps = studentList.stream().collect(Collectors.groupingBy(Student::getSex));


// 先按性别分组,然后再按年龄段分组
Map<String, Map<String, List<Student>>> maps = studentList.stream()
   .collect(Collectors.groupingBy(Student::getSex,
      Collectors.groupingBy(s -> {
          if (s.getAge() < 20) {
              return "低age";
          } else {
              return "高age";
          }
      })));


// 按年龄25分成两个组
Map<Boolean, List<Student>> maps = studentList.stream().collect(Collectors.partitioningBy(s -> {
    if (s.getAge() < 25) {
        return true;
    } else {
        return false;
    }
}));


// 找出年龄最大的人
Optional<Student> optional1 = studentList.stream().collect(Collectors.maxBy(Comparator.comparing(Student::getAge)));
optional1.ifPresent(System.out::println);


// 年龄总和
// reducing的参数,第一个:初始值。第二个:转换函数。第三个:累积函数
int sum = studentList.stream().collect(Collectors.reducing(0, Student::getAge, Integer::sum));

并行 Streams

如下所述,流可以是串行执行,也可以并行执行。对于流的串行执行是单个线程完成。而并行流处理则是在多个线程上同时执行。

下面这个例子将会演示如何通过并行流处理来显著提升性能。

首先我们创建一个大容量的List元素集合:

代码:com.winterbe.java8.samples.stream.Stream_reduce


int max = 1000000;
List<String> values = new ArrayList<>(max);
for (int i = 0; i < max; i++) {
    UUID uuid = UUID.randomUUID();
    values.add(uuid.toString());
}

现在我们测量对此集合的流排序所花费的时间。

串行 Sort

long t0 = System.nanoTime();


long count = values.stream().sorted().count();
System.out.println(count);


long t1 = System.nanoTime();


long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
System.out.println(String.format("sequential sort took: %d ms", millis));


// sequential sort took: 899 ms
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值