问题引入

如果你在做一些汇总操作,比如

1、对一个交易列表按货币分组,获取每种货币的和(Map)

2、将交易分成贵的、不贵的(Map>)

3、多级分组,按城市分组,再按贵和不贵分组

如果是传统的写法,使用外部迭代即可,会有很多for+if组合,类似:

private static voidgroupImperatively() {

Map> transactionsByCurrencies = new HashMap<>();for(Transaction transaction : transactions) {

Currency currency=transaction.getCurrency();

List transactionsForCurrency =transactionsByCurrencies.get(currency);if (transactionsForCurrency == null) {

transactionsForCurrency= new ArrayList<>();

transactionsByCurrencies.put(currency, transactionsForCurrency);

}

transactionsForCurrency.add(transaction);

}

System.out.println(transactionsByCurrencies);

}

而使用Stream,可以用Collectors收集器:

Map> transactionsByCurrencies = transactions.stream().collect(groupingBy(Transaction::getCurrency));

这个类提供很多工厂方法,主要分3类

1、规约、汇总;

2、分组

3、分区

使用数据

import java.util.*;public classDish {private finalString name;private final booleanvegetarian;private final intcalories;private finalType type;public Dish(String name, boolean vegetarian, intcalories, Type type) {this.name =name;this.vegetarian =vegetarian;this.calories =calories;this.type =type;

}publicString getName() {returnname;

}public booleanisVegetarian() {returnvegetarian;

}public intgetCalories() {returncalories;

}publicType getType() {returntype;

}public enumType { MEAT, FISH, OTHER }

@OverridepublicString toString() {returnname;

}public static final List 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, 400, Dish.Type.FISH),new Dish("salmon", false, 450, Dish.Type.FISH));

}

规约汇总

先统计之前例子里的数据,统计一共有多少菜。

menu.stream().collect(counting());

找出卡路里的最大和最小值

private staticDish findMostCaloricDishUsingComparator() {

Comparator dishCaloriesComparator =Comparator.comparingInt(Dish::getCalories);

BinaryOperator moreCaloricOf =BinaryOperator.maxBy(dishCaloriesComparator);returnmenu.stream().collect(reducing(moreCaloricOf)).get();

}

也可以用reduce来取最大最小值,推荐用法

private staticDish findMostCaloricDish() {return menu.stream().collect(reducing((d1, d2) -> d1.getCalories() > d2.getCalories() ?d1 : d2)).get();

}

汇总

private static intcalculateTotalCalories() {returnmenu.stream().collect(summingInt(Dish::getCalories));

}

平均数

private staticDouble calculateAverageCalories() {returnmenu.stream().collect(averagingInt(Dish::getCalories));

}

一次性获取最大、最小、平均、和

private staticIntSummaryStatistics calculateMenuStatistics() {returnmenu.stream().collect(summarizingInt(Dish::getCalories));

}

结果

Menu statistics: IntSummaryStatistics{count=9, sum=4300, min=120, average=477.777778, max=800}

连接字符串

private staticString getShortMenu() {returnmenu.stream().map(Dish::getName).collect(joining());

}private staticString getShortMenuCommaSeparated() {return menu.stream().map(Dish::getName).collect(joining(", "));

}

广义规约reduce

以上的写法都是通过reduce来实现的,统统可以用reduce来写,比如总计

//总计,Lambda方式

private static intcalculateTotalCalories() {return menu.stream().collect(reducing(0, Dish::getCalories, (Integer i, Integer j) -> i +j));

}//使用方法引用来总计

private static intcalculateTotalCaloriesWithMethodReference() {return menu.stream().collect(reducing(0, Dish::getCalories, Integer::sum));

}//不用Collectors的汇总

private static intcalculateTotalCaloriesWithoutCollectors() {returnmenu.stream().map(Dish::getCalories).reduce(Integer::sum).get();

}//IntStream方式

private static intcalculateTotalCaloriesUsingSum() {returnmenu.stream().mapToInt(Dish::getCalories).sum();

}

以上的方式,IntStream最好,一是比较直观,二是没有Integer的装箱,性能最佳。

分组groupingBy

也叫分类,使用groupingBy方法,参数是Function方法引用,也是分类函数,分组的输出一个map,key就是类型

定义:

public static Collector>>groupingBy(Function super T, ? extends K>classifier) {returngroupingBy(classifier, toList());

}public static Collector> groupingBy(Function super T, ? extends K>classifier,

Collector super T, A, D>downstream) {return groupingBy(classifier, HashMap::new, downstream);

}

例子

//单层分类

private static Map>groupDishesByType() {returnmenu.stream().collect(groupingBy(Dish::getType));

}//单层自定义分类

private static Map>groupDishesByCaloricLevel() {returnmenu.stream().collect(

groupingBy(dish->{if (dish.getCalories() <= 400) returnCaloricLevel.DIET;else if (dish.getCalories() <= 700) returnCaloricLevel.NORMAL;else returnCaloricLevel.FAT;

} ));

}//2层分类,第一层是类型,第二层是卡路里级别

private static Map>>groupDishedByTypeAndCaloricLevel() {returnmenu.stream().collect(

groupingBy(Dish::getType,

groupingBy((Dish dish)->{if (dish.getCalories() <= 400) returnCaloricLevel.DIET;else if (dish.getCalories() <= 700) returnCaloricLevel.NORMAL;else returnCaloricLevel.FAT;

} )

)

);

}//子分组计数

private static MapcountDishesInGroups() {returnmenu.stream().collect(groupingBy(Dish::getType, counting()));

}//子分组取最大值

private static Map>mostCaloricDishesByType() {returnmenu.stream().collect(

groupingBy(Dish::getType,

reducing((Dish d1, Dish d2)-> d1.getCalories() > d2.getCalories() ?d1 : d2)));

}//不使用Optional

private static MapmostCaloricDishesByTypeWithoutOprionals() {returnmenu.stream().collect(

groupingBy(Dish::getType,

collectingAndThen(

reducing((d1, d2)-> d1.getCalories() > d2.getCalories() ?d1 : d2),

Optional::get)));

}//子组汇总

private static MapsumCaloriesByType() {returnmenu.stream().collect(groupingBy(Dish::getType,

summingInt(Dish::getCalories)));

}//分组自定义转换

private static Map>caloricLevelsByType() {returnmenu.stream().collect(

groupingBy(Dish::getType, mapping(

dish-> { if (dish.getCalories() <= 400) returnCaloricLevel.DIET;else if (dish.getCalories() <= 700) returnCaloricLevel.NORMAL;else returnCaloricLevel.FAT; },

toSet() )));

}

分区partitioningBy

分区就是区分"是"or"非"的分组,分区里可以嵌套分组,定义

private static Map>partitionByVegeterian() {returnmenu.stream().collect(partitioningBy(Dish::isVegetarian));

}private static Map>>vegetarianDishesByType() {returnmenu.stream().collect(partitioningBy(Dish::isVegetarian, groupingBy(Dish::getType)));

}private staticObject mostCaloricPartitionedByVegetarian() {returnmenu.stream().collect(

partitioningBy(Dish::isVegetarian,

collectingAndThen(

maxBy(comparingInt(Dish::getCalories)),

Optional::get)));

}

结果:

Dishes partitioned by vegetarian: {false=[pork, beef, chicken, prawns, salmon], true=[french fries, rice, season fruit, pizza]}

Vegetarian Dishes by type: {false={FISH=[prawns, salmon], MEAT=[pork, beef, chicken]}, true={OTHER=[french fries, rice, season fruit, pizza]}}

Most caloric dishes by vegetarian: {false=pork, true=pizza}

分区的例子,求质数(素数)

即对于大于1的数,如果除了1和它本身,它不能再被其它正整数整除,那么我们说它是一个质数。

传统的判断一个数是不是质数的写法:

public static boolean isPrimeNormal(intnum) {for(int i=2; i

}

}return true;

}//优化的算法是只测试待测数平方根以下:

private static boolean isPrime(intsrc) {double sqrt =Math.sqrt(src);if (src < 2) {return false;

}if (src == 2 || src == 3) {return true;

}if (src % 2 == 0) {//先判断是否为偶数,若偶数就直接结束程序

return false;

}for (int i = 3; i <= sqrt; i+=2) {if (src % i == 0) {return false;

}

}return true;

}

Stream写法:

public static Map> partitionPrimes(intn) {return IntStream.rangeClosed(2, n).boxed()

.collect(partitioningBy(candidate->isPrime(candidate)));

}public static boolean isPrime(intcandidate) {return IntStream.rangeClosed(2, candidate-1)

.limit((long) Math.floor(Math.sqrt((double) candidate)) - 1)

.noneMatch(i-> candidate % i == 0);

}

结果

{false=[4, 6, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 22, 24, 25, 26, 27, 28, 30, 32, 33, 34, 35, 36, 38, 39, 40, 42, 44, 45, 46, 48, 49, 50, 51, 52, 54, 55, 56, 57, 58, 60, 62, 63, 64, 65, 66, 68, 69, 70, 72, 74, 75, 76, 77, 78, 80, 81, 82, 84, 85, 86, 87, 88, 90, 91, 92, 93, 94, 95, 96, 98, 99, 100], true=[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]}

Map转换ToMap

ToMap 收集器用于收集流元素至 Map 实例,实现该功能需要提供两个函数:

keyMapper

valueMapper

keyMapper 用于从流元素中抽取map 的key,valueMapper抽取与可以关联的value。定义:

Collector> toMap(Function super T, ? extends K>keyMapper,

Function super T, ? extends U> valueMapper)

下面示例收集流元素至Map中,存储字符串作为key,其长度作为value:

List bookList = new ArrayList<>();

bookList.add(new Book("The Fellowship of the Ring", 1954, "0395489318"));

bookList.add(new Book("The Two Towers", 1954, "0345339711"));

bookList.add(new Book("The Return of the King", 1955, "0618129111"));public Map listToMap(Listbooks) {returnbooks.stream().collect(Collectors.toMap(Book::getIsbn, Book::getName));

}

Function.identity() 是一个预定义的返回接收参数的快捷函数。

如果我们集合中包含重复元素会怎么样?与toSet相反,toMap不能过滤重复元素。这个比较好理解————其如何确定key关联那个value?

List listWithDuplicates = Arrays.asList("a", "bb", "c", "d", "bb");

assertThatThrownBy(() -> {

listWithDuplicates.stream().collect(toMap(Function.identity(), String::length));

}).isInstanceOf(IllegalStateException.class);

我们看到,toMap甚至不判断值是否相等,如果key重复,立刻抛出IllegalStateException异常。要解决这个问题,我们需要使用另一种方法和附加参数mergefunction:

Collector toMap(Function super T, ? extends K>keyMapper,

Function super T, ? extends U>valueMapper,

BinaryOperator mergeFunction)

BinaryOperator是合并函数

实验:

public Map listToMapWithDupKey(Listbooks) {returnbooks.stream().collect(Collectors.toMap(Book::getReleaseYear, Function.identity(),

(existing, replacement)->existing));

}

java软件任务分组管理_了不起的Java-Stream流规约、分组、分区相关推荐

  1. Java复习第11天---11.4---Java8新特性---Stream流常用方法3和综合案例

    Java复习第11天---11.4---Java8新特性---Stream流常用方法3和综合案例 目录 文章目录 1.count:计数-终结方法 2.limit:取前几个元素-延迟方法 3.skip: ...

  2. Java复习第11天---11.3---Java8新特性---Stream流特点和常用方法2

    Java复习第11天---11.3---Java8新特性---Stream流特点和常用方法2 目录 文章目录 1.Stream流特点 2.filter:过滤 3.map:映射 ***后记*** : 内 ...

  3. Java复习第11天---11.2---Java8新特性---Stream流获取方式和常用方法1

    Java复习第11天---11.2---Java8新特性---Stream流获取方式和常用方法1 目录 文章目录 1.Stream流的2中获取方式 1.1.集合的stream方法 1.2.Stream ...

  4. 手机的java软件有哪些功能_手机有哪些软件可以练习JAVA?

    这里介绍一个手机软件-AIDE,相当于手机上的Java集成开发环境,可以直接编辑运行Java程序,除此之外还可以开发简单的安卓应用和手机小游戏,下面我简单介绍一下这个软件的安装和使用: 1.首先,下载 ...

  5. java策略管理_详解Java编程中的策略模式

    策略模式属于对象的行为模式.其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换.策略模式使得算法可以在不影响到客户端的情况下发生变化. 策略模式的结构 策略模式 ...

  6. java 发送短信 多通道_一种Java卡多通道临时对象管理方法与流程

    本发明涉及Java智能卡领域,具体涉及一种Java卡多通道临时对象管理方法. 背景技术: :JavaCard规范支持逻辑通道的概念,允许最多智能卡中的16个应用程序会话同时开启,每个逻辑通道一个会话. ...

  7. java制作一个应用程序_一个制作java小应用程序的全过程

    一个制作java小应用程序的全过程 一.安装java软件: 从网上下载jdk-7u25-windows-i586.exe,安装到C:\Program Files\Java\jdk1.7.0_25. 二 ...

  8. 深入java虚拟机 第四版_深入理解Java虚拟机-常用vm参数分析

    Java虚拟机深入理解系列全部文章更新中... https://blog.ouyangsihai.cn/shen-ru-li-jie-java-xu-ni-ji-java-nei-cun-qu-yu- ...

  9. java方法区内存泄露_深入理解java虚拟机-第二章:java内存区域与内存泄露异常...

    2.1概述: java将内存的管理(主要是回收工作),交由jvm管理,确实很省事,但是一点jvm因内存出现问题,排查起来将会很困难,为了能够成为独当一面的大牛呢,自然要了解vm是怎么去使用内存的. 2 ...

最新文章

  1. 取input 输入_tensorRT动态输入(python)
  2. X1000应用程序打包流程
  3. ITK:创建一个后向差分运算符
  4. hihoCoder 1052 基因工程 最详细的解题报告
  5. 工业物联网发展环境加速形成 中国企业如何突围?
  6. CSS3之border
  7. 软件测试知识产权保护,一种软件测试方法及软件测试系统专利_专利申请于2017-09-07_专利查询 - 天眼查...
  8. 互联网晚报 | 3月8日 星期二 |​ ​​沪指深V反弹,创业板指跌幅收窄至0.75%;腾讯低代码平台与微信开发者平台打通...
  9. 主要省份城市的DNS服务器地址
  10. 软件管理理论—目标管理 SMART 原则
  11. oracle财务系统表,Oracle ERP 财务模块表结构.ppt
  12. 热敏电阻温度采集简述
  13. 数字图像处理复习总结
  14. 2022年备考[嵌入式系统设计师]你准备好了吗?
  15. 360一键root工具 v5.1.3 pc绿色版
  16. 360校招真题——最强大脑
  17. vehicle架构的想法
  18. 记录:使用DJANGO从0开始搭建微信公众平台(0)
  19. 怎么把两个PDF合并成一个?这几种操作轻松合并
  20. java中12 5.0f等于多少_F_____

热门文章

  1. 火影忍者疾风传720集视频
  2. jenkins安装笔记(一)
  3. 同事有这4个“表现”,你要高看一眼,主动结交,此人不简单
  4. 数据分析思维模式(上)
  5. 爬虫—新浪微博(登陆访问、cookie访问)
  6. 混合整数二阶锥规划公式中具有不同动力学的定价惯性和频率响应
  7. 2021 腾讯游戏客户端暑期实习 一面
  8. 计算机启动键盘无法使用,win7开机键盘不能用如何解决_windows7开机键盘无法使用解决教程...
  9. Java中高级面试题及答案收集(三)
  10. Linux服务器SSL证书认证