资讯专栏INFORMATION COLUMN

Java8-流

whinc / 1789人阅读

摘要:因此,使用并行流需要考虑以下几点数据量将问题分解之后并行化处理,再将结果合并会带来额外的开销。

目录

简介

用法

例子

注意点

一. 简介

流是Java8引入的一个新特性,提供了对集合元素一系列便捷的操作,可以用很少的代码实现复杂的功能。
流有两大类,分别是对象流(Stream),基本数据流(IntStream、LongStream、DoubleStream)。

二.用法

流的使用通常为三个步骤:①创建流 ②描述流 ③求值

创建流

通过Stream静态工厂方法来创建,Stream类提供了以下方法创建流。

of:
Stream stream = Stream.of(1, 2, 3, 4);
IntStream intStream = IntStream.of(new int[]{1, 2, 3});

generator:生成一个无限长度的Stream
可以看作是个工厂,每次返回一个给定类型的对象。
Stream generate = Stream.generate(Math::random);

iterate:生成无限长度的Stream
其元素的生成是重复对给定的种子值(seed)调用用户指定函数生成的
Stream stream = Stream.iterate(1, element -> element + 1);

empty():生成空流
Stream.empty();

通过文件生成流:File.lines()
Stream lines = Files.lines(Paths.get("data.txt"));

最常用的 通过Collection子类获取Stream
List list = new ArrayList<>();
Stream stream = list.stream();

通过Arrays.stream()封装数据生成流,也很常用
IntStream intStream = Arrays.stream(new int[]{1, 2, 3});

比较少用的 CharSequence 接口方法 .chars()
String str = "hello world";
IntStream intStream = str.chars();

描述流

创建好流之后,就可以描述流了。更准确的说法是,声明流的中间操作。
中间操作比较常用的有以下几种(返回值为Stream的均为中间操作)

.filter(man -> man.getAge() > 18)  过滤器

.map(Man::getName)       获取字段,map操作可以执行多次,将数据转为我们期望的

.mapToInt            转为数值流,这个和sum这些组合使用可以省去装箱成本

.limit(3)            截断流,前三个

.distinct()            去重

.sorted()            排序

.skip(3)             丢弃前三个

.flatMap(Arrays::stream)     合并流

求值

与中间操作相对应的是终端操作,也就是我们的第三步,求值。
常用的终端操作有以下几种(返回值不为Stream的为终端操作)

.collect(toList());    结果转为List格式

.collect(toSet());    结构转为Set格式,自带distinct效果

.count();       计数

.findfirst();       找第一个

.findAny();      找任何一个

.forEach(System.out::println) 为每个元素进行的操作

.min         获取最小值

.max         获取最大值

说明
流是使用了内部迭代而不是外部迭代
内部迭代使用了惰性求值,只有在需要结果的那一刻才进行求值。
所有的中间操作(只描述流)都是惰性求值,因为只返回了Stream。
三.例子

// 用户类

public class User {
    private Integer id;
    private String name;
    private Integer age;
    // GET SET Constructor ..
}

// 初始化数据

List userList = new ArrayList() {
    /** */
    private static final long serialVersionUID = 1L;
    {
        add(new User(1, "Luffy",   17)); 
        add(new User(2, "Zoro",    19)); 
        add(new User(3, "Nami",    18)); 
        add(new User(4, "Usopp",   17)); 
        add(new User(5, "Sanji",   19)); 
        add(new User(6, "Chopper", 15));
        add(new User(7, "Robin",   28)); 
        add(new User(8, "FRANKY",  34)); 
        add(new User(9, "BROOK",   88)); 
    }
};

// 例子

@Test
public void test1() {
    // 获取所有年龄大于17的用户
    List users = userList.stream()
            .filter(user -> user.getAge() > 17)
            .collect(Collectors.toList());
    System.out.println(users);
}

// 静态导入Collectors后会更简洁。
// import static java.util.stream.Collectors.*;

@Test
public void test2() {
    // 根据年龄是否大于17岁对集合进行分块
    Map> users = userList.stream()
        .collect(partitioningBy(u -> u.getAge() > 17));
    System.out.println(users);
}

@Test
public void test3() {
    // 按照年龄,将用户分组
    Map> users = userList.stream()
        .collect(groupingBy(User::getAge));
    System.out.println(users);
}

@Test
public void test4() {
    // 获取所有用户的名称
    List names = userList.stream()
        .map(User::getName)
        .collect(toList());
    System.out.println(names);
}

@Test
public void test5() {
    // 获取年龄最大的用户(只返回一个)
    User user = userList.stream()
        .max((u1, u2) -> {
            return u1.getAge() > u2.getAge() ? 1 : -1;
        })
        .get();
    System.out.println(user);
}

@Test
public void test6() {
    // 获取用户年龄总和
    int ageSum = userList.stream()
        .map(User::getAge)
        .reduce(0, (acc, element) -> {
            acc += element;
            return acc;
        });
    System.out.println(ageSum);

    ageSum = userList.stream()
        .map(User::getAge)
        .reduce(0, Integer::sum);
    System.out.println(ageSum);

    ageSum = userList.stream()
        .collect(summingInt(User::getAge));
    System.out.println(ageSum);
}

@Test
public void test7() {
    // 获取组成用户姓名的字母
    Set set = userList.stream()
        .map(User::getName)
        .map(name -> name.split(""))
        .flatMap(Arrays::stream)
        .collect(toSet());
    System.out.println(set);
}
四.注意点

如何进行调试

// 可以利用peek查看流中的数据状态
userList.stream()
    .peek(System.out::println)
    .map(u -> u.getName())
    .peek(System.out::println)
    .collect(toList());

使用并行还是串行

// 将流改为并行流十分简单,只需要将stream方法改为parallelStream
Map> users = userList.parallelStream()                
    .collect(groupingBy(User::getAge));

// 或是使用 parallel() 声明为并行
Stream.iterate(0L, i -> i + 1)
    .parallel()
    .limit(10)
    .reduce(0L, Long::sum);

Java8流的并行属于数据并行,即将数据分为几部分,利用多核CPU对每块数据执行运算,最终汇总数据,得到答案。底层是使用了fork/join框架,fork递归分解问题,然后每段并行执行,最终join合并结果。 因此,使用并行流需要考虑以下几点:

数据量 : 将问题分解之后并行化处理,再将结果合并会带来额外的开销。
因此数据足够大,每个数据处理管道花费的时间足够多时,并行化处理才有意义

将数据对半分解的难易程度 :
ArrayList、数组、IntStream.range,这些数据结构支持随机读取,也就是说能轻而易举地被任意分解。
LinkedList,对半分解太难。需要o(n) 时间复杂度

核的数量 : 单核的机器是没必要并行化的

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/68998.html

相关文章

  • Java8实战》-读书笔记第一章(02)

    摘要:实战读书笔记第一章从方法传递到接着上次的,继续来了解一下,如果继续简化代码。去掉并且生成的数字是万,所消耗的时间循序流并行流至于为什么有时候并行流效率比循序流还低,这个以后的文章会解释。 《Java8实战》-读书笔记第一章(02) 从方法传递到Lambda 接着上次的Predicate,继续来了解一下,如果继续简化代码。 把方法作为值来传递虽然很有用,但是要是有很多类似与isHeavy...

    lushan 评论0 收藏0
  • 乐字节-Java8新特性之Stream(上)

    摘要:需要注意的是很多流操作本身就会返回一个流,所以多个操作可以直接连接起来,如下图这样,操作可以进行链式调用,并且并行流还可以实现数据流并行处理操作。为集合创建并行流。 上一篇文章,小乐给大家介绍了《Java8新特性之方法引用》,下面接下来小乐将会给大家介绍Java8新特性之Stream,称之为流,本篇文章为上半部分。 1、什么是流? Java Se中对于流的操作有输入输出IO流,而Jav...

    dingda 评论0 收藏0
  • java8系列」式编程Stream

    摘要:前言系列神秘的系列神奇的函数式接口继上两篇之后,本文已经系列的第三篇了。相反,他们会返回一个持有结果的新。操作是延迟执行的。截断流,使其元素不超过给定数量。返回流中元素总数。返回流中最大值。 前言 「Java8系列」神秘的Lambda「Java8系列」神奇的函数式接口继上两篇之后,本文已经java8系列的第三篇了。本篇文章比较长,但我希望大家都能认真读完。读不完可以先收藏,在找时间读。...

    bovenson 评论0 收藏0
  • Java8特性和Lambda表达式

    摘要:表达式体现了函数式编程的思想,即一个函数亦可以作为另一个函数参数和返回值,使用了函数作参数返回值的函数被称为高阶函数。对流对象进行及早求值,返回值不在是一个对象。 Java8主要的改变是为集合框架增加了流的概念,提高了集合的抽象层次。相比于旧有框架直接操作数据的内部处理方式,流+高阶函数的外部处理方式对数据封装更好。同时流的概念使得对并发编程支持更强。 在语法上Java8提供了Lamb...

    gaara 评论0 收藏0
  • Stream与Lambda表达式(一) 杂谈

    摘要:一流转换为数组集合陈杨将流转换为数组将流转换为数组将流转换为集合将流转换为集合解析 一、流 转换为数组、集合 package com.java.design.java8.Stream; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context...

    Harpsichord1207 评论0 收藏0
  • Java8(stream)操作

    摘要:串行与并行可以分为串行与并行两种,串行流和并行流差别就是单线程和多线程的执行。返回串行流返回并行流和方法返回的都是类型的对象,说明它们在功能的使用上是没差别的。唯一的差别就是单线程和多线程的执行。 Stream是什么 Stream是Java8中新加入的api,更准确的说: Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便...

    yacheng 评论0 收藏0

发表评论

0条评论

whinc

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<