寫了好多和Java集合類有關的文章,學習了好多集合類的用法,有沒有感受仍是有一些常見的需求集合類沒有辦法知足呢?須要本身使用Java集合中的類去實現,可是這種經常使用的輪子Google和apache都幫咱們造好啦.java
Java相關的工具包中有兩個頗有名,Google Guava
和Apache Commons
,今天就來看一下Guava中實現的一些其餘的集合類,基本上都是在JDK的集合類上作了一些加強.apache
在上文Java Collections中,提到了Collections
類中提供了一些能夠返回集合不可變視圖的方法,咱們如今來試用一下.數據結構
咱們新建一個包含5個元素的list.而後建立一個它的不可變視圖.ide
List<Integer> list = new ArrayList<>(Arrays.asList(1,2,3,4,5));
List<Integer> umList = Collections.unmodifiableList(list);
複製代碼
通過上面的步驟,咱們拿到了umList
,他是不可變的,可是list
沒有消失,它仍然是可變的,咱們能夠經過給list
添加一個值來改變umList
的元素集.工具
由於在Collections.unmodifiableList
中,持有了一個list
的引用,全部對list的更改也會同步體如今umList
上.學習
並且上面的代碼中,多了一箇中間變量list
.由於咱們不須要它,咱們建立它只是爲了獲取後面的不可變集合.比較繁瑣.ui
使用Guava
怎麼作呢?像下面這樣:this
ImmutableCollection li = ImmutableList.of(1, 2, 3, 4, 5);
複製代碼
是否是感受清晰了不少,同時,這個li
是真正的不可變的.編碼
ImmutableList還提供了多種建立的方法,好比:spa
copyOf
從一個已有的List來建立上面的代碼以ImmutableList
舉例,可是Guava
還提供了ImmutableSet
,ImmutableMap
,ImmutableCollection
等類,能夠根據須要的數據結構分別調用.
咱們常常會有一種需求,須要在Map結構裏面存List/Set.
好比,統計用戶的簽到日期用來畫日曆,那麼咱們想要的數據是:name->[2019-04-01,2019-04-28]
這樣子的數據結構.
那麼咱們先加入一個用戶在5月1號的簽到記錄怎麼辦呢?寫一會兒代碼,
// 模擬已有的數據結構
static Map<String, List<String>> userSign = new HashMap<>();
// 新放進去一條數據
public static void putIt(String name, String date) {
// 正式的邏輯部分
List<String> dates = userSign.getOrDefault(name, new ArrayList<>());
if (!dates.contains(date)) {
dates.add(date);
}
userSign.put(name, dates);
}
複製代碼
能夠看到比較麻煩,並且要不是有Map的getOrDefault()
方法,代碼還得多幾行,由於還要分爲已存在和未存在來搞..
Guava中提供了一種數據結構,來保存這種一個key對應多個value的狀況,就是MultiMap
.
雖然他的名字帶有map
,可是看源碼能夠發現,他的類生命沒有繼承Map接口.
要使用MultiMap
來實現上面的方法,只須要這樣子:
ArrayListMultimap<String, String> userSign = ArrayListMultimap.create();
userSign.put("huyan", "2019-05-01");
userSign.put("huyan", "2019-05-02");
List<String> huyanSign = userSign.get("huyan");
複製代碼
是的,直接聲明放入就行了,要消費的時候,使用get方法能夠得到一個Arratlist
,遍歷操做便可.
下載了Guava的源碼就能夠發現,其實他裏面就是用Map<String,List>來實現的,這是定義的地方:
能夠看到定義了一個:Map<K,Collection<V>
.定義爲Collection
是爲了實現其餘的集中數據結構.
好比:
HashMultimap
的值是放在set中LinkedListMultimap
的值放在LinkedList中.等等.
老實說這個挺好用的,可是爲啥要叫set呢...你們對set的印象都是不能夠放入重複元素,可是Multiset
的做用就是對重複元素計數..
使用方式以下:
Multiset<String> s = HashMultiset.create();
s.add("pf");
s.add("pf");
s.add("pf");
s.add("hh");
// i =3
int i = s.count("pf");
複製代碼
這個和我前幾天寫的計數器的做用是同樣的,計數器傳送門.
內部實現使用HashMap來保存key->count
的一個映射關係.
這個類是真的實現了JDK的Map接口的,使用它必須保證key和value都沒有重複值.由於他支持根據value獲取key,即將HashMap的key和value進行調換.
BiMap<String, String> m = HashBiMap.create();
m.put("pf", "111");
String value = m.get("pf");
String key = m.inverse().get("111");
複製代碼
這個類適合用在key和value都惟一,且常常會出現根據value來獲取key的狀況.
碰到多個索引一個結果的時候,Map<String,Map<String,Object>>
這種實現方式固然是能夠的,可是實在是太讓人難以看懂和編碼了.
Guava提供了一種名叫Table
的數據結構,能夠優雅的實現.
使用以下:
Table<Integer, Integer, String> tt = HashBasedTable.create();
tt.put(1, 2, "huyan");
String name = tt.get(1, 2);
Map<Integer, String> row = tt.row(1);
Map<Integer, String> colum = tt.column(1);
Set<Table.Cell<Integer, Integer, String>> ha = tt.cellSet();
複製代碼
初始化方式和上面的幾種結構沒有什麼區別,都是經過靜態工廠方法進行初始化,get和put方法根據兩個索引來存放和惟一索引一條數據.
此外,還能夠拿到某一行或者某一列的Map結構,能夠拿到全部單元格的一個set.極大的方便了處理相似於表格的數據.
固然,看一下源碼就會發現,其實Table底層也是使用兩個map的嵌套實現的,可是Java語言嘛,講究的就是一個封裝,雖然咱們能夠本身實現,可是咱們應該作的是去學習一下好的實現方法,看懂,理解而且可以在其餘場景應用相似的思想,而不是每次都要本身寫兩個map.畢竟現成好用的輪子,在適用的場景下仍是應該多多使用加深理解.
在面對多個字段排序比較的場景,通常咱們的代碼都會比較難看,好比對下面這個類:
private static class Student {
int id;
String name;
int age;
}
複製代碼
咱們如今是沒有辦法對其進行比較,或者進行排序的,由於沒有定義對於他的比較策略,假設咱們的策略是:
首先比較id,id相等比較name,name相等比較age,這是一種很常見的多字段比較策略.那麼咱們給Student
類加上Comparable
的實現.
// 爲了簡潔起見,沒有寫外部類代碼,只貼了重寫的comparTo方法.
@Override
public int compareTo(Object o) {
Student s = (Student) o;
int idResult = s.id - this.id;
int nameResult = s.name.compareTo(this.name);
int ageResult = s.age - this.age;
return idResult != 0 ? idResult : nameResult != 0 ? nameResult : ageResult;
}
複製代碼
最後那一串?:?:
是否是看的眼睛疼,固然你能夠選擇三重if-else,我以爲也沒好到哪裏去.
可是可使用ComparisonChain
,這名字一看就是比較鏈,很是適合咱們的場景.改寫以下:
@Override
public int compareTo(Object o) {
Student s = (Student) o;
return ComparisonChain.start().compare(s.id, this.id).compare(s.name, this.name).compare(s.age, this.age).
result();
}
複製代碼
這個代碼可讀性的提高可謂是十分大了,語義十分清晰,能夠很清楚的看懂開始,先比較,再比較,返回結果這樣的一個過程.
上面的ComparisonChain解決了在實現Comparable時候多個字段排序的狀況,那麼JDK中有不少的方法須要提供外部的比較器,這時候咱們但願以多個比較器進行排序呢?
Ordering提供了使用多個比較器的方法,它自身實現了Comparator
接口,能夠集成多個比較器.
Ordering<Student> studentOrdering = Ordering.compound(Arrays.asList((o1, o2) -> {
return ComparisonChain.start().result();
}, (o1, o2) -> 0, (o1, o2) -> 0));
Collections.sort(students, studentOrdering);
複製代碼
上面的代碼中,使用Ordering集成了多個比較器,以後將其自身傳入Collections
的sort
方法,使用它對list進行排序.
以上皆爲我的所思所得,若有錯誤歡迎評論區指正。
歡迎轉載,煩請署名並保留原文連接。
聯繫郵箱:huyanshi2580@gmail.com
更多學習筆記見我的博客------>呼延十