Stream-java8新特性

简介

Collectors

  1. 将数据收集进一个列表
Collectors.toList();
Collectors.toSet();
  1. 将数据收集进一个集合
Collectors.toMap();
  1. 用自定义的实现Collection的数据结构收集
Collectors.toCollection();
  1. 集合元素拼接
Collectors.Joining();

collectingAndThen(); //  收集之后继续做一些处理。
        .()中一般是Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(AssetInfo::getNumber)))
    public static class HeaderDto implements Serializable {

        private static final long serialVersionUID = 89423674447742L;

        private String version;

        @JSONField(name = "sys-code")
        private String sysCode;

        @JSONField(name = "biz-id")
        private String bizId;

        @JSONField(name = "biz-type")
        private String bizType;

        private Long timestamp;

        private String sign;

        public String getVersion() {
            return version;
        }

        public void setVersion(String version) {
            this.version = version;
        }

        public String getSysCode() {
            return sysCode;
        }

        public void setSysCode(String sysCode) {
            this.sysCode = sysCode;
        }

        public String getBizId() {
            return bizId;
        }

        public void setBizId(String bizId) {
            this.bizId = bizId;
        }

        public String getBizType() {
            return bizType;
        }

        public void setBizType(String bizType) {
            this.bizType = bizType;
        }

        public Long getTimestamp() {
            return timestamp;
        }

        public void setTimestamp(Long timestamp) {
            this.timestamp = timestamp;
        }

        public String getSign() {
            return sign;
        }

        public void setSign(String sign) {
            this.sign = sign;
        }
    }


    public static void main(String[] args) {
        List<HeaderDto> list = new ArrayList<>();
        HeaderDto dto1 = new HeaderDto();
        dto1.setBizId("01");
        dto1.setBizType("机构A");

        HeaderDto dto2 = new HeaderDto();
        dto2.setBizId("01");
        dto2.setBizType("机构A");

        HeaderDto dto3 = new HeaderDto();
        dto3.setBizId("01");
        dto3.setBizType("机构B");

        HeaderDto dto4 = new HeaderDto();
        dto4.setBizId("02");
        dto4.setBizType("机构C");

        list.add(dto1);
        list.add(dto2);
        list.add(dto3);
        list.add(dto4);

        // 分组后, 将value去重后拼接成 字符串,两个元素之间用 , 分割
        Map<String, String> filter = list.stream()
                .collect(Collectors.groupingBy(HeaderDto::getBizId,
                        Collectors.mapping(HeaderDto::getBizType,
                                Collectors.collectingAndThen(Collectors.toSet(), e -> String.join(",", e)))));

// {01=机构A,机构B, 02=机构C}
        System.out.println(filter);
    }
  1. 元素聚合
maxBy,
minBy,
summingInt,
averagingDouble

mapping
  1. 分组
Map<Long, List<String>> collect = list.stream().collect(
		Collectors.toMap(SettleRegisterInfo::getId, 
						item -> Arrays.asList(item.getChannel().split(","))));
  1. 累计操作
sum

示例

一、collect

collect 是一个终端操作,它接收的参数是将流中的元素累积到汇总结果的各种方式(称为收集器)

二、预定义收集器

包括将流元素归约和汇总到一个值.如下:

工厂方法返回类型用于示例
toListList < T >把流中所有元素收集到List中List< Menu > menus=Menu.getMenus.stream().collect(Collectors.toList()) ;
toSetSet< T >把流中所有元素收集到Set中,删除重复项Set< Menu > menus=Menu.getMenus.stream().collect(Collectors.toSet());
toCollectionCollection< T >把流中所有元素收集到给定的供应源创建的集合中ArrayList< Menu > menus=Menu.getMenus.stream().collect(Collectors.toCollection(ArrayList::new));
countingLong计算流中元素个数Long count=Menu.getMenus.stream().collect(counting);
SummingIntInteger对流中元素的一个整数属性求和Integer count=Menu.getMenus.stream().collect(summingInt(Menu::getCalories));
averagingIntDouble计算流中元素integer属性的平均值Double averaging=Menu.getMenus.stream().collect(averagingInt(Menu::getCalories));
JoiningString连接流中每个元素的toString方法生成的字符串String name=Menu.getMenus.stream().map(Menu::getName).collect(joining(“, ”));
maxByOptional< T >一个包裹了流中按照给定比较器选出的最大元素的optional,如果为空返回的是Optional.empty();Optional< Menu > fattest=Menu.getMenus.stream().collect(maxBy(Menu::getCalories))
minByOptional< T >一个包裹了流中按照给定比较器选出的最大元素的optional,如果为空返回的是Optional.empty();Optional< Menu > lessest=Menu.getMenus.stream().collect(minBy(Menu::getCalories));
Reducing归约操作产生的类型从一个作为累加器的初始值开始,利用binaryOperator与流中的元素逐个结合,从而将流归约为单个值int count=Menu.getMenus.stream().collect(reducing(0,Menu::getCalories,Integer::sum));
collectingAndThen转换函数返回的类型包裹另一个转换器,对其结果应用转换函数Int count=Menu.getMenus.stream().collect(collectingAndThen(toList(),List::size))
groupingByMap<K,List< T >>根据流中元素的某个值对流中的元素进行分组,并将属性值做为结果map的键Map<Type,List< Menu >> menuType=Menu.getMenus.stream().collect(groupingby(Menu::getType));
partitioningByMap<Boolean,List< T >>根据流中每个元素应用谓语的结果来对项目进行分区Map<Boolean,List< Menu >> menuType=Menu.getMenus.stream().collect(partitioningBy(Menu::isType));

三,预定义收集器可以用groupby对流中元素进行分组或者用partitioningBy进行分区

四,收集器可以高效的复合起来,进行多级分组,多级分区和归约

五,可以自己实现collector接口进行定义自己的收集器

具体参考代码

List<AssetSnUpdateData> result = List<AssetSnUpdateData>.stream().filter(s -> snList.contains(s.getAssetSn())).collect(Collectors.toList()); 


Map<Long, List<String>> collect = list.stream()
                                .collect(Collectors.toMap(SettleRegisterInfo::getId, item -> Arrays.asList(item.getChannel().split(","))));


list.stream.collect(Collectors.toMap(e -> e, e -> 1, (a, b) -> a + b)) // 获得元素出现频率的 Map,键为元素,值为元素出现的次数
	.entrySet().stream() // Set<Entry>转换为Stream<Entry>
	.filter(entry -> entry.getValue() > 1) // 过滤出元素出现次数大于 1 的 entry
	.map(entry -> entry.getKey()) // 获得 entry 的键(重复元素)对应的 Stream
	.collect(Collectors.toList()); // 转化为 List


public static void main(String[] args) {
        List<AssetInfo> associatedAssetInfoList = new ArrayList<>();
        AssetInfo assetInfo = new AssetInfo();
        assetInfo.setNumber("001");
        assetInfo.setOldNumber("001001");
        AssetInfo assetInfo1 = new AssetInfo();
        assetInfo1.setNumber("001");
        assetInfo1.setOldNumber("001002");
        AssetInfo assetInfo2 = new AssetInfo();
        assetInfo2.setNumber("002");
        assetInfo2.setOldNumber("001001");
        AssetInfo assetInfo3 = new AssetInfo();
        assetInfo3.setNumber("003");
        assetInfo3.setOldNumber("001001");
        associatedAssetInfoList.add(assetInfo);
        associatedAssetInfoList.add(assetInfo1);
        associatedAssetInfoList.add(assetInfo2);
        associatedAssetInfoList.add(assetInfo3);

        if (!CollectionUtils.isEmpty(associatedAssetInfoList)) {
            // todo: 根据资产编号去重
            associatedAssetInfoList = associatedAssetInfoList.stream().collect(
                    Collectors.collectingAndThen(
                        Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(AssetInfo::getNumber))),
                            ArrayList::new)
            );
        }
        associatedAssetInfoList.forEach(e -> System.out.println(e));

        // 1-todo: 根据资产的编号和旧资产编号进行分组,即多属性进行分组
        Map<List<String>, List<AssetInfo>> collect = associatedAssetInfoList.stream()
                .collect(Collectors.groupingBy(f -> Arrays.asList(f.getNumber(),f.getOldNumber())));

        // 1-todo: 实现原理不同,上面的是直接根据多个属性进行分组,下面是先根据number分组,再根据oldNumber分组,最终结果是map嵌套map
        Map<String, Map<String, List<AssetInfo>>> collect = associatedAssetInfoList.stream()
                .collect(Collectors.groupingBy(AssetInfo::getNumber, Collectors.groupingBy(AssetInfo::getOldNumber)));

        // 2-map收集资产分类 -> 去重后 -> 收集后转换为集合
        List<String> typeList = list.stream().map(TypeNameModelImportData::getTypeName).distinct()
                .collect(Collectors.toList()); // map收集资产分类 -> 去重后 -> 收集后转换为集合
        
        // 2-map收集资产分类的名称 -> 取set集合方式来去重
        Set<String> typeSet = valueList.stream().map(TypeNameModelImportData::getTypeName)
                        .collect(Collectors.toSet()); // map收集资产分类的名称 -> 去重 取set集合
        
        Map<String, Long> map = p2pIdList.stream().filter(Objects::nonNull).collect(
                Collectors.groupingBy(p2pId -> p2pId, Collectors.counting())); // todo: 过滤器 Objects::nonNull 过滤掉null值 -> 根据p2pId分组
        
        Map<String, Integer> assetNameKV = allNameList.stream()
                .collect(Collectors.toMap(SysAssetName::getName, SysAssetName::getId)); // excel中所有的资产名称对应的 name-id        

    }


java7-8的代码写法变化

在 Java 7 中,如果要发现 type 为 grocery 的所有交易,然后返回以交易值降序排序好的交易 ID 集合,我们需要这样写:

清单 1. Java 7 的排序、取值实现


List<Transaction> groceryTransactions = new Arraylist<>();
for(Transaction t: transactions){
 	if(t.getType() == Transaction.GROCERY){
 		groceryTransactions.add(t);
 	}
}

	
Collections.sort(groceryTransactions, new Comparator(){
 	public int compare(Transaction t1, Transaction t2){
 		return t2.getValue().compareTo(t1.getValue());
 	}
});


List<Integer> transactionIds = new ArrayList<>();
for(Transaction t: groceryTransactions){
 	transactionsIds.add(t.getId());
}

而在 Java 8 使用 Stream,代码更加简洁易读;而且使用并发模式,程序执行速度更快。

清单 2. Java 8 的排序、取值实现


List<Integer> transactionsIds = transactions.parallelStream()
	.filter(t -> t.getType() == Transaction.GROCERY)
	.sorted(comparing(Transaction::getValue).reversed())
	.map(Transaction::getId).collect(toList());

reduce:它提供一个起始值(种子),然后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合。

字符串拼接、数值的 sum、min、max、average 都是特殊的 reduce。

例如 Stream 的 sum 就相当于


Integer sum = integers.reduce(0, (a, b) -> a+b); 

// 或

Integer sum = integers.reduce(0, Integer::sum);

清单 3. reduce 的用例


// 字符串连接,concat = "ABCD"
String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat); 

// 求最小值,minValue = -3.0
double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min); 

// 求和,sumValue = 10, 有起始值
int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);

// 求和,sumValue = 10, 无起始值
sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();

// 过滤,字符串连接,concat = "ace"
concat = Stream.of("a", "B", "c", "D", "e", "F")
		.filter(x -> x.compareTo("Z") > 0).reduce("", String::concat);

清单 15: 上面代码例如第一个示例的 reduce(),第一个参数(空白字符)即为起始值,第二个参数(String::concat)为 BinaryOperator。
这类有起始值的 reduce() 都返回具体的对象。而对于第四个示例没有起始值的 reduce(),
由于可能没有足够的元素,返回的是 Optional,请留意这个区别。

延伸

Java8中Collectors工具类中得partitioningBy与groupingBy区别
https://blog.youkuaiyun.com/qq_28336067/article/details/79215512
partitioningBy和groupingBy都是用于将数据进行分组的函数。
两者的区别要从函数的签名看起。
partitioningBy函数的定义如下


public static <T>
    Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) {
        return partitioningBy(predicate, toList());
    }

可以看出函数的参数一个Predicate接口,那么这个接口的返回值是boolean类型的,也只能是boolean类型,然后他的返回值是Map的key是boolean类型,也就是这个函数的返回值只能将数据分为两组也就是ture和false两组数据。

groupingBy函数的定义,仅查看与partitioningBy参数相同的定义


public static <T, K> Collector<T, ?, Map<K, List<T>>>
    groupingBy(Function<? super T, ? extends K> classifier) {
        return groupingBy(classifier, toList());
    }

groupingBy的函数参数为Function然后他的返回值也是Map,但是他的key是泛型,那么这个分组就会将数据分组成多个key的形式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值