Stream API(一)流

流是“从支持数据处理操作的源生成的一系列元素”。流中的元素是按需计算的。

集合的弊端:
1. 无法像SQL语句那样筛选数据,需要利用迭代器或者累加器筛选。
2. 并行处理集合比较繁琐,易出错。

流的优势:
3. 以声明性方式处理数据集合。
4. 透明地进行并行处理,无需写多线程代码。

一、流与集合

代码例子:

//传统集合处理方式
List<Dish> lowCaloricDishes = new ArrayList<>();
//筛选低卡路
for(Dish d : menu){
    if(d.getCalories() < 400){
        lowCaloricDishes.add(d);
    }
}
//按卡路里排序
Collectioins.sort(lowCaloricDishes, new Comparator<Dish>(){
    public int compare(Dish d1, Dish d2){
        return Integer.compare(d1.getCalories(), d2.getCalories());
    }
});
//获取名字集合
List<String> lowCaloricDishesName = new ArrayList<>();
for(Dish d : lowCaloricDishes){
    lowCaloricDishesName.add(d.getName());
}

//流处理方式
List<String> lowCaloricDishesName = menu.stream()
    .filter(d -> d.getCalories() < 400)
    .sort(comparing(Dish::getCalories))
    .map(Dish::getName)
    .collect(toList());
//其中stream可以换成parallelStream来使用并行处理

集合的处理方式一般为外部迭代,而流处理为内部迭代。


内部迭代与外部迭代

二、流操作

流的操作有两类:中间操作和中端操作。
中间操作:输入类型为Stream<T>,输出类型为Stream<T>。
终端操作:输入类型为Stream<T>,输出类型为非Stream值。


中间操作

终端操作

三、使用流

  1. 筛选和切片:filter、distinct、limit、skip
  2. 映射:map、flatMap
  3. 查找和匹配:allMatch、anyMatch、noneMatch、findFirst、findAny
  4. 归约:reduce
    1. 累加:stream().reduce(0, (a, b) -> a + b)
    2. 累乘:stream().reduce(1, (a, b) -> a * b)
    3. 最大值:stream().reduce(Integer::max)
    4. 最小值:stream().reduce(Integer::min)
    5. Lambda替换累加:stream().reduce(0, Integer::sum)
    6. Lambda替换最小值:stream.reduce((x, y) -> x < y ? x : y)

经典案例(交易员和交易问题):

(1) 找出2011年发生的所有交易,并按交易额排序(从低到高)。
(2) 交易员都在哪些不同的城市工作过?
(3) 查找所有来自于剑桥的交易员,并按姓名排序。
(4) 返回所有交易员的姓名字符串,按字母顺序排序。
(5) 有没有交易员是在米兰工作的?
(6) 打印生活在剑桥的交易员的所有交易额。
(7) 所有交易中,最高的交易额是多少?
(8) 找到交易额最小的交易。

//Trader类
public class Trader {
    private final String name;
    private final String city;

    public Trader(String name, String city) {
        this.name = name;
        this.city = city;
    }

    public String getName() {
        return name;
    }

    public String getCity() {
        return city;
    }

    @Override
    public String toString() {
        return "Trader{" +
                "name='" + name + " in " +
                "city='" + city  +
                '}';
    }
}

//Transaction类
public class Transaction {
    private final Trader trader;
    private final int year;
    private final int value;

    public Transaction(Trader trader, int year, int value) {
        this.trader = trader;
        this.year = year;
        this.value = value;
    }

    public Trader getTrader() {
        return trader;
    }

    public int getYear() {
        return year;
    }

    public int getValue() {
        return value;
    }

    @Override
    public String toString() {
        return "Transaction{" +
                "trader=" + trader +
                ", year=" + year +
                ", value=" + value +
                '}';
    }
}

//数据
Trader raoul = new Trader("Raoul", "Cambridge");
Trader mario = new Trader("Mario", "Milan");
Trader alan = new Trader("Alan", "Cambridge");
Trader brain = new Trader("Brain", "Cambridge");

List<Transaction> transactions = Arrays.asList(
    new Transaction(brain, 2011, 300),
    new Transaction(raoul, 2012, 1000),
    new Transaction(raoul, 2011, 400),
    new Transaction(mario, 2012, 710),
    new Transaction(mario, 2012, 700),
    new Transaction(alan, 2012, 950)
);

答案请参考: Steam流--交易员和交易问题答案

四、数值流

Stream API提供了原始类型流特化以支持处理数值流的方法。
Java8引入三个原始类型特化流接口:IntStream、DoubleStream、LongStream,分别将流中的元素特化为 int、double、long,从而避免装箱的成本。

  1. 映射到数值流
    常用方法有mapToInt、mapToDouble、mapToLong,返回的是特化流而不是Stream<T>。
//下面例子中返回的是IntStream,而不是Stream<Integer>
int calories = menu.stream()
                   .mapToInt(Dish::getCalories)
                   .sum();
//sum方法中若流为空,则结果为0。
//IntStream还支持其他方法,如max、min、average等操作。
  1. 数值流转换为对象流
//将 Stream 转 换为数值流
IntStream intStream = menu.stream().mapToInt(Dish::getCalories);
//将数值流转 换为Stream
Stream<Integer> stream = intStream.boxed();
  1. 默认值OptionalInt
    Optional类是一个可以表示值存在或不存在的容器。避免了流为空是,数值为0的情况。
    Optional原始类型特化版本:OptionalInt、OptionalDouble、OptionalLong。
// 例如,要找到IntStream中的最大元素,可以调用max方法,它会返回一个OptionalInt:
OptionalInt maxCalories = menu.stream()
                              .mapToInt(Dish::getCalories)
                              .max(); 
// 现在,如果没有最大值的话,你就可以显式处理OptionalInt去定义一个默认值了:
//如果没有最大值的话,显式提供一个默认最大值
int max = maxCalories.orElse(1);
  1. 数值范围
    Java 8引入了两个可以用于IntStream和LongStream的静态方法,range和rangeClosed。这两个方法都是第一个参数接受起始值,第二个参数接受结束值。但 range是不包含结束值的,而rangeClosed则包含结束值。
//表示范围:[1, 100], 一个从1到100的偶数流
IntStream evenNumbers = IntStream.rangeClosed(1, 100) 
                                     .filter(n -> n % 2 == 0);
//结果为50,若改为range方法则结果为49。                                    
System.out.println(evenNumbers.count());

五、构建流

  1. 由值创建流

    //Stream.of创建显示流
    Stream<String> stream = Stream.of("Java 8", "Lambdas ", "in ", "Action");
    //empty得到空流
    Stream<String> emptyStream = Stream.empty();
    
  2. 由数组创建流

    //Arrays.stream从数组创建一个流
    int[] numbers = {2, 3, 5, 7, 11, 13};
    int sum = Arrays.stream(numbers).sum();
    
  3. 由文件生成流
    例如统计一个文件中有多少各不相同的词:


    相关代码介绍
  4. 由函数生成流
    Stream API提供了从函数生成流的方式:Stream.iterate和Stream.generate,这两个操作可以创建无限流(即不像从集合中创建固定大小的流)。

    //依次在每个新产生的值上应用Lambda
    Stream.iterate(0, n -> n + 2)
            .limit(10)
            .forEach(System.out::println);
               
    //这个不是依次对每个新生成的值应用函数
    Stream.generate(Math::random)
            .limit(5)
            .forEach(System.out::println);
    
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352

推荐阅读更多精彩内容