深入了解集合操作工具Guava?Collect
集合操作是編程中使用頻率非常高的,所有有一款針對(duì)集合的操作工具是非常有必要的。通過框架提供的工具一方面可以減少開發(fā)相似功能的耗時(shí);同時(shí)框架在安全與穩(wěn)定性上更被推薦。
Guava Collect是Guava工具包中的一個(gè)子模塊,主要對(duì)jdk中的集合操作添加了一些簡(jiǎn)易的API,同時(shí)也是對(duì)Collections工具類的擴(kuò)展。當(dāng)然Guava還定義了一些特定場(chǎng)景的數(shù)據(jù)結(jié)構(gòu)以及一些針對(duì)jdk集合的優(yōu)化,最典型的就是Immutable Collections(不可變集合),你會(huì)發(fā)現(xiàn)調(diào)用Guava API很多都是不可變的
意義
我們常見的集合類有:
- List
- Set
- Vector
- Stack
- Map
- Queue
集合是一種非常常見的數(shù)據(jù)結(jié)構(gòu),JDK在處理各種數(shù)據(jù)集時(shí),提供了以上集合類型的數(shù)據(jù)結(jié)構(gòu)以及其對(duì)應(yīng)API方便開發(fā)者高效簡(jiǎn)易地對(duì)數(shù)據(jù)對(duì)象操作
特色
guava主要提供了以下幾個(gè)方面的支持:
增加了不可變集合
- 不受信任的庫可以安全使用。
- 線程安全:可以被許多線程使用,沒有競(jìng)爭(zhēng)條件的風(fēng)險(xiǎn)。
- 不需要支持突變,并且可以通過該假設(shè)節(jié)省時(shí)間和空間。所有不可變集合實(shí)現(xiàn)都比它們的可變兄弟更節(jié)省內(nèi)存。(分析)
- 可以用作常數(shù),期望它保持不變。
增加了新的集合類型
- Multiset 與普通的Set相比,提供了元素出現(xiàn)頻率的記錄??捎糜谠爻霈F(xiàn)次數(shù)的記錄
- Multimap 一個(gè)與Map相比,一個(gè)建可以對(duì)應(yīng)對(duì)應(yīng)多個(gè)值。與Spring中MultiValueMap一樣
- BiMap 鍵值都是唯一的Map
- Table 具有行、列的表格,數(shù)據(jù)視圖中可能更直觀。
- ClassToInstanceMap 鍵為Class,值為Class實(shí)例的特殊Map
- RangeSet 代表一組數(shù)據(jù)區(qū)間,類似數(shù)學(xué)中的 [1,9)
- RangeMap 與RangeSet類似,不過將其區(qū)間作為建,可以有自己的值。[1,9) -> 'VAL'
優(yōu)化了常用的操作
集合的創(chuàng)建
- ImmutableSet.of(elem ...)
- Lists.newArrayList(elem ...)
- Sets.newHashSet(elem ...)
- Maps.newHashMap()
- ...
常用的操作 判斷兩個(gè)集合是否相等:Iterables.elementsEqual()
- 集合分段處理:Lists.partition()
- 取集合的交集:Sets.intersection()
- 取集合的差集:Sets.difference()
- ...
使用
Guava Collect作為集合操作工具,我們主要從實(shí)際業(yè)務(wù)中了解其能夠幫助我們實(shí)現(xiàn)怎樣的需求,下面看下其API的使用情況:
假設(shè)我們有10000名學(xué)生,通過Faker生成這些模擬的學(xué)生數(shù)據(jù)數(shù)據(jù):
List<Student>?students?=?new?ArrayList<>(); Faker?faker?=?new?Faker(Locale.CHINA); @Before public?void?init(){ ????Faker?enFaker?=?new?Faker(); ????Name?name?=?faker.name(); ????IntStream.range(0,10000).forEach(index->{ ????????students.add( ????????Student.of() ????????.setId(String.valueOf(index+1)) ????????.setName(name.name()) ????????.setAge(faker.number().numberBetween(18,22)) ????????.setGender(new?String[]{"男","女"}[faker.number().numberBetween(0,2)]) ????????.setAddress(faker.address().streetAddress()) ????????.setScore(faker.number().randomDouble(3,50,100)) ????????.setEmail(?faker.internet().emailAddress(enFaker.name().username())) ????????.setTelephone(faker.phoneNumber().cellPhone()) ????????); ????}); }
Multiset
獲取元素出現(xiàn)頻次。比如獲取男生與女生的學(xué)生數(shù)量分別為多少
????@Test ????public?void?multiset(){ ????????Multiset?multiset?=?HashMultiset.create(); ????????students.forEach(student?->?{ ????????????if(Objects.equals(student.getGender(),"男")){ ????????????????multiset.add("男"); ????????????}else{ ????????????????multiset.add("女"); ????????????} ????????}); ????????System.out.println("學(xué)生中男生數(shù)量:"+?multiset.count("男")); ????????System.out.println("學(xué)生中女生數(shù)量:"+?multiset.count("女")); ????}
Multimap
一個(gè)鍵對(duì)應(yīng)多個(gè)值時(shí)。比如查看各個(gè)年齡的學(xué)生是哪些
????@Test ????public?void?multimap(){ ????????ListMultimap<Integer,?Student>?multimap?= ????????????????MultimapBuilder.hashKeys().arrayListValues().build(); ????????students.forEach(student?->?{ ????????????multimap.put(student.getAge(),student); ????????}); ????????System.out.println(?multimap.get(20)?); ????}
BiMap
鍵和值都是唯一時(shí)。比如處理學(xué)生的郵箱和手機(jī)號(hào),客戶互換鍵值位置
????@Test ????public?void?biMap(){ ????????BiMap?biMap?=?HashBiMap.create(); ????????students.forEach(student?->?{ ????????????biMap.put(student.getEmail(),student.getTelephone()); ????????}); ????????BiMap?inverse?=?biMap.inverse();//?鍵值更換 ????????System.out.println(?biMap?); ????????System.out.println(?inverse?); ????}
Table
二維表,通過行(鍵)、列(鍵)取值 比如可以以學(xué)生為行數(shù)據(jù),其中id為行鍵,列名分別為學(xué)生屬性名稱
ID | 姓名 | 年齡 | 性別 |
---|---|---|---|
1 | TOM | 22 | 男 |
????@Test ????public?void?table(){ ????????Table<String,?String,?Object>?weightedGraph?=?HashBasedTable.create(); ????????students.forEach(student?->?{ ????????????weightedGraph.put(student.getId(),?"姓名",?student.getName()); ????????????weightedGraph.put(student.getId(),?"年齡",?student.getAge()); ????????????weightedGraph.put(student.getId(),?"性別",?student.getGender()); ????????????weightedGraph.put(student.getId(),?"郵箱",?student.getEmail()); ????????????weightedGraph.put(student.getId(),?"電話",?student.getTelephone()); ????????????weightedGraph.put(student.getId(),?"地址",?student.getAddress()); ????????????weightedGraph.put(student.getId(),?"分?jǐn)?shù)",?student.getScore()); ????????}); ????????Map<String,?Object>?row?=?weightedGraph.row("1"); ????????Map<String,?Object>?column?=?weightedGraph.column("姓名"); ????????Set<Table.Cell<String,?String,?Object>>?cells?=?weightedGraph.cellSet(); ????????System.out.println(?row?); ????????System.out.println(?column?); ????????System.out.println(?cells?); ????}
ClassToInstanceMap
當(dāng)值是鍵的類型實(shí)例時(shí),通過該Map現(xiàn)在鍵值關(guān)系
????@Test ????public?void?classToInstanceMap(){ ????????ClassToInstanceMap<Number>?numberDefaults?=?MutableClassToInstanceMap.create(); ???? ????????numberDefaults.put(Number.class,1); ???? ????????Map<Class,Object>?objectMap?=?new?HashMap<>(); ????????objectMap.put(Number.class,2); ????}
RangeSet
區(qū)間Set。比如通過學(xué)生分?jǐn)?shù)確定學(xué)生等級(jí)
????@Test ????public?void?rangeSet(){ ????????RangeSet<Double>?ArangeSet?=?TreeRangeSet.create(); ????????ArangeSet.add(Range.closed(90d,100d));?//?[90,100] ????????RangeSet<Double>?BrangeSet?=?TreeRangeSet.create(); ????????BrangeSet.add(Range.closedOpen(80d,90d));?//?[80,90) ????????RangeSet<Double>?CrangeSet?=?TreeRangeSet.create(); ????????CrangeSet.add(Range.closedOpen(70d,80d));?//?[70,80) ????????RangeSet<Double>?DrangeSet?=?TreeRangeSet.create(); ????????DrangeSet.add(Range.closedOpen(60d,70d));?//?[60,70) ????????RangeSet<Double>?ErangeSet?=?TreeRangeSet.create(); ????????ErangeSet.add(Range.lessThan(60d));?//?[...,60) ????????students.forEach(student?->?{ ????????????System.out.print(?"?學(xué)生:"+?student.getName()?); ????????????System.out.print(?",分?jǐn)?shù)為:"+?student.getScore()?); ????????????String?rank?=?""; ????????????if(ArangeSet.contains(student.getScore())){ ????????????????rank?=?"A"; ????????????}else?if(BrangeSet.contains(student.getScore())){ ????????????????rank?=?"B"; ????????????}else?if(CrangeSet.contains(student.getScore())){ ????????????????rank?=?"C"; ????????????}else?if(DrangeSet.contains(student.getScore())){ ????????????????rank?=?"D"; ????????????}else?if(ErangeSet.contains(student.getScore())){ ????????????????rank?=?"E"; ????????????} ????????????System.out.print(?",等級(jí)為:"+?rank?+"\n"); ????????}); ????}
RangeMap和RangeSet類似,區(qū)別是添加了區(qū)間命名。和上面一樣
????@Test ????public?void?rangeMap(){ ????????RangeMap<Double,?String>?rangeMap?=?TreeRangeMap.create(); ????????rangeMap.put(Range.closed(90d,100d),"A");?//?[90,100] ????????rangeMap.put(Range.closedOpen(80d,90d),"B");?//?[80,90) ????????rangeMap.put(Range.closedOpen(70d,80d),"C");?//?[70,80) ????????rangeMap.put(Range.closedOpen(60d,70d),"D");?//?[60,70) ????????rangeMap.put(Range.lessThan(60d),"E");?//?[...,60) ????????students.forEach(student?->?{ ????????????System.out.print(?"?學(xué)生:"+?student.getName()?); ????????????System.out.print(?",分?jǐn)?shù)為:"+?student.getScore()?); ????????????System.out.print(?",等級(jí)為:"+?rangeMap.get(student.getScore())?+"\n"); ????????}); ????}
下面看下對(duì)常用集合的一些操作
當(dāng)然我們首先需要將數(shù)據(jù)使用Guava Collect對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)來存儲(chǔ)數(shù)據(jù),這樣才能使用其對(duì)應(yīng)的API:
集合創(chuàng)建 FluentIterable.of(elem ...)
- Lists.newArrayList(elem ...)
- Sets.newHashSet(elem ...)
- Maps.newHashMap()
- HashMultiset.create()
- ArrayListMultimap.create()
- Tables.newCustomTable(Maps.newLinkedHashMap(), () -> Maps.newLinkedHashMap())\
條件過濾
FluentIterable.filter(predicate); FluentIterable.anyMatch(predicate); FluentIterable.allMatch(predicate); FluentIterable.firstMatch(predicate);
拆分 Iterables.partition(list, pageSize); // 拆解集合
計(jì)算 Iterables.frequency(list, elem); //元素出現(xiàn)的次數(shù)
集合的并集、交集、差集 // 并集 Sets.union(set1, set2); // 交集 Sets.intersection(set1, set2); // 差集 set1為參考 Sets.difference(set1, set2); // 并集-交集 Sets.symmetricDifference(set1, set2); // 同上 Sets.difference(Sets.union(set1, set2),Sets.intersection(set1, set2) ); // 笛卡爾積 Sets.cartesianProduct(Arrays.asList(Sets.newHashSet(1, 2, 3), Sets.newHashSet(3, 4, 5, 6)); // Map,KV相同的部分 difference.entriesInCommon(); // 同K不同V difference.entriesDiffering(); // 左邊存在的右邊不存的K difference.entriesOnlyOnLeft(); // 右邊存在的左邊不存的K difference.entriesOnlyOnRight();
索引 // 將元素中的子項(xiàng)作為索引,由于元素檢索 Maps.uniqueIndex() Multimaps.index()
Jdk中的集合操作
自從Jdk中引入了集合Stream的操作后,從很大程度上簡(jiǎn)化了對(duì)集合的操作,以前大量代碼現(xiàn)在可能簡(jiǎn)單幾行就能夠達(dá)到相同的效果,同時(shí)支持并發(fā)處理,一并提升了效率。
下面看下常見的集合基于stream操作,同樣以上面的學(xué)生為例:
遍歷 forEach
????@Test ????public?void?forEach(){ ????????students.stream().forEach(System.out::println); ????}
轉(zhuǎn)換 map
將元素轉(zhuǎn)換成其他類型。比如根據(jù)學(xué)生名稱、性別組成新的List;以id為鍵元素為值的Map或者學(xué)生姓名拼接的字符串等等
????@Test ????public?void?transform(){ ????????//?轉(zhuǎn)換為數(shù)組 ????????List<String>?listResult?=?students.stream() ????????.map((val)->?val.getName()?+?":"?+?val.getGender()).collect(Collectors.toList()); ????????System.out.println(?listResult?); ???? ????????//?轉(zhuǎn)換成String ????????String?stringResult?=?students.stream().map(Student::getName).collect(Collectors.joining()); ????????System.out.println(?stringResult?); ???? ????????//?轉(zhuǎn)換成Map ????????Map<String,?Student>?mapResult?=?students.stream().collect( ????????//?key?,value?,mergerOperation,?initialization ????????Collectors.toMap(Student::getName,Student::self,(v1,v2)->{ ????????????//?出現(xiàn)相同key時(shí)的合并規(guī)則 ????????????return?null; ????????????},HashMap::new) ????????); ????????System.out.println(?mapResult?); ????}
過濾 filter
根據(jù)條件匹配滿足要求的元素。如找出分?jǐn)?shù)大于80分的學(xué)生
?????@Test ????public?void?filter(){ ????????List<Student>?filterResult?=?students.stream().filter((val)->{ ????????????return?val.getScore()>80; ????????}).collect(Collectors.toList()); ????????System.out.println(filterResult); ????}
拆解 flatMap
將二層級(jí)集合進(jìn)行拆解,并成一級(jí)集合。如[[1,2,3],[4,5,6]] -> [1,2,3,4,5,6]
????@Test ????public?void?flatMap(){ ????????//復(fù)合拆解 ????????List<Integer>?result?=?Stream.of(Arrays.asList(1,?2,?3),?Arrays.asList(4,?5,?6)) ????????????????.flatMap(subList?->?subList.stream()) ????????????????.collect(Collectors.toList()); ????????System.out.println(result);//?1?2?3?4?5?6 ????}
計(jì)算實(shí)現(xiàn)數(shù)據(jù)的匯總、求平均值、最大值...,當(dāng)然主要針對(duì)數(shù)字(Number)類型
????@Test ????public?void?calculate(){ ????????//?求和 ????????double?sum?=?students.stream().mapToDouble(Student::getScore).sum(); ????????//?最大值 ????????double?max?=?students.stream().mapToDouble(Student::getScore).max().getAsDouble(); ????????//?最小值 ????????double?min?=?students.stream().mapToDouble(Student::getScore).min().getAsDouble(); ????????//?平均值 ????????double?avg?=?students.stream().mapToDouble(Student::getScore).average().getAsDouble(); ????????//?歸約運(yùn)算?fold?.?count、sum、min、max、average ????????DoubleSummaryStatistics?doubleSummaryStatistics?=?students.stream().mapToDouble(Student::getScore).summaryStatistics(); ????}
歸納計(jì)算 reduce
在很多語言中都存在的函數(shù),如python、javascript。數(shù)據(jù)的累加、map的功能
????@Test ????public?void?reduce(){ ????????//?結(jié)果和identity(初始值)類型相同 ????????//?identity?accumulator?combiner ????????Map?result?=?students.stream().reduce( ????????????????new?HashMap<String,Student>(),?//初始值 ????????????????(map,?student)?->?{ ????????????????????map.put(student.getId(),student); ????????????????????return?map; ????????????????}, ????????????????(map1,?map2)?->?{ ????????????????????//?并發(fā)執(zhí)行時(shí)的map合并 ????????????????????return?null; ????????????????} ????????); ????}
并發(fā) parallel
上面的操作我們還可以使用parallel對(duì)stream并發(fā)處理
????Arrays.asList().stream().parallel()...; ????Arrays.asList().parallelStream()...;
分段處理對(duì)集合按固定規(guī)格分段處理,處理大批量數(shù)據(jù)時(shí),結(jié)合parallel實(shí)現(xiàn)分段并發(fā)處理來提示效率
????@Test ????public?void?partition(){ ????????List<String>?list?=?new?ArrayList<>(); ????????int?partition?=?100;?//每段100個(gè)元素 ???? ????????int?part?=?list.size()?/?partition??+?(list.size()?%?partition==0??0:1); ????????Stream.iterate(0,?n?->?n+1) ????????????????.limit(part) ????????????????.parallel()?//并發(fā) ????????????????.map(index?->?list.stream().skip(index?*?partition).limit(partition).parallel().collect(Collectors.toList())) ????????????????.forEach(System.out::println); ????}
總結(jié)
本章主要介紹了Guava Collect部分,以及對(duì)集合操作的常用API,通過示例可以看到有其對(duì)JDK集合的擴(kuò)展有了更廣泛與簡(jiǎn)易的操作。同時(shí)在JDK引入 了Stream操作后,Guava Collect中的很多功能通過Stream也可以比較容易的實(shí)現(xiàn)了,當(dāng)然具體如何選擇根據(jù)實(shí)際情況。需要注意的是Guava Collect中 返回的基本都是不可變的集合,這樣在對(duì)數(shù)據(jù)的操作會(huì)更加的安全。
到此這篇關(guān)于深入了解集合操作工具Guava Collect的文章就介紹到這了,更多相關(guān)Guava Collect集合操作內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java.lang.OutOfMemoryError: Metaspace異常解決的方法
這篇文章主要介紹了java.lang.OutOfMemoryError: Metaspace異常解決的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03java原裝代碼完成pdf在線預(yù)覽和pdf打印及下載
本文主要介紹了java原裝代碼完成pdf在線預(yù)覽和pdf打印及下載的方法,具有一定的參考價(jià)值,下面跟著小編一起來看下吧2017-02-02maven+阿里云創(chuàng)建國(guó)內(nèi)鏡像的中央倉庫(親測(cè)可用)
本篇文章主要介紹了maven+阿里云創(chuàng)建國(guó)內(nèi)鏡像的中央倉庫(親測(cè)可用),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-12-12java多線程數(shù)據(jù)分頁處理實(shí)例講解
在本篇內(nèi)容里小編給大家分享了一篇關(guān)于java多線程數(shù)據(jù)分頁處理實(shí)例講解內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。2021-01-01Spring?Boot在啟動(dòng)時(shí)執(zhí)行一次的功能實(shí)現(xiàn)
這篇文章主要給大家介紹了關(guān)于Spring?Boot在啟動(dòng)時(shí)執(zhí)行一次的功能實(shí)現(xiàn),在實(shí)習(xí)過程中,有時(shí)候會(huì)遇到一些項(xiàng)目啟動(dòng)初始化的需求,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-08-08SpringBoot通過JSON傳遞請(qǐng)求參數(shù)的實(shí)例詳解
這篇文章主要介紹了SpringBoot通過JSON傳遞請(qǐng)求參數(shù),示例介紹SpringMVC如何通過JSON格式傳遞入?yún)?,代碼簡(jiǎn)單易懂,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-11-11