java的Guava工具包介紹
集合
普通集合
List<String> list = Lists.newArrayList(); Set<String> set = Sets.newHashSet(); Map<String, String> map = Maps.newHashMap();
Set 取交集、并集、差集
HashSet<Integer> setA = Sets.newHashSet(1, 2, 3, 4, 5); HashSet<Integer> setB = Sets.newHashSet(4, 5, 6, 7, 8); Sets.SetView<Integer> union = Sets.union(setA, setB); System.out.println("union:" + union); Sets.SetView<Integer> difference = Sets.difference(setA, setB); System.out.println("difference:" + difference); Sets.SetView<Integer> intersection = Sets.intersection(setA, setB); System.out.println("intersection:" + intersection);
map 取交集、并集、差集
HashMap<String, Integer> mapA = Maps.newHashMap(); mapA.put("a", 1); mapA.put("b", 2); mapA.put("c", 3); HashMap<String, Integer> mapB = Maps.newHashMap(); mapB.put("b", 20); mapB.put("c", 3); mapB.put("d", 4); MapDifference<String, Integer> differenceMap = Maps.difference(mapA, mapB); Map<String, MapDifference.ValueDifference<Integer>> entriesDiffering = differenceMap.entriesDiffering(); //左邊差集 Map<String, Integer> entriesOnlyLeft = differenceMap.entriesOnlyOnLeft(); //右邊差集 Map<String, Integer> entriesOnlyRight = differenceMap.entriesOnlyOnRight(); //交集 Map<String, Integer> entriesInCommon = differenceMap.entriesInCommon(); System.out.println(entriesDiffering); // {b=(2, 20)} System.out.println(entriesOnlyLeft); // {a=1} System.out.println(entriesOnlyRight); // {d=4} System.out.println(entriesInCommon); // {c=3}
不可變集合(immutable)
不可變集合的特性有:
- 在多線程操作下,是線程安全的;
- 所有不可變集合會比可變集合更有效的利用資源;
- 中途不可改變。
如果你的需求是想創(chuàng)建一個一經(jīng)初始化后就不能再被改變的集合那么它適合你,因為這些工具類根本就沒給你提供修改的 API,這意味著你連犯錯誤的機會都沒有。
ImmutableList<Integer> iList = ImmutableList.of(12,54,87); ImmutableSet<Integer> iSet = ImmutableSet.of(354,54,764,354); ImmutableMap<String, Integer> iMap = ImmutableMap.of("k1", 453, "k2", 534);
以上 Immutable 開頭的相關(guān)集合類的 add、remove 方法都被聲明為 deprecated。當你手誤點到了這些方法發(fā)現(xiàn)是 deprecated 的時候你不會還想著使用吧。
注意:每個Guava immutable集合類的實現(xiàn)都拒絕 null 值。
有趣的集合
MultiSet: 無序+可重復
我們映像中的 Set 應該是無序的,元素不可重復的。MultiSet 顛覆了三觀,因為它可以重復。
定義一個 MultiSet 并添加元素:
Multiset<Integer> set = HashMultiset.create(); set.add(3); set.add(3); set.add(4); set.add(5); set.add(4);
你還可以添加指定個數(shù)的同一個元素:
set.add(7, 3);
這表示你想添加 3 個 7。
打印出來的 MultiSet 也很有意思:
[3 x 2, 4 x 2, 5, 7 x 3]
2個3,2個4,一個5,3個7。
獲取某個元素的個數(shù):
int count = set.count(3);
這個工具類確實很有意思,幫我們實現(xiàn)了 word count。
Multimap :key 可以重復的 map
這個 map 也很有意思。正常的 map 為了區(qū)分不同的 key,它倒好,直接給你來一樣的 key 。
Multimap<String, String> map = LinkedHashMultimap.create(); map.put("key", "haha"); map.put("key", "haha1"); Collection<String> key = map.get("key"); System.out.println(key);
使用很簡單,用一個 key 可以獲取到該 key 對應的兩個值,結(jié)果用 list 返回。恕我無知,我還沒想到這個 map 能夠使用的場景。
Multimap 提供了多種實現(xiàn):
Multimap 實現(xiàn) | key 字段類型 | value 字段類型 |
---|---|---|
ArrayListMultimap | HashMap | ArrayList |
HashMultimap | HashMap | HashSet |
LinkedListMultimap | LinkedHashMap | LinkedList |
LinkedHashMultimap | LinkedHashMap | LinkedHashSet |
TreeMultimap | TreeMap | TreeSet |
ImmutableListMultimap | ImmutableMap | ImmutableList |
ImmutableSetMultimap | ImmutableMap | ImmutableSet |
雙向 Map
(Bidirectional Map) 鍵與值都不能重復
這個稍稍正常一點。如果 key 重復了則會覆蓋 key ,如果 value 重復了則會報錯。
public static void main(String[] args) { BiMap<String, String> biMap = HashBiMap.create(); biMap.put("key", "haha"); biMap.put("key", "haha1"); biMap.put("key1", "haha"); String value = biMap.get("key"); System.out.println(value); }
上面的示例中鍵 ”key“ 有兩個,運行可以發(fā)現(xiàn) get 的時候會用 ”haha1" 覆蓋 ”haha“,另外 value 為 ”haha“ 也有兩個,你會發(fā)現(xiàn)運行上面的代碼不會報錯,這是因為 ”key“ 對應的 value 已經(jīng)被 "haha1" 覆蓋了。否則是會報錯。
雙鍵 map - 超級實用
雙鍵的 map ,我突然感覺我發(fā)現(xiàn)了新大陸。比如我有一個業(yè)務場景是:根據(jù)職位和部門將公司人員區(qū)分開來。key 可以用職位 + 部門組成一個字符串,那我們有了雙鍵 map 之后就沒這種煩惱。
public static void main(String[] args) { Table<String, String, List<Object>> tables = HashBasedTable.create(); tables.put("財務部", "總監(jiān)", Lists.newArrayList()); tables.put("財務部", "職員",Lists.newArrayList()); tables.put("法務部", "助理",Lists.newArrayList()); System.out.println(tables); }
工具類
JDK里大家耳熟能詳?shù)氖?code>Collections 這個集合工具類, 提供了一些基礎(chǔ)的集合處理轉(zhuǎn)換功能, 但是實際使用里很多需求并不是簡單的排序, 或者比較數(shù)值大小, 然后 Guava 在此基礎(chǔ)上做了許多的改進優(yōu)化, 可以說是 Guava 最為成熟/流行的模塊之一。
- 數(shù)組相關(guān):Lists
- 集合相關(guān):Sets
- map 相關(guān):Maps
連接符(Joiner)和分隔符(Splitter)
Joiner 做為連接符的使用非常簡單,下例是將 list 轉(zhuǎn)為使用連接符連接的字符串:
List<Integer> list = Lists.newArrayList(); list.add(34); list.add(64); list.add(267); list.add(865); String result = Joiner.skipNulls().on("-").join(list); System.out.println(result); 輸出:34-64-267-865
將 map 轉(zhuǎn)為自定義連接符連接的字符串:
Map<String, Integer> map = Maps.newHashMap(); map.put("key1", 45); map.put("key2",234); String result = Joiner.on(",").withKeyValueSeparator("=").join(map); System.out.println(result); 輸出: key1=45,key2=234
分隔符 Splitter 的使用也很簡單:
String str = "1-2-3-4-5-6"; List<String> list = Splitter.on("-").splitToList(str); System.out.println(list); 輸出: [1, 2, 3, 4, 5, 6]
如果字符串中帶有空格,還可以先去掉空格:
String str = "1-2-3-4- 5- 6 "; List<String> list = Splitter.on("-").omitEmptyStrings().trimResults().splitToList(str); System.out.println(list);
將 String 轉(zhuǎn)為 map:
String str = "key1=54,key2=28"; Map<String,String> map = Splitter.on(",").withKeyValueSeparator("=").split(str); System.out.println(map); 輸出: {key1=54, key2=28}
Comparator 的實現(xiàn)
Java 提供了 Comparator 可以用來對對象進行排序。Guava 提供了排序器 Ordering 類封裝了很多實用的操作。
Ordering 提供了一些有用的方法:
- natural() 對可排序類型做自然排序,如數(shù)字按大小,日期按先后排序
- usingToString() 按對象的字符串形式做字典排序[lexicographical ordering]
- from(Comparator) 把給定的Comparator轉(zhuǎn)化為排序器
- reverse() 獲取語義相反的排序器
- nullsFirst() 使用當前排序器,但額外把null值排到最前面。
- nullsLast() 使用當前排序器,但額外把null值排到最后面。
- compound(Comparator) 合成另一個比較器,以處理當前排序器中的相等情況。 lexicographical() 基于處理類型T的排序器,返回該類型的可迭代對象Iterable
的排序器。
onResultOf(Function) 對集合中元素調(diào)用Function,再按返回值用當前排序器排序。
示例:
UserInfo build = UserInfo.builder().uid(234L).gender(1).build(); UserInfo build1 = UserInfo.builder().uid(4354L).gender(0).build(); Ordering<UserInfo> byOrdering = Ordering.natural().nullsFirst().onResultOf((Function<UserInfo, Comparable<Integer>>) input -> input.getGender()); System.out.println(byOrdering.compare(build1, build));
build 的 gender 大于 build1 的,所以返回 -1,反之返回 1。
統(tǒng)計中間代碼運行時間
Stopwatch 類提供了時間統(tǒng)計的功能,相當于幫你封裝了調(diào)用 System.currentTimeMillis() 的邏輯。
Stopwatch stopwatch = Stopwatch.createStarted(); try { //TODO 模擬業(yè)務邏輯 Thread.sleep(2000L); } catch (InterruptedException e) { e.printStackTrace(); } long nanos = stopwatch.elapsed(TimeUnit.SECONDS); System.out.println(nanos);
Guava Cache - 本地緩存組件
Guava Cache 在日常的使用中非常地頻繁,甚至都沒有意識到這是第三方提供的工具類而是把它當成了 JDK 自帶的實現(xiàn)。
// LoadingCache是Cache的緩存實現(xiàn) LoadingCache<String, Object> cache = CacheBuilder.newBuilder() //設(shè)置緩存大小 .maximumSize(1000) //設(shè)置到期時間 .expireAfterWrite(10, TimeUnit.MINUTES) //設(shè)置緩存里的值兩分鐘刷新一次 .refreshAfterWrite(2, TimeUnit.MINUTES) //開啟緩存的統(tǒng)計功能 .recordStats() //構(gòu)建緩存 .build(new CacheLoader<String, Object>() { //此處實現(xiàn)如果根據(jù)key找不到value需要去如何獲取 @Override public Object load(String s) throws Exception { return new Object(); } //如果批量加載有比反復調(diào)用load更優(yōu)的方法則重寫這個方法 @Override public Map<String, Object> loadAll(Iterable<? extends String> keys) throws Exception { return super.loadAll(keys); } });
設(shè)置本地緩存使用 CacheBuilder.newBuilder(),支持設(shè)置緩存大小,緩存過期時間,緩存刷新頻率等等。如果你想統(tǒng)計緩存的命中率, Guava Cache 也提供了這種能力幫你匯總當前緩存是否有效。
同時緩存如果因為某種原因未自動刷新或者清除,Guava Cache 也支持用戶手動調(diào)用 API 刷新或者清除緩存。
cache.invalidateAll();//清除所有緩存項 //清理的時機:在寫操作時順帶做少量的維護工作,或者偶爾在讀操作時做——如果寫操作實在太少的話 //如果想自己維護則可以調(diào)用Cache.cleanUp(); cache.cleanUp(); //另外有時候需要緩存中的數(shù)據(jù)做出變化重載一次,這個過程可以異步執(zhí)行 cache.refresh("key");
單機限流工具類 - RateLimiter
常用的限流算法有 漏桶算法、令牌桶算法。這兩種算法各有側(cè)重點:
- 漏桶算法:漏桶的意思就像一個漏斗一樣,水一滴一滴的滴下去,流出是勻速的。當訪問量過大的時候這個漏斗就會積水。漏桶算法的實現(xiàn)依賴隊列,一個處理器從隊頭依照固定頻率取出數(shù)據(jù)進行處理。如果請求量過大導致隊列堆滿那么新來的請求就會被拋棄。漏桶一般按照固定的速率流出。
- 令牌桶則是存放固定容量的令牌,按照固定速率從桶中取出令牌。初始給桶中添加固定容量令牌,當桶中令牌不夠取出的時候則拒絕新的請求。令牌桶不限制取出令牌的速度,只要有令牌就能處理。所以令牌桶允許一定程度的突發(fā),而漏桶主要目的是平滑流出。
RateLimiter 使用了令牌桶算法,提供兩種限流的實現(xiàn)方案:
- 平滑突發(fā)限流(SmoothBursty)
- 平滑預熱限流(SmoothWarmingUp)
實現(xiàn)平滑突發(fā)限流通過 RateLimiter 提供的靜態(tài)方法來創(chuàng)建:
RateLimiter r = RateLimiter.create(5); while (true) { System.out.println("get 1 tokens: " + r.acquire() + "s"); } 輸出: get 1 tokens: 0.0s get 1 tokens: 0.197059s get 1 tokens: 0.195338s get 1 tokens: 0.196918s get 1 tokens: 0.19955s get 1 tokens: 0.199062s get 1 tokens: 0.195589s get 1 tokens: 0.195061s ......
設(shè)置每秒放置的令牌數(shù)為 5 個,基本 0.2s 一次符合每秒 5 個的設(shè)置。保證每秒不超過 5 個達到了平滑輸出的效果。
在沒有請求使用令牌桶的時候,令牌會先創(chuàng)建好放在桶中,所以此時如果突然有突發(fā)流量進來,由于桶中有足夠的令牌可以快速響應。RateLimiter 在沒有足夠令牌發(fā)放時采用滯后處理的方式,前一個請求獲取令牌所需等待的時間由下一次請求來承受。
平滑預熱限流并不會像平滑突發(fā)限流一樣先將所有的令牌創(chuàng)建好,它啟動后會有一段預熱期,逐步將分發(fā)頻率提升到配置的速率。
比如下面例子創(chuàng)建一個平均分發(fā)令牌速率為 2,預熱期為 3 分鐘。由于設(shè)置了預熱時間是 3 秒,令牌桶一開始并不會 0.5 秒發(fā)一個令牌,而是形成一個平滑線性下降的坡度,頻率越來越高,在 3 秒鐘之內(nèi)達到原本設(shè)置的頻率,以后就以固定的頻率輸出。這種功能適合系統(tǒng)剛啟動需要一點時間來“熱身”的場景。
RateLimiter r = RateLimiter.create(2, 3, TimeUnit.SECONDS); while (true) { System.out.println("get 1 tokens: " + r.acquire(1) + "s"); System.out.println("get 1 tokens: " + r.acquire(1) + "s"); System.out.println("end"); } 輸出: get 1 tokens: 0.0s get 1 tokens: 1.33068s end get 1 tokens: 0.995792s get 1 tokens: 0.662838s end get 1 tokens: 0.494775s get 1 tokens: 0.497293s end get 1 tokens: 0.49966s get 1 tokens: 0.49625s end
從上面的輸出看前面兩次獲取令牌都很耗時,往后就越來越趨于平穩(wěn)。
今天給大家介紹的常用的 Guava 工具類就這些,不過 JDK8 開始 Java官方 API 也在完善,比如像字符串相關(guān)的功能 JDK也很強大。都是工具,哪個好用就用哪個。
到此這篇關(guān)于java的Guava工具包介紹的文章就到這了,更多相關(guān)Guava工具包內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持腳本之家!
相關(guān)文章
淺談spring-boot-rabbitmq動態(tài)管理的方法
這篇文章主要介紹了淺談spring-boot-rabbitmq動態(tài)管理的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12spring?boot?使用Mybatis-plus查詢方法解析
這篇文章主要介紹了spring?boot?使用Mybatis-plus查詢方法解析,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-09-09Spring Boot + FreeMarker 實現(xiàn)動態(tài)Word文檔導出功能
Spring Boot與FreeMarker的組合,為開發(fā)者提供了一個強大的平臺,可以輕松實現(xiàn)動態(tài)Word文檔的導出,本文將指導你如何使用Spring Boot與FreeMarker模板引擎,創(chuàng)建一個簡單的應用,用于根據(jù)數(shù)據(jù)庫數(shù)據(jù)動態(tài)生成Word文檔并下載,感興趣的朋友一起看看吧2024-06-06