java8-流的操作

流的操作

流的使用一般包括三件事:

一个数据源来执行一个查询;

一个中间操作链,形成一条流的流水线;

一个终端操作,执行流水线,并能生成结果

中间操作

操作 类型 返回类型 操作参数 函数描述符
filter 中间 Stream<T> Predicate<T> T -> boolean
map 中间 Stream<T> Function<T,R> T->R
limit 中间 Stream<T>
sorted 中间 Stream<T> Comparator<T> (T,T)->int
distinct 中间 Stream<T>

终端操作

操作 类型 目的
forEach 终端 消费流中的每个元素并对其应用Lambda.这一操作返回void
count 终端 返回流中元素的个数,这一操作返回long
collect 终端 把流归约成一个集合,比如List,Map甚至是Integer

使用流

筛选

出了filter ,流还支持一个叫做distinct的方法,它会返回一个元素各异(根据流所生成元素的hashCode和equals方法实现)的流.例如,如下代码会筛选出列表中所有的偶数,并确保没有重复

List<Integer> numbers = Arrays.asList(1,2,1,3,3,2,4);
numbers.stream()
  .filter(i -> i%2 ==0)
  .distinct()
  .forEach(System.out::println);

截断流

流支持limit(n) 方法,该方法会返回一个不超过给定长度的流.

List<Dish> dishes = menu.stream()
.filter(d->d.getCalories()>300)
.limit(3)
.collect(toList());

跳过元素

流还支持skip(n)方法,返回一个扔掉了前n个元素的流.

List<Dish> dishes = menu.stream().filter(d->d.getCalories()>300).skip(2).collect(toList());

映射

对流中每一个元素应用函数

例如: 如果要找出每道菜的名称有多长,则如下:

List<Integer> dishNameLengths = menu.stream().map(Dish::getname).map(String::length).collect(toList())

流的扁平化

例如:给定单词列表["hello","world"],你想要返回列表["h","e","l","l","o","w","o","r","l","d"]

利用上面的可能是:

words.stream().map(word->word.split("")).distinct().collect(toList());

这个方法的问题自傲与,传递给map方法的Lambda为每个单词返回一个String[] (String列表),因此,map返回的流实际上是Stream< String[] >类型的.你真正想要的是用Stream< String > 来表示一个字符流

1.尝试使用map和Arrays.stream()

String[] arrayOfWords = {"GoodBye","World"};
Stream< String> streamOfWords = Arrays.stream(arrayOfWords);    

2.使用flatMap

        String[] arrayOfWords = {"GoodBye","World"};
        Stream< String> streamOfWords = Arrays.stream(arrayOfWords);
        List<String[]> collect = streamOfWords.map(m -> m.split("")).collect(toList());
        System.out.println(collect);

例2:

给定列表[1,2,3] 和[3,4] ,返回[(1,3),(1,4),(2,3).....]

        List<Integer> integers = Arrays.asList(1, 2, 3);
        List<Integer> integers1 = Arrays.asList(3, 4);
        List<int[]> collect = integers.stream()
                .flatMap(i -> integers1.stream()
                        .map(j -> new int[]{i, j}))
                .collect(toList());
        collect.forEach(i -> {
            String s = Arrays.toString(i);
            System.out.println(s);
        });

例3:

扩展前面的例子,只返回综合能被3正处的数对呢?

        List<Integer> int1 = Arrays.asList(1, 2, 3);
        List<Integer> int2 = Arrays.asList(3, 4);
        List<int[]> collect = int1.stream().flatMap(i -> int2.stream().filter(j -> (i + j) % 3 == 0).map(j -> new int[]{i, j})).collect(toList());
        System.out.println(collect);

查找和匹配

匹配
1.检查谓词是否至少匹配一个一元素

anyMatch方法可以回答"流中是否有一个元素能匹配给定的谓词"

if(menu.stream().anyMatch(Dish::isVegetarian)){
    System.out.println("The menu is vegetarian friendly!!");    
}
2.检查谓词是否匹配所有元素

例如:用allMatch来看看菜品是否有利健康

boolean isHealthy = menu.stream().allMatch(d->d.getCalories()<1000);
3.确保流中没有任何元素与给定的谓词匹配 noneMatch
boolean isHealthy = menu.stream().noneMatch(d->d.getCalories()<1000);
查找
findAny方法将返回当前流中的任意元素
Optional<Dish> dish = menu.stream().filter(Dish::isVegetarian).findAny();
查找第一个元素
List<Integer> someNumbers = Arrays.asList(1,2,3,4,5);
Optional<Integer> firstSquareDivisibleByThree = 
    someNumbers.stream()
    .map(x->x*x)
    .filter(x->x%3 == 0)
    .findFirst();

归约

元素求和 reduce

reduce接受两个参数

一个初始值,这里是0

一个BinaryOperator< T >来将两个元素结合起来产生一个新值 ,这里我们用的是lambda(a,b)->a+b;

int sum = numbers.stream().reduce(0,(a,b)->a+b);

相乘:

int product = numbers.stream().reduce(1,(a,b)->a*b);

在java 8 中,Integer类现有了一个静态的sum方法来对两个数求和

int num=numbers.stream().reduce(0,Integer::sum);

最大值:

Optional<Integer> max = numbers.stream().reduce(Intger::max)

最小值:

Optional<Integer> min = numbers.stream().reduce(Integer::min);

数值流

原始类型流特化

java 8 引入了三个原始类型特化流接口来专门支持处理数值流的方法:IntStream . DoubleStream 和LongStream,.

分别将流中的元素特化成为int,long ,double,从而避免了暗含装箱成本

1.映射到数值流

将流转化为特化版本的常用方法是mapToInt,mapToDouble ,mapToLong.

int calaories = menu.stream()
    .mapToInt(Dish::getCalories)//返回一个IntStream
    .sum();

2.转换回対向流 boxed

IntStream intStream = menu.stream().mapToInt(Dish::getCalories);
Stream<Integer> stream = intStream.boxed();

3.默认值OptionalInt

IntStream中有个默认值0,所以,为了解决求最大值或最小值的问题 ,使用Optional原始类型特化版本:OptionalInt,

OptionalDouble,OptionalLong.

例如:要找到IntStream中的最大元素,可以调用max方法,它会返回一个OptionalInt:

OptionalInt maxCalories = menu.stream()
    .mapToInt(Dish::getCalories)
    .max();
//如果没有最大值的话,可默认一个最大值
int max = maxCalories.orElse(1);

4.数值范围

假如你想要生成1到100之间的所有数字. java8引入了两个可以用于IntStream和LongStream的静态方法,帮助生成这种范围:range和rangeClosed. 这两个方法都是第一个参数接受起始值,第二个参数接受结束值.但是range是不包含结束值的,而rangeClosed则包含结束值

IntStream evenNumbers = IntStream.rangeClosed(1,100)
    .filter(n -> n % 2 == 0);//一个从1到100的偶数流

System.out.println(evenNumbers.count());//从1到100有50个偶数

构建流

由值创建流

可以使用静态方法Stream.of,通过显示值创建一个流.它可以接受任意数量的参数

Strean<String> stream = Stream.of("java 8","Lambdas ","In ","Action");
stream.map(String::toUpperCase).forEach(System.out::println);
//使用empty得到一个空流
Stream<String> emptySteam = Stream.empty();
由数组创建流
int[] numbers = {2,3,4,11,13};
int sum = Arrays.stream(numbers).sum();
由文件生成流
long uniqueWords = 0;
try(Stream<String> lines = 
    Files.lines(Paths.get("data.txt"),Charset.defaultCharset())){
    uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" ")))
        .distinct()
        .count();
}catch(IOException e){
  
}
由函数生成流:创建无限流

Stream.iterate和Stream.generate ,一般来说应该使用limit(n)来对这种流加以限制,以避免打印无穷多个值

1.迭代

Stream.iterate(0,n -> n+2)
    .limit(10)
    .forEach(System.out::println);

2.生成

与iterate方法类似,generate方法也可生成一个无限流.但generate不是依次对每个新生成的值应用函数的.它接受一个Supplier< T >类型的Lambda提供新的值

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

推荐阅读更多精彩内容

  • 概要 流让你从外部迭代转向内部迭代。这样,你就用不着写下面这样的代码来显式地管理数据集合的迭代(外部迭代)了: 现...
    浔它芉咟渡阅读 1,438评论 1 2
  • Java8 in action 没有共享的可变数据,将方法和函数即代码传递给其他方法的能力就是我们平常所说的函数式...
    铁牛很铁阅读 1,144评论 1 2
  • 第一章 为什么要关心Java 8 使用Stream库来选择最佳低级执行机制可以避免使用Synchronized(同...
    谢随安阅读 1,441评论 0 4
  • Stream 允许你以声明性方式处理数据集合,流还可以透明地并行处理,你就无需写任何多线程代码了。和迭代器类似,流...
    PawsUp阅读 744评论 0 3
  • 流提供了一种让我们可以在比集合更高的概念级别上指定计算的数据试图,用来解决“做什么而非怎么做”的问题。 从迭代到流...
    _gitignore阅读 1,152评论 0 1