原貼地址:
http://jubin2002.javaeye.com/blog/471661
Java的集合框架是Java類庫當中使用頻率最高的部分之一,Google公司發起了一個項目,用來擴展Java的集合框架,提供一些高級的集合操作API。
http://code.google.com/p/google-collections/
這個項目叫做Google Collection,託管在Google Code上面,它必須使用JDK5.0以上的版本
下面,讓我帶你領略下這個項目的優雅之處吧
1,Immutable Collections
什麼是Immutable?
Immutable是不可改變的意思。
在JDK中有Collections.unmodifiableFoo()來轉換,不過他們之間依然有區別。 Collections.unmodifiableFoo()只是原集合的一個視圖,在這個視圖層面無法修改,但當原集合發生改變時,他也會跟着改變。而 ImmutableFoo則是在任何情況下均無法修改。
Immutable有什麼作用呢?
他們更加容易使用並且安全不易出錯。(更多原因請參看Effective JAVA第二版15條)
Immutable vs. unmodifiable
雖然JDK的unmodifiable方法也能保證集合視圖的不變性。但是Immutable能“保證不可改變”,“極易使用”,“更快”,“使用更少的內存(比如ImmutableSet能少2-3X)”。
來看例子程序,以前:
- public static final Set<Integer> LUCKY_NUMBERS =
- Collections.unmodifiableSet(new LinkedHashSet<Integer>(Arrays.asList( 4 , 8 , 15 , 16 , 23 , 42 )));
public static final Set<Integer> LUCKY_NUMBERS =
Collections.unmodifiableSet(new LinkedHashSet<Integer>(Arrays.asList(4, 8, 15, 16, 23, 42)));
現在:
- public static final ImmutableSet<Integer> LUCKY_NUMBERS =
- ImmutableSet.of(4 , 8 , 15 , 16 , 23 , 42 );
public static final ImmutableSet<Integer> LUCKY_NUMBERS =
ImmutableSet.of(4, 8, 15, 16, 23, 42);
Map 也一樣,以前:
- public static final Map<String, Integer> ENGLISH_TO_INT;
- static {
- Map<String, Integer> map = new LinkedHashMap<String, Integer>();
- map.put("four" , 4 );
- map.put("eight" , 8 );
- map.put("fifteen" , 15 );
- map.put("sixteen" , 16 );
- map.put("twenty-three" , 23 );
- map.put("forty-two" , 42 );
- ENGLISH_TO_INT = Collections.unmodifiableMap(map);
- }
public static final Map<String, Integer> ENGLISH_TO_INT;
static {
Map<String, Integer> map = new LinkedHashMap<String, Integer>();
map.put("four", 4);
map.put("eight", 8);
map.put("fifteen", 15);
map.put("sixteen", 16);
map.put("twenty-three", 23);
map.put("forty-two", 42);
ENGLISH_TO_INT = Collections.unmodifiableMap(map);
}
現在:
- ImmutableMap<String, Integer> map =
- ImmutableMap.of("four" , 4 , "eight" , 8 , "fifteen" , 15 , "sixteen" , 16 , "twenty-three" , 23 , "forty-two" , 42 );
ImmutableMap<String, Integer> map =
ImmutableMap.of("four", 4,"eight", 8, "fifteen", 15, "sixteen", 16, "twenty-three", 23,"forty-two", 42);
在google,In the past, we'd ask, "does this need to be immutable?"
Now we ask, "does it need to be mutable?"
2,Multisets
什麼是Multisets?
當我們有一捆東西的時候我們就會想到用集合,但是我們要用什麼樣的集合呢,在這,我們會考慮以下幾點
1)他能重複麼,2)他的排序有意義麼,3)他的插入順序
List:有序,可以重複,Set:無序,不可以重複
而MultiSets是無序,可以重複的
請看例子,tag,以前:
- Map<String, Integer> tags
- = new HashMap<String, Integer>();
- for (BlogPost post : getAllBlogPosts()) {
- for (String tag : post.getTags()) {
- int value = tags.containsKey(tag) ? tags.get(tag) : 0 ;
- tags.put(tag, value + 1 );
- }
- }
Map<String, Integer> tags
= new HashMap<String, Integer>();
for (BlogPost post : getAllBlogPosts()) {
for (String tag : post.getTags()) {
int value = tags.containsKey(tag) ? tags.get(tag) : 0;
tags.put(tag, value + 1);
}
}
現在:
- Multiset<String> tags = HashMultiset.create();
- for (BlogPost post : getAllBlogPosts()) {
- tags.addAll(post.getTags());
- System.out.println(tags.toString()); //輸出[sasa, yaomin x 2, jubin x 2, lele]
- }
Multiset<String> tags = HashMultiset.create();
for (BlogPost post : getAllBlogPosts()) {
tags.addAll(post.getTags());
System.out.println(tags.toString()); //輸出[sasa, yaomin x 2, jubin x 2, lele]
}
Multiset API
- int count(Object element);
- int add(E element, int occurrences); // occurrences是指element出現的次數
- boolean remove(Object element, int occurrences);
- int setCount(E element, int newCount);
- boolean setCount(E e, int oldCount, int newCount);
int count(Object element);
int add(E element, int occurrences);// occurrences是指element出現的次數
boolean remove(Object element, int occurrences);
int setCount(E element, int newCount);
boolean setCount(E e, int oldCount, int newCount);
Multiset implementations:
ImmutableMultiset,HashMultiset,LinkedHashMultiset,TreeMultiset,EnumMultiset,ConcurrentMultiset
3,Multimaps
以前:
- Map<Salesperson, List<Sale>> map
- = new HashMap<Salesperson, List<Sale>>();
- public void makeSale(Salesperson salesPerson, Sale sale) {
- List<Sale> sales = map.get(salesPerson);
- if (sales == null ) {
- sales = new ArrayList<Sale>();
- map.put(salesPerson, sales);
- }
- sales.add(sale);
- }
Map<Salesperson, List<Sale>> map
= new HashMap<Salesperson, List<Sale>>();
public void makeSale(Salesperson salesPerson, Sale sale) {
List<Sale> sales = map.get(salesPerson);
if (sales == null) {
sales = new ArrayList<Sale>();
map.put(salesPerson, sales);
}
sales.add(sale);
}
現在:
- Multimap<Salesperson, Sale> multimap
- = ArrayListMultimap.create();
- public void makeSale(Salesperson salesPerson, Sale sale) {
- multimap.put(salesPerson, sale);
- }
Multimap<Salesperson, Sale> multimap
= ArrayListMultimap.create();
public void makeSale(Salesperson salesPerson, Sale sale) {
multimap.put(salesPerson, sale);
}
什麼是Multimaps?
類似Map的一種以key-value是對的集合,不過他的key不需要唯一。{a=1, a=2, b=3, c=4, c=5, c=6}
multimap.get(key)會返回一個可修改的集合視圖,或者你也可以把它看做Map<K, Collection<V>>
{a=[1, 2], b=[3], c=[4, 5, 6]}
再看一個例子,在上個例子的基礎上找到最大的sale,沒用Multimaps:
- public Sale getBiggestSale() {
- Sale biggestSale = null ;
- for (List<Sale> sales : map.values()) {
- Sale myBiggestSale = Collections.max(sales,
- SALE_CHARGE_COMPARATOR);
- if (biggestSale == null ||
- myBiggestSale.getCharge() > biggestSale().getCharge()) {
- biggestSale = myBiggestSale;
- }
- }
- return biggestSale;
- }
public Sale getBiggestSale() {
Sale biggestSale = null;
for (List<Sale> sales : map.values()) {
Sale myBiggestSale = Collections.max(sales,
SALE_CHARGE_COMPARATOR);
if (biggestSale == null ||
myBiggestSale.getCharge() > biggestSale().getCharge()) {
biggestSale = myBiggestSale;
}
}
return biggestSale;
}
用了Multimaps:
- public Sale getBiggestSale() {
- return Collections.max(multimap.values(),
- SALE_CHARGE_COMPARATOR);
- }
public Sale getBiggestSale() {
return Collections.max(multimap.values(),
SALE_CHARGE_COMPARATOR);
}
Multimap有6個有用的方法,get(),keys(), keySet(), values(), entries(), asMap()。
大多數Map的方法和Multimaps是一樣的,比如說,size(), isEmpty(),containsKey(), containsValue()
put(), putAll(),clear(),values()
有些有點區別,比如說,get()返回Collection<V>而不是V,remove(K)變成remove(K,V)和removeAll(K),keySet()變成keys(),entrySet()變成entries()
Multimap implementations:ImmutableMultimap,ArrayListMultimap,HashMultimap
LinkedHashMultimap,TreeMultimap
3,BiMap
BiMap又名unique-valued map,也就是說,他的key和value都是不能重複的,這就導致了他的key和value能互相轉換,bimap.inverse().inverse() == bimap
BiMap implementations:ImmutableBiMap,HashBiMap,EnumBiMap
以前:
- private static final Map<Integer, String> NUMBER_TO_NAME;
- private static final Map<String, Integer> NAME_TO_NUMBER;
- static {
- NUMBER_TO_NAME = Maps.newHashMap();
- NUMBER_TO_NAME.put(1 , "Hydrogen" );
- NUMBER_TO_NAME.put(2 , "Helium" );
- NUMBER_TO_NAME.put(3 , "Lithium" );
- /* reverse the map programatically so the actual mapping is not repeated */
- NAME_TO_NUMBER = Maps.newHashMap();
- for (Integer number : NUMBER_TO_NAME.keySet()) {
- NAME_TO_NUMBER.put(NUMBER_TO_NAME.get(number), number);
- }
- }
- public static int getElementNumber(String elementName) {
- return NUMBER_TO_NAME.get(elementName);
- }
- public static string getElementName( int elementNumber) {
- return NAME_TO_NUMBER.get(elementNumber);
- }
private static final Map<Integer, String> NUMBER_TO_NAME;
private static final Map<String, Integer> NAME_TO_NUMBER;
static {
NUMBER_TO_NAME = Maps.newHashMap();
NUMBER_TO_NAME.put(1, "Hydrogen");
NUMBER_TO_NAME.put(2, "Helium");
NUMBER_TO_NAME.put(3, "Lithium");
/* reverse the map programatically so the actual mapping is not repeated */
NAME_TO_NUMBER = Maps.newHashMap();
for (Integer number : NUMBER_TO_NAME.keySet()) {
NAME_TO_NUMBER.put(NUMBER_TO_NAME.get(number), number);
}
}
public static int getElementNumber(String elementName) {
return NUMBER_TO_NAME.get(elementName);
}
public static string getElementName(int elementNumber) {
return NAME_TO_NUMBER.get(elementNumber);
}
現在:
- private static final BiMap<Integer,String> NUMBER_TO_NAME_BIMAP;
- static {
- NUMBER_TO_NAME_BIMAP = Maps.newHashBiMap();
- NUMBER_TO_NAME_BIMAP.put(1 , "Hydrogen" );
- NUMBER_TO_NAME_BIMAP.put(2 , "Helium" );
- NUMBER_TO_NAME_BIMAP.put(3 , "Lithium" );
- }
- public static int getElementNumber(String elementName) {
- return NUMBER_TO_NAME_BIMAP.inverse().get(elementName);
- }
- public static string getElementName( int elementNumber) {
- return NUMBER_TO_NAME_BIMAP.get(elementNumber);
- }
private static final BiMap<Integer,String> NUMBER_TO_NAME_BIMAP;
static {
NUMBER_TO_NAME_BIMAP = Maps.newHashBiMap();
NUMBER_TO_NAME_BIMAP.put(1, "Hydrogen");
NUMBER_TO_NAME_BIMAP.put(2, "Helium");
NUMBER_TO_NAME_BIMAP.put(3, "Lithium");
}
public static int getElementNumber(String elementName) {
return NUMBER_TO_NAME_BIMAP.inverse().get(elementName);
}
public static string getElementName(int elementNumber) {
return NUMBER_TO_NAME_BIMAP.get(elementNumber);
}
更好的:
- private static final BiMap<Integer,String> NUMBER_TO_NAME_BIMAP
- = new ImmutableBiMapBuilder<Integer,String>()
- .put(1 , "Hydrogen" )
- .put(2 , "Helium" )
- .put(3 , "Lithium" )
- .getBiMap();