1. 程式人生 > 其它 >JAVA程式設計師必學的Google Guava庫(第一篇)

JAVA程式設計師必學的Google Guava庫(第一篇)

技術標籤:Java

要說Java程式設計師必須要學習的東西的話,Spring當仁不讓,必須是第一個框架,也有另外一個東西,也是新手程式設計師必須要學習的東西,那就是Google的Guava庫,這個庫的作者,和Effective Java的作者是同一人,都來自於Google,學好了這個庫,你的Java的能力,相信會更上一個臺階。Java的優勢是什麼?那就是各種成熟的輪子,Guava就是其中之一,如果想程式碼很少BUG,就開始使用它吧。

目錄
1、基本工具
2、集合

1、基本工具

1.1 避免NULL

NULL的含義是不確定的,應該避免,尤其是在Map和Set中,返回NULL,並不清楚是不存在,還是有值,但是是NULL,最常用的一個技巧就是:

// 好的程式碼
List arr = new ArrayList();
// 不好的程式碼
List arr = null;
Map<Integer,String> map = new HashMap<>();
//處理預設值
map.getOrDefault(123,"預設值");
//沒有123這個KEY的時候,才執行put方法,如果map中已經有值了,就不執行put方法
//並且返回舊值
map.putIfAbsent(123,"123");

這樣可以避免空指標。Guava的建議是: 使用快速失敗操作拒絕null值對開發者更有幫助。

1.2 Guava用
Optional
表示可能為null的T型別引用:

Optional<Integer> possible = Optional.of(50);
possible.isPresent(); // true
possible.get(); // returns 50

2、集合類

2.1 不可變集合

這個屬於防禦性程式設計內容,不可變集合的意思就是集合建立了之後,就不能改變了,類似於:

// SIZE 的值是不可以改變的
final int SIZE = 10;
// final修飾傳統的map,起不到效果
final Map<Integer,String> map = new HashMap<
>(); // 不可以 map = new HashMap<>(); // 可以 map.put(123,"123")

想要設計不可變集合類的主要原因有:

  • 當物件被不可信的庫呼叫時,不可變形式是安全的。
  • 執行緒安全。
  • 容易測試。
  • 安全,不容易出BUG,以防某些錯誤的程式碼修改了集合的內容。

建立的方法(還有1個copyOf方法,這裡不打算引入了):

final ImmutableSet<Integer> set = ImmutableSet.<Integer>builder()
                .add(12)
                .add(30)
                .add(12)
                .build();
ImmutableSortedSet<Integer> sortedSet = ImmutableSortedSet.of(1, 2, 3, 4, 5);
//轉List,取第K小個元素
sortedSet.asList().get(k)

2.2 新集合型別

有一些新的型別可以使用,例如java沒有Table型別,下面將一一介紹。

2.2.1 Multiset

這個東西,可以想象成1個ArrayList,沒有順序,但是它是一個Set,不過可以有重複的Key,還記錄了重複的Key的出現的數量。比方說你要統計一篇文章中出現的單詞的次數:

Map<String, Integer> counts = new HashMap<String, Integer>();
for (String word : words) {
    Integer count = counts.get(word);
    if (count == null) {
        counts.put(word, 1);
    } else {
        counts.put(word, count + 1);
    }
}

這種寫法很笨拙,也容易出錯,並且不支援同時收集多種統計資訊,如總詞數。我們可以做的更好。替代方法:

//模擬文章內容
String [] arr = {"123","123","1234","12345"};
Multiset<String> multiset = HashMultiset.create();
for(String s : arr) {
    multiset.add(s);
}
//統計123出現了幾次
System.out.println(multiset.count("123"));
//統計set中一共有幾個元素
System.out.println(multiset.size());

Multiset介面的方法:

方法描述
count(E)給定元素在Multiset中的計數
elementSet()Multiset中不重複元素的集合,型別為Set
entrySet()和Map的entrySet類似,返回Set<Multiset.Entry>,其中包含的Entry支援getElement()和getCount()方法
add(E, int)增加給定元素在Multiset中的計數
remove(E, int)減少給定元素在Multiset中的計數
setCount(E, int)設定給定元素在Multiset中的計數,不可以為負數
size()返回集合元素的總個數(包括重複的元素)

該介面的實現類:

Map對應的****Multiset是否支援null元素
HashMapHashMultiset
TreeMapTreeMultiset是(如果comparator支援的話)
LinkedHashMapLinkedHashMultiset
ConcurrentHashMapConcurrentHashMultiset
ImmutableMapImmutableMultiset

2.2.2 Multimap

一般都遇到過以下場景,這種可以使用Multimap替代,需要注意Multimap不是Map。

//傳統方法
Map<K, List<V>>
Map<K, Set<V>>
// 替代方法 這裡用ArrayListMultimap來舉例
ArrayListMultimap<String,String> arrayListMultimap = ArrayListMultimap.create();
arrayListMultimap.put("abc","1");
arrayListMultimap.put("abc","12");
arrayListMultimap.put("abc","123");
arrayListMultimap.put("abc","1234");
System.out.println(arrayListMultimap.get("abc"));
// [1, 12, 123, 1234]
//arrayListMultimap.get("abc") 返回的是1個List<String>的值

Multimap提供了多種形式的實現。在大多數要使用Map<K, Collection>的地方,你都可以使用它們:

實現鍵行為類似值行為類似
ArrayListMultimapHashMapArrayList
HashMultimapHashMapHashSet
LinkedListMultimapLinkedHashMapLinkedList
LinkedHashMultimapLinkedHashMapLinkedHashMap
TreeMultimapTreeMapTreeSet
ImmutableListMultimapImmutableMapImmutableList
ImmutableSetMultimapImmutableMapImmutableSet

2.2.3 BiMap

傳統上,實現鍵值對的雙向對映需要維護兩個單獨的map,並保持它們間的同步。但這種方式很容易出錯,而且對於值已經在map中的情況,會變得非常混亂。例如:

Map<String, Integer> nameToId = Maps.newHashMap();
Map<Integer, String> idToName = Maps.newHashMap();
nameToId.put("Bob", 42);
idToName.put(42, "Bob");
//如果"Bob"和42已經在map中了,會發生什麼?
//如果我們忘了同步兩個map,會有詭異的bug發生...

替代方法:

BiMap<String, Integer> nameIdBiMap = HashBiMap.create();
nameIdBiMap.put("a",1);
nameIdBiMap.put("b",2);
nameIdBiMap.put("c",3);
//nameIdBiMap.put("a",4);
System.out.println("a的id:" + nameIdBiMap.get("a"));
System.out.println("id為1:" + nameIdBiMap.inverse().get(1));

如果不是唯一的,會出現什麼情況呢? 例如2個人重名了。如果我們放開了上面的註釋語句,模擬下這種衝突,就會出現ID為1的人被覆蓋掉了,這點在使用的時候,需要特別注意。

BiMap的各種實現

鍵值實現值鍵實現對應的BiMap實現
HashMapHashMapHashBiMap
ImmutableMapImmutableMapImmutableBiMap
EnumMapEnumMapEnumBiMap
EnumMapHashMapEnumHashBiMap

2.3.4 Table

Table有點類似於2維陣列,根據行和列,確認某一個值,沒有Table,老方法就是搞個Map<V,Map<K,V>>這樣的結構來實現。替代方法:

Table<Integer, Integer, Integer> table = HashBasedTable.create();
table.put(1, 1 ,3);
table.put(1, 2, 4);
table.put(2, 1, 5);
table.put(2, 2, 6);

System.out.println(table.row(1)); //{1=3, 2=4}
System.out.println(table.column(1)); //{1=3, 2=5}
System.out.println(table.get(1, 1)); //3

是不是感覺很方便?

2.3.5 RangeSet

這個東西很有意思,在某些場景,我感覺可能會有用,RangeSet的意思是一些非連續區間,例如:
{[1,10], [11,15)} {[1,10], [11,20)} 在數學上,它是不連續的,也可能是連續的。這個時候,想要描述這段區間的時候,可以使用RangeSet這個類。
注:RangeSet不支援GWT,也不支援JDK5和更早版本;因為,RangeSet需要充分利用JDK6中NavigableMap的特性。

RangeSet<Integer> rangeSet = TreeRangeSet.create();
rangeSet.add(Range.closed(1, 10)); // {[1,10]}
rangeSet.add(Range.closedOpen(11, 15));//不相連區間:{[1,10], [11,15)}
rangeSet.add(Range.closedOpen(15, 20)); //相連區間; {[1,10], [11,20)}
rangeSet.add(Range.openClosed(0, 0)); //空區間; {[1,10], [11,20)}
rangeSet.remove(Range.open(5, 10)); //分割[1, 10]; {[1,5], [10,10], [11,20)}
System.out.println(rangeSet.contains(1)); //true
System.out.println(rangeSet.contains(6)); //false

1、RangeSet的實現支援非常廣泛的檢視:

  • complement():返回RangeSet的補集檢視。complement也是RangeSet型別,包含了不相連的、非空的區間。
  • subRangeSet(Range):返回RangeSet與給定Range的交集檢視。這擴充套件了傳統排序集合中的headSet、subSet和tailSet操作。
  • asRanges():用Set<Range>表現RangeSet,這樣可以遍歷其中的Range。
  • asSet(DiscreteDomain)(僅ImmutableRangeSet支援):用ImmutableSortedSet表現RangeSet,以區間中所有元素的形式而不是區間本身的形式檢視。(這個操作不支援DiscreteDomain 和RangeSet都沒有上邊界,或都沒有下邊界的情況)
    2、RangeSet的查詢方法 為了方便操作,RangeSet直接提供了若干查詢方法,其中最突出的有:
  • contains©:RangeSet最基本的操作,判斷RangeSet中是否有任何區間包含給定元素。
  • rangeContaining©:返回包含給定元素的區間;若沒有這樣的區間,則返回null。
  • encloses(Range):簡單明瞭,判斷RangeSet中是否有任何區間包括給定區間。
  • span():返回包括RangeSet中所有區間的最小區間。

2.3.6 其它

還有一些其它的類,就不多寫了,可以自己去看看API文件

  • ClassToInstanceMap
  • RangeMap 這個和RangeSet有點像,相當於某個區間對應了某個值,例如:
RangeMap<Integer, String> rangeMap = TreeRangeMap.create();
rangeMap.put(Range.closed(1, 10), "foo"); //{[1,10] => "foo"}

具體用到了的時候,可以再研究一下。
全文完。 JAVA程式設計師必學的Google Guava庫(第二篇)
參考文件:
併發程式設計網 http://ifeve.com/google-guava/

關注我的部落格,獲取更多Java程式設計知識: 雙King的技術部落格