java8流操作

流是Java API的新成员,它允许你以声明性方式处理数据集合

流操作有两个重要的特点:

  1. 流水线——很多流操作本身会返回一个流,这样多个操作就可以链接起来,形成一个大的流水线。

2.内部迭代——与使用迭代器显式迭代的集合不同,流的迭代操作是在背后进行的。

流与集合的区别:

集合中的每个元素都得先算出来才能添加到集合中

流就像是一个延迟创建的集合,只有在消费者要求的时候才会计算值

流的构建

Stream.of("Java 8 ", "Lambdas ", "In ", "Action") 由值创建流

Arrays.stream(numbers) 由数组创建流

List.stream() 集合创建流

Stream API提供了两个静态方法来从函数生成流:Stream.iterate和Stream.generate。 这两个操作可以创建所谓的无限流:不像从固定集合创建的流那样有固定大小的流。由iterate 和generate产生的流会用给定的函数按需创建值,因此可以无穷无尽地计算下去!一般来说, 应该使用limit(n)来对这种流加以限制,以避免打印无穷多个值。

流的使用

可以使用流进行筛选&切片、映射、查找&匹配、归约。

下列使用均以此List为例

List<Dish> menu = Arrays.asList(
                new Dish("pork", false, 800, Dish.Type.MEAT),
                new Dish("beef", false, 700, Dish.Type.MEAT),
                new Dish("chicken", false, 400, Dish.Type.MEAT),
                new Dish("french fries", true, 530, Dish.Type.OTHER),
                new Dish("rice", true, 350, Dish.Type.OTHER),
                new Dish("season fruit", true, 120, Dish.Type.OTHER),
                new Dish("pizza", true, 550, Dish.Type.OTHER),
                new Dish("prawns", false, 300, Dish.Type.FISH),
                new Dish("salmon", false, 450, Dish.Type.FISH));
  • 筛选&切片

    filter:筛选

    // 筛选出菜单中所有素食,filter接收一个函数式接口,我们可以使用之前提到的方法引用
    // collect是一个规约操作,之后介绍
    menu.stream().filter(Dish::isVegetarian).collect(Collectors.toList())
    

    distinct:去重

    // 使用流去除集合中的重复元素,并生成新的集合
    List<Integer> list = Arrays.asList(1,2,2,3,4,5,5,5);
    list.stream().distinct().collect(Collectors.toList());
    

    limit:截断

    // 取集合中前3个元素,并生成新的集合
    List<Integer> list = Arrays.asList(1,2,2,3,4,5,5,5);
    list.stream().limit(3).collect(Collectors.toList());
    

    skip:跳过

    // 跳过前4个元素,并生成新的集合
    List<Integer> list = Arrays.asList(1,2,2,3,4,5,5,5);
    list.stream().skip(4).collect(Collectors.toList());
    
  • 映射

    map:接受一个函数作为参数,并将其映射成一个新的元素

    // 给定一个Integer的集合,使用map映射为由每个数的平方构成的String集合
    List<Integer> list = Arrays.asList(1,2,3,4,5);
    List<String> list2= 
      list.stream().map((Integer i)->i*i+"").collect(Collectors.toList());
    

    flatMap :将流合并为单个流

    // 给定两个数字列表,返回所有的数对 结果1,3 1,4 2,3 2,4 3,3 3,4
    // 例中使用了两个流,用flatMap合并为了单个流
    List<Integer> list1 = Arrays.asList(1,2,3);
    List<Integer> list2 = Arrays.asList(3,4);
    List<int[]> list3 = 
      list1.stream()
                  .flatMap((Integer i)->list2.stream()
                             .map((Integer j)->new int[]{i,j}))
                  .collect(Collectors.toList());
    
  • 查找和匹配

    anyMatch:流中是否有一个元素能匹配给定的谓词

    // 若集合中存在任一元素等于8,返回true,否则返回false
    List<Integer> list = Arrays.asList(1,2,2,3,4,5,5,5);
    Boolean flag = list.stream().anyMatch((Integer a)->a==8);
    

    allMatch:检查谓词是否匹配所有元素

    noneMatch:它可以确保流中没有任何元素与给定的谓词匹配

    findAny:方法将返回当前流中的任意元素

    // 返回任一元素,结果为Optional<T>类型的值,防止null错误
    List<Integer> list = Arrays.asList(1,2,2,3,4,5,5,5);
    Integer i = list.stream().findAny();
    

    findFirst:查找第一个元素。

    <u>找到第一个元素在并行上限制更多。如果你不关心返回的元素是哪个,请使用findAny,因为它在使用并行流 时限制较少。</u>

  • 归约

    reduce:用于求和、最大最小值等规约操作。

    reduce接受两个参数: 一个初始值,这里是0; 一个BinaryOperator<T>来将两个元素结合起来产生一个新值

    // 求和
    List<Integer> numbers = Arrays.asList(1,2,2,3,4,5,5,5);
    int sum = numbers.stream().reduce(0, (a, b) -> a + b);
    // 或者使用已有的sum、min、max方法用来求和、获取最大、最小值 。例:
    numbers.stream().reduce(Integer::min); 
    
  • 数值流

    stream.mapToInt() 可以将普通流转为数值流

    intStream.boxed() 将数值流转为普通流

    <u>数值流使用range和rangeClosed方法可以生成指定范围内的流。这两个方法都是第一个参数接受起始值,第二个参数接受结束值。但 range是不包含结束值的。</u>

流收集器

我们在之前提到了collect,其作用是规约,将流中的元素使用Collector累积成一个汇总结果。

Collectors.counting()汇总数量

Collectors.maxBy() 获取最大值

例:

// 获取菜单列表中卡路里数值最大的菜。
Comparator<Dish> dishCaloriesComparator=Comparator.comparingInt(Dish::getCalories);
Optional<Dish> mostCalorieDish = menu.stream().collect(maxBy(dishCaloriesComparator));

Collectors.summingLong和Collectors.summingDouble 这两个方法用于求和,可以用于求和字段为long或double的情况。

Collectors.averagingInt(),连同对应的averagingLong和 averagingDouble 用于计算数值的平均数

万能方法:Collectors.summarizing(),可以获取元素个数,总和,平均值、最大值、最小值。

Collectors.joining() 内部使用了StringBuilder来把生成的字符串逐个追加起来.

Collectors.reducing() 接受一个BinaryOperator<t>参数,也就是一个 BiFunction<T,T,T>。这就意味着它需要的函数必须能接受两个参数,然后返回一个相同类型的值。


Collectors.groupingBy() 分组

// 根据卡路里进行分组,返回一个Map<CaloricLevel, List<Dish>> 类型的结果
public enum CaloricLevel { DIET, NORMAL, FAT }
Map<CaloricLevel, List<Dish>> dishesByCaloricLevel 
= menu.stream().collect( groupingBy(dish -> { 
               if (dish.getCalories() <= 400) return CaloricLevel.DIET;
               else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
                        else return CaloricLevel.FAT;
                        }));
// 多级分组,根据类型分组,再统计数量,返回结果Map: {MEAT=3, FISH=2, OTHER=4}
Map<Dish.Type,Map<CaloricLevel,List<Dish>>> map = menu.stream()
  .collect(Collectors.groupingBy(Dish::getType,Collectors.groupingBy((Dish d)->{
            if (d.getCalories()<400){return CaloricLevel.DIET;}
                    else {return CaloricLevel.FAT;}
            })));
/**
普通的单参数groupingBy(f)(其中f是分类函数)实际上是groupingBy(f, toList())的简便写法。
*/

Collectors.partitioningBy() 分区

// 区分出素食与非素食
menu.stream().collect(partitioningBy(Dish::isVegetarian))

并行流

<u>并行流就是一个把内容分成多个数据块,并用不同的线程分别处理每个数据块的流。这样一来,你就可以自动把给定操作的工作负荷分配给多核处理器的所有内核。</u>

Stream.parallel() 顺序流转为并行流

Stream.sequential () 并行流转为顺序流

在使用并行流时,要避免共享可变状态,确保并行Stream得到正确的结果。

并行流内部使用了默认的ForkJoinPool ,它默认的 线程数量就是你的处理器数量 ,可 以 通 过 系 统 属 性 java.util.concurrent.ForkJoinPool.common. parallelism来改变线程池大小 ,System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism","12");

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

推荐阅读更多精彩内容