<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在SQL中經常會用到分組,我們也常常遇到一些組合分組的場景。
有下面的一個User類
import lombok.Builder; import lombok.Data; import java.time.LocalDateTime; @Data @Builder public class User { private String name; private int id; private String city; private String sex; private LocalDateTime birthDay; }
場景:根據 城市 進行分組
使用的是方法參照:User::getCity
來完成分組
import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class Demo2 { public static void main(String[] args) { DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); // data list List<User> userList = Arrays.asList( User.builder().id(123456).name("Zhang, San").city("ShangHai").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(), User.builder().id(777777).name("Zhang, San").city("ShangHai").sex("woman").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(), User.builder().id(888888).name("Li, Si").city("ShangHai").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(), User.builder().id(999999).name("Zhan, San").city("HangZhou").sex("woman").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(), User.builder().id(555555).name("Li, Si").city("NaJin").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build() ); Map<String, List<User>> groupMap = userList.stream() .collect(Collectors.groupingBy(User::getCity)); groupMap.forEach((k, v) -> { System.out.println(k); System.out.println(v); }); } }
①
場景:根據 城市,性別
進行分組
一般的寫法會是下面的這種寫法,通過lambda表示式將key的生成邏輯傳入進去:u -> u.getCity() + "|" + u.getSex()
來實現分組的效果。
import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class Demo2 { public static void main(String[] args) { DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); // data list List<User> userList = Arrays.asList( User.builder().id(123456).name("Zhang, San").city("ShangHai").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(), User.builder().id(777777).name("Zhang, San").city("ShangHai").sex("woman").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(), User.builder().id(888888).name("Li, Si").city("ShangHai").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(), User.builder().id(999999).name("Zhan, San").city("HangZhou").sex("woman").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(), User.builder().id(555555).name("Li, Si").city("NaJin").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build() ); Map<String, List<User>> groupMap = userList.stream() .collect(Collectors.groupingBy(u -> u.getCity() + "|" + u.getSex())); groupMap.forEach((k, v) -> { System.out.println(k); System.out.println(v); }); } }
單個條件的分組用的比較多,userList.stream().collect(Collectors.groupingBy(User::getCity));
這種方法參照的方式看起來很清爽。
在我們遇到多個欄位的分組的時候,我並不太想使用前面那種傳統的寫法①。
我在想,既然單個欄位的分組寫法是:
userList.stream().collect(Collectors.groupingBy(User::getCity));
那麼多個欄位的寫法可否是下面這種( 類推 ),傳入多個方法參照!
userList.stream().collect(Collectors.groupingBy(User::getCity,User::getSex));
很可惜 jdk 類庫中Collectors 沒有提供這種寫法
因為jdk沒有提供這種寫法,於是自己就想寫了一個Util
來幫助我們使用多個方法參照的方式完成組合分組
MyBeanUtil groupingBy(userList, User::getSex, User::getCity);
Demo:
import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; public class MyBeanUtil { public static void main(String[] args) { DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); // data list List<User> userList = Arrays.asList( User.builder().id(123456).name("Zhang, San").city("ShangHai").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(), User.builder().id(777777).name("Zhang, San").city("ShangHai").sex("woman").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(), User.builder().id(888888).name("Li, Si").city("ShangHai").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(), User.builder().id(999999).name("Zhan, San").city("HangZhou").sex("woman").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(), User.builder().id(555555).name("Li, Si").city("NaJin").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build() ); // 進行分組,根據名字和城市分組 Map<String, List<User>> groupMap = groupingBy(userList, User::getSex, User::getCity); //列印分組結果 groupMap.forEach((k, v) -> { System.out.println(k); System.out.println(v); }); } /** * 將資料分組,根據方法參照(bean的get方法) * * @param list 為分組的資料 * @param functions get方法陣列 */ @SafeVarargs public static <T, R> Map<String, List<T>> groupingBy(List<T> list, Function<T, R>... functions) { return list.stream().collect(Collectors.groupingBy(t -> groupingBy(t, functions))); } /** * 分組工具根據函數式介面使用分組,將資料根據分組結果進行拆分 */ @SafeVarargs public static <T, R> String groupingBy(T t, Function<T, R>... functions) { if (functions == null || functions.length == 0) { throw new NullPointerException("functions陣列不可以為空"); } else if (functions.length == 1) { return functions[0].apply(t).toString(); } else { return Arrays.stream(functions).map(fun -> fun.apply(t).toString()).reduce((str1, str2) -> str1 + "|" + str2).get(); } } }
依然不是很滿足這種寫法,因為這種寫法需要藉助 Util 類,不夠接地氣!
我更希望是下面這種接地氣的寫法:能夠完全整合在jdk類庫中
userList.stream().collect(Collectors.groupingBy(User::getCity,User::getSex));
為了達到上述的效果,那麼顯然我們是需要修改jdk原始碼的;
於是我就將java.util.stream.Collectors
原始碼完整copy出來,然後加入下面3個方法
public static <T, K> Collector<T, ?, HashMap<K, List<T>>> groupingBy(Function<? super T, ? extends K>... classifier) { return groupingBy("|", classifier); } public static <T, K> Collector<T, ?, HashMap<K, List<T>>> groupingBy(String split, Function<? super T, ? extends K>... classifier) { return groupingBy(split, classifier, HashMap::new, toList()); } public static <T, K, D, A, M extends Map<? super K, D>> Collector<T, ?, M> groupingBy(String split, Function<? super T, ? extends K>[] classifierArr, Supplier<M> mapFactory, Collector<? super T, A, D> downstream) { Supplier<A> downstreamSupplier = downstream.supplier(); BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator(); BiConsumer<Map<K, A>, T> accumulator = (m, t) -> { String key = Arrays.stream(classifierArr).map(classifier -> Objects.requireNonNull(classifier.apply(t))).map(String::valueOf).reduce((s1, s2) -> s1 + split + s2).get(); A container = m.computeIfAbsent((K) key, k -> downstreamSupplier.get()); downstreamAccumulator.accept(container, t); }; BinaryOperator<Map<K, A>> merger = Collectors.<K, A, Map<K, A>>mapMerger(downstream.combiner()); @SuppressWarnings("unchecked") Supplier<Map<K, A>> mangledFactory = (Supplier<Map<K, A>>) mapFactory; if (downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)) { return new CollectorImpl<>(mangledFactory, accumulator, merger, CH_ID); } else { @SuppressWarnings("unchecked") Function<A, A> downstreamFinisher = (Function<A, A>) downstream.finisher(); Function<Map<K, A>, M> finisher = intermediate -> { intermediate.replaceAll((k, v) -> downstreamFinisher.apply(v)); @SuppressWarnings("unchecked") M castResult = (M) intermediate; return castResult; }; return new CollectorImpl<>(mangledFactory, accumulator, merger, finisher, CH_NOID); } }
就達到了我們預期的效果,為了方便大家也一起體驗一下,我已經將demo完整的放到了github上
原始碼地址:https://github.com/1015770492/CollectorsDemo
下載好原始碼後,找到下面這個類
Demo:
import java.io.Serializable; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; public class MultiGroupByDemo { public static void main(String[] args) { DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); // data list List<User> userList = Arrays.asList( User.builder().id(123456).name("Zhang, San").city("ShangHai").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(), User.builder().id(777777).name("Zhang, San").city("ShangHai").sex("woman").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(), User.builder().id(888888).name("Li, Si").city("ShangHai").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(), User.builder().id(999999).name("Zhan, San").city("HangZhou").sex("woman").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(), User.builder().id(555555).name("Li, Si").city("NaJin").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build() ); /* * maybe we can */ // 1.Use the default vertical separator System.out.println("Use the default vertical separator:"); HashMap<String, List<User>> defaultSpilt = userList.stream().collect(Collectors.groupingBy(User::getName, User::getCity)); printMap(defaultSpilt); System.out.println(); // 2.Use custom delimiters System.out.println("Use custom delimiters:"); userList.stream().collect(Collectors.groupingBy("--", User::getName, User::getCity, User::getId)); HashMap<? extends Serializable, List<User>> collect = userList.stream().collect(Collectors.groupingBy("--", User::getName, User::getCity, User::getId)); printMap(collect); System.out.println(); // 3.Use custom delimiters System.out.println("Use custom delimiters:"); userList.stream().collect(Collectors.groupingBy("--", User::getName, User::getCity, User::getId)); HashMap<? extends Serializable, List<User>> collect2 = userList.stream().collect(Collectors.groupingBy(User::getName, User::getCity, User::getBirthDay)); printMap(collect2); } public static <T> void printMap(Map<? extends Serializable, List<T>> map){ map.forEach((k, v) -> { System.out.println(k); System.out.println(v); }); } }
最後我希望這個特效能被JDK所吸收,這樣可以方便大家更好的使用這些好用的特性
到此這篇關於使用Java8進行分組(多個欄位的組合分組)的文章就介紹到這了,更多相關Java8 分組內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45