Lambda--Optional、Collectors高級進階方法


Lambda--Collectors、optional高級使用

偶然看到了同事groupingBy用法,然后百度衍生出了optional,collectors,map等各種用法。突然發現自己之前寫的代碼又爛又復雜了,后面用optional可以防止空指針,collectors也可以極大簡化代碼量。

csdn csdn csdn csdn csdn


@

🔥1.Optional

optional里面有防止空指針的語句,但我覺得有些時候報空指針錯更方便定位到問題。比如從庫里查找出數據,沒找到但是你初始化了一個默認值做了后續處理並返回給前端展示了,這其實就算是BUG了,當然有些時候避免空指針報錯也是非常不錯的選擇。

1.1.Optional--基本方法
//of會報空指針,ofNullable不會(可以防止空指針),而是重新new個optional
Optional.of()
Optional.ofNullable
    
//user不為空即輸出 
Optional.ofNullable(user).ifPresent(System.out::println); 

//user為空則重新new一個
Optional.ofNullable(user).orElse(User.builder().build());

//user為空則報空指針錯誤(雖然ofNullable里面new了optional,但user依然會為空)
Optional.ofNullable(user).orElseThrow(() -> new NullPointerException());    

//user為空則重新new一個
Optional.ofNullable(user).orElseGet(() -> User.builder().build());

//初始化list數組
List<String> list = null;
List<String> newList = Optional.ofNullable(list).orElse(Lists.newArrayList());
    
1.2.Optional--filter、map

Optional中可以像stream流一樣操作數據

/**
 * @program: springboot
 * @description:
 * @author: huyuqiao
 * @create: 2021/08/07 10:57
 */
@Data
@Builder(toBuilder = true)
public class UserInfo {
    private String name;
    private String email;
    private Integer price;
}



@Test
public void optionalTest(){
    // Optional--操作對象:filter
    UserInfo userInfo = UserInfo.builder()
        .name("huyuqiao")
        .email("1842449680@qq.com")
        .price(100)
        .build();
    UserInfo newUserInfo = Optional.ofNullable(userInfo).filter(u -> u.getPrice() < 1000).orElse(UserInfo.builder().build());
    System.out.println(newUserInfo);

    // Optional--操作對象:map
    String name = Optional.ofNullable(userInfo).map(UserInfo::getName).orElse("自定義姓名");
    System.out.println(name);

    // Optional--操作對象:flatmap
    Optional.ofNullable(userInfo).flatMap(u -> Optional.ofNullable(u.getName()));
}

1.2.Optional--flatMap

​ flatMap:扁平化獲取層級下的元素並組裝成同一層級。

​ 比如現有數組[[1],[2],[3]],要提取里面所有元素並組裝成[1,2,3],可利用flatMap獲取

    @Test
    public void flatMapTest(){
        List<List<Integer>> outer = new ArrayList<>();
        List<Integer> inner1 = new ArrayList<>();
        inner1.add(1);
        inner1.add(9);
        List<Integer> inner2 = new ArrayList<>();
        inner2.add(2);
        List<Integer> inner3 = new ArrayList<>();
        inner3.add(3);
        List<Integer> inner4 = new ArrayList<>();
        inner4.add(4);
        List<Integer> inner5 = new ArrayList<>();
        inner5.add(5);
        outer.add(inner1);
        outer.add(inner2);
        outer.add(inner3);
        outer.add(inner4);
        outer.add(inner5);
        List<Integer> result = outer.stream().flatMap(inner -> inner.stream().map(i -> i + 1)).collect(Collectors.toList());
        System.out.println(result);
    }
//[2, 10, 3, 4, 5, 6]

🔥2.Collectors

會使用了optional基本語句后,后面就可以開始使用Collectors語句了,個人感覺對開發非常實用,可以極大簡化代碼開發量。先給出基本類,然后就可以使用了

/**
 * @program: springboot
 * @description:
 * @author: huyuqiao
 * @create: 2021/08/06 10:10
 */

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 學生信息
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    /** 姓名 */
    private String name;
    /** 總分 */
    private int totalScore;
    /** 是否本地人 */
    private boolean local;
    /** 年級 */
    private GradeType gradeType;

    /**
     * 年級類型
     */
    public enum GradeType {ONE,TWO,THREE}
}
2.1.averagingDouble--計算平均值

注意: double格式可以表示-253到253范圍內的所有連續整數。如果管道有超過253的值,則平均計算中的除數將在253處飽和,從而導致額外的數值誤差。(所以計算數多時需要專用BigDecimal進行計算)

 	//統計所有學生的平均總成績
	@Test
    public void averagingDoubleTest(){
        Double avgScore = menu.stream().collect(Collectors.averagingDouble(Student::getTotalScore));
        Optional.ofNullable(avgScore).ifPresent(System.out::println);
    }
2.2.collectingAndThen--轉換類型

collectingAndThen方法:調整Collector收集器以執行其它的結束轉換

       @Test
    public void collectingAndThenTest(){
        // unmodifiablelist:不能add,set。arrays.aslist可以set,但是不能add
        List<Student> studentList = menu.stream().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
        System.out.println(studentList);

        //collectingandthen:計算總平均值並轉換成自定義字符串返回
        Optional.ofNullable(menu.stream().collect(
                Collectors.collectingAndThen(Collectors.averagingInt(Student::getTotalScore), a -> "the average totalscore is" + a)
        )).ifPresent(System.out::println);

    }
2.3.counting--類似count()

counting方法:統計collectors收集器元素

    @Test
    public void countingTest(){
        Optional.of(menu.stream().collect(Collectors.counting())).ifPresent(System.out::println);
    }
// 7
2.4.groupingBy*、groupingByConcurrent--分組(重點)

groupingBy方法:分組並返回到map中(個人感覺非常好用,對商品進行分組返回給前端,大大簡化了代碼量)。

groupingByConcurrent:並發分組,無法保證返回數據類型的順序或線程安全性

    //按對象某一屬性進行分組
    @Test
    public void groupByTest(){
        Map<Student.GradeType, List<Student>> collect = menu.stream().collect(Collectors.groupingBy(Student::getGradeType));
        Optional.ofNullable(collect).ifPresent(System.out::println);
        //{THREE=[Student(name=劉一, totalScore=721, local=true, gradeType=THREE), Student(name=陳二, totalScore=637, local=true, gradeType=THREE), Student(name=張三, totalScore=666, local=true, gradeType=THREE), Student(name=王五, totalScore=483, local=false, gradeType=THREE), Student(name=趙六, totalScore=367, local=true, gradeType=THREE)], TWO=[Student(name=李四, totalScore=531, local=true, gradeType=TWO)], ONE=[Student(name=孫七, totalScore=499, local=false, gradeType=ONE)]}

        //分組后統計每組的平均分數值,並對每組key升序排列
        Map<Student.GradeType, Double> map = menu.stream().collect(Collectors.groupingBy(Student::getGradeType, TreeMap::new , Collectors.averagingInt(Student::getTotalScore)));
        Optional.ofNullable(map).ifPresent(System.out::println);
        //{ONE=499.0, TWO=531.0, THREE=574.8}
    }

GroupingBy其他功能:按單個屬性、多個屬性,條件分組、多級分組、分組后組裝成其他集合

package com.empirefree.springboot;

import com.alibaba.fastjson.JSON;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @program: springboot
 * @description:
 * @author: huyuqiao
 * @create: 2021/08/05 09:47
 */
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class CollectorsTest {
    @Data
    public class Product {
        private final Long id;
        private final Integer num;
        private final BigDecimal price;
        private final String name;
        private final String category;
    }

    @Test
    public void testanything(){
        // 參考文章:https://blog.csdn.net/u014231523/article/details/102535902
        Product prod1 = new Product(1L, 1, new BigDecimal("15.5"), "面包", "零食");
        Product prod1_1 = new Product(11L, 11, new BigDecimal("15.5"), "面包", "零食");
        Product prod2 = new Product(2L, 2, new BigDecimal("20"), "餅干", "零食");
        Product prod3 = new Product(3L, 3, new BigDecimal("30"), "月餅", "零食");
        Product prod4 = new Product(4L, 3, new BigDecimal("10"), "青島啤酒", "啤酒");
        Product prod5 = new Product(5L, 10, new BigDecimal("15"), "百威啤酒", "啤酒");


        List<Product> prodList = Arrays.asList(prod1, prod1_1, prod2, prod3, prod4, prod5);
        Object object7 = prodList.stream().collect(Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparingInt(Product::getNum)), Optional::get));
        System.out.println(object7);

        // 1、按某個屬性分組
        Map<String, List<Product>> productMap = prodList.stream().collect(Collectors.groupingBy(Product::getCategory));
        System.out.println(JSON.toJSONString(productMap));

        // 2、按多個屬性分組
        Map<String, List<Product>> productMap2 = prodList.stream().collect(Collectors.groupingBy(item -> item.getCategory() + "_" + item.getName()));
        System.out.println(JSON.toJSONString(productMap2));

        // 3、按條件分組
        Map<String, List<Product>> productMap3 = prodList.stream().collect(Collectors.groupingBy(item ->{
            if (item.getNum() < 3){
                return "3";
            } else {
                return "other";
            }
        }));
        System.out.println(JSON.toJSONString(productMap3));

        // 4、多級分組
        Map<String, Map<String, List<Product>>> productMap4 = prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.groupingBy(item ->{
            if (item.getNum() < 3){
                return "3";
            } else {
                return "other";
            }
        })));
        System.out.println(JSON.toJSONString(productMap4));

        // 5、分組求總數、求和
        Map<String, Long> productMap5 = prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.counting()));
        Map<String, Integer> productMap6 = prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.summingInt(Product::getNum)));
        System.out.println(JSON.toJSONString(productMap5));
        System.out.println(JSON.toJSONString(productMap6));

        // 6、分組后只獲取最大值
        Map<String, Product> prodMap7 = prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparingInt(Product::getNum)), Optional::get)));
        System.out.println(prodMap7);


        // 7、分組后組裝成其他集合
        Map<String, Set<String>> productMap8 = prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.mapping(Product::getName, Collectors.toSet())));
        System.out.println(JSON.toJSONString(productMap8));
    }
}

2.5.partitioningBy--分區

partitioningBy方法:分區計算。返回map的可序列化和線程安全性無法保證

partitioningBy:返回map的key是Boolean。無法保證線程安全性

groupingBy:返回map的key是分組的值。可以保證線程安全性

    @Test
    public void partitioningByTest(){
        Map<Boolean, Double> collect = menu.stream().collect(Collectors.partitioningBy(Student::isLocal, Collectors.averagingInt(Student::getTotalScore)));
        Optional.ofNullable(collect).ifPresent(System.out::println);
    }
2.6.joining--拼接元素

joining方法:連接colletors的每個元素

    @Test
    public void joiningTest(){
        Optional.ofNullable(menu.stream().map(Student::getName).collect(Collectors.joining(",", "[", "]"))).ifPresent(System.out::println);
        //[劉一,陳二,張三,李四,王五,趙六,孫七]
    }
2.7.mapping--類似map

mapping方法:類似於map()。

    @Test
    public void mappingTest(){
        Optional.ofNullable(menu.stream().collect(Collectors.mapping(Student::getName, Collectors.joining(",", "[", "]")))).ifPresent(System.out::println);
    }
2.8.maxBy、minBy--類型max(),min()

maxBy、minBy方法:獲取比較器最大和最小值。返回的是Optional

    @Test
    public void maxOrMinByTest(){
        menu.stream().collect(Collectors.maxBy(Comparator.comparing(Student::getTotalScore))).ifPresent(System.out::println);
        menu.stream().collect(Collectors.minBy(Comparator.comparing(Student::getTotalScore))).ifPresent(System.out::println);
    }
2.9.reducing--類型reduce()

reducing方法:對收集器元素計算

    @Test
    public void reducingTest(){
        Integer result = menu.stream().map(Student::getTotalScore).collect(Collectors.reducing(0, (a, b) -> a + b));
        System.out.println(result);
    }
2.10.summarizingDouble*--匯總max,min,count,sum,avg(妙點)

summarizingDouble方法:匯總list中某屬性的所有匯總值(最大、最小、總數,總和,平均值)

    @Test
    public void summarizingInt(){
        DoubleSummaryStatistics result = menu.stream().collect(Collectors.summarizingDouble(Student::getTotalScore));
        Optional.ofNullable(result).ifPresent(System.out::println);
    }
2.11.toCollection--轉換成集合

toCollection方法:轉換收集器的集合類型

    //轉換成LinkedList
	@Test
    public void toCollectionTest(){
        Optional.of(menu.stream().filter(s -> s.getTotalScore() > 600).collect(Collectors.toCollection(LinkedList::new))).ifPresent(s -> {
            System.out.println(s.getClass());
            System.out.println(s);
        });
    }
2.12.toMap--轉換成list

toMap方法:可以將list轉換成map(但要注意key值重復與value為空情況)

@Test
    public void listToMapTest(){
        // 1 list to map -- obj
        List<User> users = Arrays.asList(
                new User(101, "Jack"),
                new User(102, "Kreas"),
                new User(103, "Marry"),
//                new User(103, "Marry2"),
                new User(104, "Timi"),
                new User(105, "Alice")
//                new User(105, null)
        );

        // key不能有重復
        Map<Long, User> map = users.stream().collect(Collectors.toMap(User::getId, o -> o));
        System.out.println(JSON.toJSONString(map));
        // 1.2 list to map -- 屬性
        Map<Long, String> map2 = users.stream().collect(Collectors.toMap(User::getId, User::getName));
        System.out.println(JSON.toJSONString(map2));

        // 3 list to map -- 解決key重復  value為空則報空指針
        Map<Long, String> map3 = users.stream().collect(Collectors.toMap(User::getId, User::getName, (o1, o2) -> o1));
        System.out.println(JSON.toJSONString(map3));
        Map<Long, String> map4 = users.stream().collect(Collectors.toMap(User::getId, User::getName, (o1, o2) -> o2));
        System.out.println(JSON.toJSONString(map4));
        Map<Long, String> map5 = users.stream().collect(Collectors.toMap(User::getId, User::getName, (o1, o2) -> o1 + "," + o2));
        System.out.println(JSON.toJSONString(map5));

        // 4 list to map -- 解決value為空,但不能解決key值重復
        Map<Long, String> map6 = users.stream().collect(Collectors.toMap(User::getId, o -> o.getName() == null ? "" : o.getName()));
        System.out.println(JSON.toJSONString(map6));
    }

注意:當有key重復以及value為null情況時,個人建議還是直接用put/putIfAbsent。

  Map<Long, String> map7 = new HashMap<>();
users.stream().forEach(user -> map7.put(user.getId(), user.getName()));
System.out.println(map7);

🔥3.總結

個人認為:

Optional.ofNullable(對象).orElse(對象.builder().build()):防止空指針非常實用

Collectors里面最重要的當屬groupingBy與summarizingDouble。其他的個人覺得知道要能看得懂是干什么用的,畢竟很多都可以直接在stream里面直接處理好,沒必要在collect里面處理。

另外:

map中重復值+1操作:computeIfAbsent可以實現

提取List 成list<>:flatMap可以實現

List轉map:map的put/putifAbsent即可

Collectors參考鏈接:https://blog.csdn.net/sl1992/article/details/98900343

toMap參考鏈接:https://zyqok.blog.csdn.net/article/details/108646765?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EsearchFromBaidu%7Edefault-1.pc_relevant_baidujshouduan&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EsearchFromBaidu%7Edefault-1.pc_relevant_baidujshouduan


🔥4.補充

4.1.Map操作--compute計算

Map:判斷list值,沒有就添加,有就value+1

    @Test
    public void mapTest(){
        /*
        * compute:計算並更新值(但是可能會空指針)
            computeIfAbsent:Value不存在時才計算
            computeIfPresent:Value存在時才計算
        * */
        HashMap<String, Integer> prices = new HashMap<>();

        // 往HashMap中添加映射關系
        prices.put("Shoes", 180);
        prices.put("Bag", 300);
        prices.put(null, 150);
        prices.put(null, 250);
        System.out.println("HashMap: " + prices);
        //有就不put,沒有才put
        prices.putIfAbsent("Shoes", 1810);

        //有才put,沒有不put
        prices.computeIfPresent("Shoes3", (key, value) -> value + 100);

        //有就不put
        prices.computeIfAbsent("Shoes4", key -> 18120);

        //無論有無都put計算
        prices.compute("Shoes2", (key, value) -> 1810);
        prices.compute("Shoes2", (key, value) -> 1820);
        System.out.println(prices);

        //給map中put值,有就 + 1,沒有就添加初始化為0
        Map<String, AtomicInteger> countTagClientMap = new LinkedHashMap<>();
        countTagClientMap.computeIfAbsent("a", value -> new AtomicInteger()).incrementAndGet();     //這個可以實現為空就put,否則 + 1
        countTagClientMap.computeIfAbsent("a", value -> new AtomicInteger());
        System.out.println(countTagClientMap);
        countTagClientMap.computeIfPresent("a", (key, value) -> new AtomicInteger(value.incrementAndGet()));
        countTagClientMap.compute("a", (key, value) -> value == null ? new AtomicInteger(1) : new AtomicInteger(value.incrementAndGet())); //這個可以實現為空就put,否則 + 1
        System.out.println(countTagClientMap);
    }
4.2.TreeMap--排序操作

treeMap:treeMap默認是對key值升序排序

    @Test
    public void treeMap() {
        // 對key值排序
        TreeMap<Integer, Integer> map1 = new TreeMap<Integer, Integer>();  //默認的TreeMap升序排列
        TreeMap<Integer, Integer> map2 = new TreeMap<Integer, Integer>(Comparator.reverseOrder());
        map2.put(1, 2);
        map2.put(2, 4);
        map2.put(7, 1);
        map2.put(5, 2);
        System.out.println("Map2=" + map2);

        map1.put(1, 2);
        map1.put(2, 4);
        map1.put(7, 1);
        map1.put(5, 2);
        System.out.println("map1=" + map1);
    }

書山有路勤為徑,學海無涯苦作舟。程序員不僅要懂代碼,更要懂生活,關注我,一起進步。

請添加圖片描述


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM