集合
普通集合
1
2
3
|
List<String> list = Lists.newArrayList(); Set<String> set = Sets.newHashSet(); Map<String, String> map = Maps.newHashMap(); |
Set 取交集、并集、差集
1
2
3
4
5
6
7
8
|
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 取交集、并集、差集
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
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)
不可變集合的特性有:
- 在多線程操作下,是線程安全的;
- 所有不可變集合會比可變集合更有效的利用資源;
- 中途不可改變。
如果你的需求是想創建一個一經初始化后就不能再被改變的集合那么它適合你,因為這些工具類根本就沒給你提供修改的 API,這意味著你連犯錯誤的機會都沒有。
1
2
3
|
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 開頭的相關集合類的 add、remove 方法都被聲明為 deprecated。當你手誤點到了這些方法發現是 deprecated 的時候你不會還想著使用吧。
注意:每個Guava immutable集合類的實現都拒絕 null 值。
有趣的集合
MultiSet: 無序+可重復
我們映像中的 Set 應該是無序的,元素不可重復的。MultiSet 顛覆了三觀,因為它可以重復。
定義一個 MultiSet 并添加元素:
1
2
3
4
5
6
|
Multiset<Integer> set = HashMultiset.create(); set.add( 3 ); set.add( 3 ); set.add( 4 ); set.add( 5 ); set.add( 4 ); |
你還可以添加指定個數的同一個元素:
1
|
set.add( 7 , 3 ); |
這表示你想添加 3 個 7。
打印出來的 MultiSet 也很有意思:
1
|
[ 3 x 2 , 4 x 2 , 5 , 7 x 3 ] |
2個3,2個4,一個5,3個7。
獲取某個元素的個數:
1
|
int count = set.count( 3 ); |
這個工具類確實很有意思,幫我們實現了 word count。
Multimap :key 可以重復的 map
這個 map 也很有意思。正常的 map 為了區分不同的 key,它倒好,直接給你來一樣的 key 。
1
2
3
4
5
|
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 對應的兩個值,結果用 list 返回。恕我無知,我還沒想到這個 map 能夠使用的場景。
Multimap 提供了多種實現:
Multimap 實現 | 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 重復了則會報錯。
1
2
3
4
5
6
7
8
|
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“ 有兩個,運行可以發現 get 的時候會用 ”haha1" 覆蓋 ”haha“,另外 value 為 ”haha“ 也有兩個,你會發現運行上面的代碼不會報錯,這是因為 ”key“ 對應的 value 已經被 "haha1" 覆蓋了。否則是會報錯。
雙鍵 map - 超級實用
雙鍵的 map ,我突然感覺我發現了新大陸。比如我有一個業務場景是:根據職位和部門將公司人員區分開來。key 可以用職位 + 部門組成一個字符串,那我們有了雙鍵 map 之后就沒這種煩惱。
1
2
3
4
5
6
7
|
public static void main(String[] args) { Table<String, String, List<Object>> tables = HashBasedTable.create(); tables.put( "財務部" , "總監" , Lists.newArrayList()); tables.put( "財務部" , "職員" ,Lists.newArrayList()); tables.put( "法務部" , "助理" ,Lists.newArrayList()); System.out.println(tables); } |
工具類
JDK里大家耳熟能詳的是Collections
這個集合工具類, 提供了一些基礎的集合處理轉換功能, 但是實際使用里很多需求并不是簡單的排序, 或者比較數值大小, 然后 Guava 在此基礎上做了許多的改進優化, 可以說是 Guava 最為成熟/流行的模塊之一。
- 數組相關:Lists
- 集合相關:Sets
- map 相關:Maps
連接符(Joiner)和分隔符(Splitter)
Joiner 做為連接符的使用非常簡單,下例是將 list 轉為使用連接符連接的字符串:
1
2
3
4
5
6
7
8
|
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 轉為自定義連接符連接的字符串:
1
2
3
4
5
6
7
|
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 的使用也很簡單:
1
2
3
4
5
|
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 ] |
如果字符串中帶有空格,還可以先去掉空格:
1
2
3
|
String str = "1-2-3-4- 5- 6 " ; List<String> list = Splitter.on( "-" ).omitEmptyStrings().trimResults().splitToList(str); System.out.println(list); |
將 String 轉為 map:
1
2
3
4
5
|
String str = "key1=54,key2=28" ; Map<String,String> map = Splitter.on( "," ).withKeyValueSeparator( "=" ).split(str); System.out.println(map); 輸出: {key1= 54 , key2= 28 } |
Comparator 的實現
Java 提供了 Comparator 可以用來對對象進行排序。Guava 提供了排序器 Ordering 類封裝了很多實用的操作。
Ordering 提供了一些有用的方法:
- natural() 對可排序類型做自然排序,如數字按大小,日期按先后排序
- usingToString() 按對象的字符串形式做字典排序[lexicographical ordering]
- from(Comparator) 把給定的Comparator轉化為排序器
- reverse() 獲取語義相反的排序器
- nullsFirst() 使用當前排序器,但額外把null值排到最前面。
- nullsLast() 使用當前排序器,但額外把null值排到最后面。
-
compound(Comparator) 合成另一個比較器,以處理當前排序器中的相等情況。 lexicographical() 基于處理類型T的排序器,返回該類型的可迭代對象Iterable
的排序器。 -
onResultOf(Function) 對集合中元素調用Function,再按返回值用當前排序器排序。
示例:
1
2
3
4
|
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。
統計中間代碼運行時間
Stopwatch 類提供了時間統計的功能,相當于幫你封裝了調用 System.currentTimeMillis() 的邏輯。
1
2
3
4
5
6
7
8
9
|
Stopwatch stopwatch = Stopwatch.createStarted(); try { //TODO 模擬業務邏輯 Thread.sleep(2000L); } catch (InterruptedException e) { e.printStackTrace(); } long nanos = stopwatch.elapsed(TimeUnit.SECONDS); System.out.println(nanos); |
Guava Cache - 本地緩存組件
Guava Cache 在日常的使用中非常地頻繁,甚至都沒有意識到這是第三方提供的工具類而是把它當成了 JDK 自帶的實現。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
// LoadingCache是Cache的緩存實現 LoadingCache<String, Object> cache = CacheBuilder.newBuilder() //設置緩存大小 .maximumSize( 1000 ) //設置到期時間 .expireAfterWrite( 10 , TimeUnit.MINUTES) //設置緩存里的值兩分鐘刷新一次 .refreshAfterWrite( 2 , TimeUnit.MINUTES) //開啟緩存的統計功能 .recordStats() //構建緩存 .build( new CacheLoader<String, Object>() { //此處實現如果根據key找不到value需要去如何獲取 @Override public Object load(String s) throws Exception { return new Object(); } //如果批量加載有比反復調用load更優的方法則重寫這個方法 @Override public Map<String, Object> loadAll(Iterable<? extends String> keys) throws Exception { return super .loadAll(keys); } }); |
設置本地緩存使用 CacheBuilder.newBuilder(),支持設置緩存大小,緩存過期時間,緩存刷新頻率等等。如果你想統計緩存的命中率, Guava Cache 也提供了這種能力幫你匯總當前緩存是否有效。
同時緩存如果因為某種原因未自動刷新或者清除,Guava Cache 也支持用戶手動調用 API 刷新或者清除緩存。
1
2
3
4
5
6
|
cache.invalidateAll(); //清除所有緩存項 //清理的時機:在寫操作時順帶做少量的維護工作,或者偶爾在讀操作時做——如果寫操作實在太少的話 //如果想自己維護則可以調用Cache.cleanUp(); cache.cleanUp(); //另外有時候需要緩存中的數據做出變化重載一次,這個過程可以異步執行 cache.refresh( "key" ); |
單機限流工具類 - RateLimiter
常用的限流算法有 漏桶算法、令牌桶算法。這兩種算法各有側重點:
- 漏桶算法:漏桶的意思就像一個漏斗一樣,水一滴一滴的滴下去,流出是勻速的。當訪問量過大的時候這個漏斗就會積水。漏桶算法的實現依賴隊列,一個處理器從隊頭依照固定頻率取出數據進行處理。如果請求量過大導致隊列堆滿那么新來的請求就會被拋棄。漏桶一般按照固定的速率流出。
- 令牌桶則是存放固定容量的令牌,按照固定速率從桶中取出令牌。初始給桶中添加固定容量令牌,當桶中令牌不夠取出的時候則拒絕新的請求。令牌桶不限制取出令牌的速度,只要有令牌就能處理。所以令牌桶允許一定程度的突發,而漏桶主要目的是平滑流出。
RateLimiter 使用了令牌桶算法,提供兩種限流的實現方案:
- 平滑突發限流(SmoothBursty)
- 平滑預熱限流(SmoothWarmingUp)
實現平滑突發限流通過 RateLimiter 提供的靜態方法來創建:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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 ...... |
設置每秒放置的令牌數為 5 個,基本 0.2s 一次符合每秒 5 個的設置。保證每秒不超過 5 個達到了平滑輸出的效果。
在沒有請求使用令牌桶的時候,令牌會先創建好放在桶中,所以此時如果突然有突發流量進來,由于桶中有足夠的令牌可以快速響應。RateLimiter 在沒有足夠令牌發放時采用滯后處理的方式,前一個請求獲取令牌所需等待的時間由下一次請求來承受。
平滑預熱限流并不會像平滑突發限流一樣先將所有的令牌創建好,它啟動后會有一段預熱期,逐步將分發頻率提升到配置的速率。
比如下面例子創建一個平均分發令牌速率為 2,預熱期為 3 分鐘。由于設置了預熱時間是 3 秒,令牌桶一開始并不會 0.5 秒發一個令牌,而是形成一個平滑線性下降的坡度,頻率越來越高,在 3 秒鐘之內達到原本設置的頻率,以后就以固定的頻率輸出。這種功能適合系統剛啟動需要一點時間來“熱身”的場景。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
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 |
從上面的輸出看前面兩次獲取令牌都很耗時,往后就越來越趨于平穩。
今天給大家介紹的常用的 Guava 工具類就這些,不過 JDK8 開始 Java官方 API 也在完善,比如像字符串相關的功能 JDK也很強大。都是工具,哪個好用就用哪個。
到此這篇關于java的Guava工具包介紹的文章就到這了,更多相關Guava工具包內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章,希望大家以后多多支持服務器之家!
原文鏈接:https://www.cnblogs.com/rickiyang/p/14661374.html