分享

带你领略 Google Collections

 燮羽 2010-11-24

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)”。

 

来看例子程序,以前:

Java代码 
  1. public static final Set<Integer> LUCKY_NUMBERS =   
  2.         Collections.unmodifiableSet(new LinkedHashSet<Integer>(Arrays.asList(4815162342)));  

现在:

Java代码 
  1. public static final ImmutableSet<Integer> LUCKY_NUMBERS =   
  2.         ImmutableSet.of(4815162342);  

Map 也一样,以前:

Java代码 
  1. public static final Map<String, Integer> ENGLISH_TO_INT;  
  2.   
  3. static {  
  4.         Map<String, Integer> map = new LinkedHashMap<String, Integer>();  
  5.         map.put("four"4);  
  6.         map.put("eight"8);  
  7.         map.put("fifteen"15);  
  8.         map.put("sixteen"16);   
  9.         map.put("twenty-three"23);  
  10.         map.put("forty-two"42);  
  11.         ENGLISH_TO_INT = Collections.unmodifiableMap(map);  
  12. }  

 现在:

Java代码 
  1. ImmutableMap<String, Integer> map =   
  2.         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,以前:

Java代码 
  1. Map<String, Integer> tags  
  2.         = new HashMap<String, Integer>();  
  3.   
  4. for (BlogPost post : getAllBlogPosts()) {  
  5.         for (String tag : post.getTags()) {  
  6.                 int value = tags.containsKey(tag) ? tags.get(tag) : 0;  
  7.                 tags.put(tag, value + 1);  
  8.         }  
  9. }  

 现在:

Java代码 
  1. Multiset<String> tags = HashMultiset.create();  
  2.         for (BlogPost post : getAllBlogPosts()) {  
  3.         tags.addAll(post.getTags());  
  4.         System.out.println(tags.toString()); //输出[sasa, yaomin x 2, jubin x 2, lele]  
  5. }  

 Multiset API

Java代码 
  1. int count(Object element);  
  2.   
  3. int add(E element, int occurrences);// occurrences是指element出现的次数  
  4.   
  5. boolean remove(Object element, int occurrences);  
  6.   
  7. int setCount(E element, int newCount);  
  8.   
  9. boolean setCount(E e, int oldCount, int newCount);  

 Multiset implementations:

        ImmutableMultiset,HashMultiset,LinkedHashMultiset,TreeMultiset,EnumMultiset,ConcurrentMultiset

 

3,Multimaps

 

以前:

Java代码 
  1. Map<Salesperson, List<Sale>> map  
  2.       = new HashMap<Salesperson, List<Sale>>();  
  3.   
  4. public void makeSale(Salesperson salesPerson, Sale sale) {  
  5.       List<Sale> sales = map.get(salesPerson);  
  6.       if (sales == null) {  
  7.             sales = new ArrayList<Sale>();  
  8.             map.put(salesPerson, sales);  
  9.       }  
  10.       sales.add(sale);  
  11. }  

 现在:

Java代码 
  1. Multimap<Salesperson, Sale> multimap  
  2.       = ArrayListMultimap.create();  
  3.   
  4. public void makeSale(Salesperson salesPerson, Sale sale) {  
  5.       multimap.put(salesPerson, sale);  
  6. }  

 什么是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:

Java代码 
  1. public Sale getBiggestSale() {  
  2.       Sale biggestSale = null;  
  3.       for (List<Sale> sales : map.values()) {  
  4.             Sale myBiggestSale = Collections.max(sales,  
  5.                   SALE_CHARGE_COMPARATOR);  
  6.             if (biggestSale == null ||  
  7.                   myBiggestSale.getCharge() > biggestSale().getCharge()) {  
  8.                   biggestSale = myBiggestSale;  
  9.             }  
  10.       }  
  11.       return biggestSale;  
  12. }  

 

用了Multimaps:

Java代码 
  1. public Sale getBiggestSale() {  
  2.       return Collections.max(multimap.values(),  
  3.             SALE_CHARGE_COMPARATOR);  
  4. }  
 

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

 

以前:

Java代码 
  1. private static final Map<Integer, String> NUMBER_TO_NAME;  
  2.   private static final Map<String, Integer> NAME_TO_NUMBER;  
  3.     
  4.   static {  
  5.     NUMBER_TO_NAME = Maps.newHashMap();  
  6.     NUMBER_TO_NAME.put(1"Hydrogen");  
  7.     NUMBER_TO_NAME.put(2"Helium");  
  8.     NUMBER_TO_NAME.put(3"Lithium");  
  9.       
  10.     /* reverse the map programatically so the actual mapping is not repeated */  
  11.     NAME_TO_NUMBER = Maps.newHashMap();  
  12.     for (Integer number : NUMBER_TO_NAME.keySet()) {  
  13.       NAME_TO_NUMBER.put(NUMBER_TO_NAME.get(number), number);  
  14.     }  
  15.   }  
  16.   
  17.   public static int getElementNumber(String elementName) {  
  18.     return NUMBER_TO_NAME.get(elementName);  
  19.   }  
  20.   
  21.   public static string getElementName(int elementNumber) {  
  22.     return NAME_TO_NUMBER.get(elementNumber);  
  23.   }  

 现在:

Java代码 
  1. private static final BiMap<Integer,String> NUMBER_TO_NAME_BIMAP;  
  2.     
  3.   static {  
  4.     NUMBER_TO_NAME_BIMAP = Maps.newHashBiMap();  
  5.     NUMBER_TO_NAME_BIMAP.put(1"Hydrogen");  
  6.     NUMBER_TO_NAME_BIMAP.put(2"Helium");  
  7.     NUMBER_TO_NAME_BIMAP.put(3"Lithium");  
  8.   }  
  9.   
  10.   public static int getElementNumber(String elementName) {  
  11.     return NUMBER_TO_NAME_BIMAP.inverse().get(elementName);  
  12.   }  
  13.   
  14.   public static string getElementName(int elementNumber) {  
  15.     return NUMBER_TO_NAME_BIMAP.get(elementNumber);  
  16.   }  

 更好的:

Java代码 
  1. private static final BiMap<Integer,String> NUMBER_TO_NAME_BIMAP  
  2.     = new ImmutableBiMapBuilder<Integer,String>()  
  3.         .put(1"Hydrogen")  
  4.         .put(2"Helium")  
  5.         .put(3"Lithium")  
  6.         .getBiMap();  
 

上篇讲到google collections的几个比较主要的点,今天我们来看看其提供的几个小的但是相当有用的东西。

 

1,Preconditions

 

Preconditions 提供了状态校验的方法。

 

Before:

Java代码 
  1. public Delivery createDelivery(Order order, User deliveryPerson) {  
  2.       
  3.     if(order.getAddress() == null) {  
  4.         throw new NullPointerException("order address");  
  5.     }  
  6.       
  7.     if(!workSchedule.isOnDuty(deliveryPerson, order.getArrivalTime())) {  
  8.         throw new IllegalArgumentException(  
  9.             String.format("%s is not on duty for %s", deliveryPerson, order));  
  10.     }  
  11.   
  12.     return new RealDelivery(order, deliveryPerson);  
  13. }  

 After:

Java代码 
  1. public Delivery createDelivery(Order order, User deliveryPerson) {  
  2.     Preconditions.checkNotNull(order.getAddress(), "order address");  
  3.     Preconditions.checkArgument(  
  4.         workSchedule.isOnDuty(deliveryPerson, order.getArrivalTime()),  
  5.             "%s is not on duty for %s", deliveryPerson, order);  
  6.   
  7.     return new RealDelivery(order, deliveryPerson);  
  8. }  
 

2,Iterables.getOnlyElement

 

Iterables.getOnlyElement 确保你的集合或者迭代器包含了刚好一个元素并且返回该元素。如果他包含0和2+元素,它会抛出RuntimeException。一般在单元测试中使用。

 

Before:

Java代码 
  1. public void testWorkSchedule() {  
  2.     workSchedule.scheduleUserOnDuty(jesse, mondayAt430pm, mondayAt1130pm);  
  3.   
  4.     Set<User> usersOnDuty = workSchedule.getUsersOnDuty(mondayAt800pm);  
  5.     assertEquals(1, usersOnDuty.size());  
  6.     assertEquals(jesse, usersOnDuty.iterator().next());  
  7. }  

 After:

Java代码 
  1. public void testWorkSchedule() {  
  2.     workSchedule.scheduleUserOnDuty(jesse, mondayAt430pm, mondayAt1130pm);  
  3.   
  4.     Set<User> usersOnDuty = workSchedule.getUsersOnDuty(mondayAt800pm);  
  5.     assertEquals(jesse, Iterables.getOnlyElement(usersOnDuty));  
  6. }  

 Iterables.getOnlyElement比Set.iterator().getNext()和List.get(0)描述的更为直接。


3,Objects.equal

 

Objects.equal(Object,Object) and Objects.hashCode(Object...)提供了内建的null处理,能使你实现equals()hashCode()更加简单。

 

Before:

Java代码 
  1. public boolean equals(Object o) {  
  2.     if (o instanceof Order) {  
  3.       Order that = (Order)o;  
  4.   
  5.       return (address != null   
  6.               ? address.equals(that.address)   
  7.               : that.address == null)   
  8.           && (targetArrivalDate != null   
  9.               ? targetArrivalDate.equals(that.targetArrivalDate)   
  10.               : that.targetArrivalDate == null)  
  11.           && lineItems.equals(that.lineItems);  
  12.     } else {  
  13.       return false;  
  14.     }  
  15. }  
  16.   
  17. public int hashCode() {  
  18.     int result = 0;  
  19.     result = 31 * result + (address != null ? address.hashCode() : 0);  
  20.     result = 31 * result + (targetArrivalDate != null ? targetArrivalDate.hashCode() : 0);  
  21.     result = 31 * result + lineItems.hashCode();  
  22.     return result;  
  23. }  

 After:

Java代码 
  1. public boolean equals(Object o) {  
  2.     if (o instanceof Order) {  
  3.       Order that = (Order)o;  
  4.   
  5.       return Objects.equal(address, that.address)  
  6.           && Objects.equal(targetArrivalDate, that.targetArrivalDate)  
  7.           && Objects.equal(lineItems, that.lineItems);  
  8.     } else {  
  9.       return false;  
  10.     }  
  11. }  
  12.   
  13. public int hashCode() {  
  14.     return Objects.hashCode(address, targetArrivalDate, lineItems);  
  15. }  

 

4,Iterables.concat()

 

Iterables.concat() 连结多种集合 (比如ArrayList和HashSet) 以至于你能在一行代码里遍历他们:

 

Before:

Java代码 
  1. public boolean orderContains(Product product) {  
  2.     List<LineItem> allLineItems = new ArrayList<LineItem>();  
  3.     allLineItems.addAll(getPurchasedItems());  
  4.     allLineItems.addAll(getFreeItems());  
  5.   
  6.     for (LineItem lineItem : allLineItems) {  
  7.       if (lineItem.getProduct() == product) {  
  8.         return true;  
  9.       }  
  10.     }  
  11.   
  12.     return false;  
  13. }  

 After:

Java代码 
  1. public boolean orderContains(Product product) {  
  2.     for (LineItem lineItem : Iterables.concat(getPurchasedItems(), getFreeItems())) {  
  3.       if (lineItem.getProduct() == product) {  
  4.         return true;  
  5.       }  
  6.     }  
  7.   
  8.     return false;  
  9. }  

 

5,Join

 

Join 是用分隔符分割字符串变得非常容易。

 

Before:

Java代码 
  1. public class ShoppingList {  
  2.   private List<Item> items = ...;  
  3.   
  4.   ...  
  5.   
  6.   public String toString() {  
  7.     StringBuilder stringBuilder = new StringBuilder();  
  8.     for (Iterator<Item> s = items.iterator(); s.hasNext(); ) {  
  9.       stringBuilder.append(s.next());  
  10.       if (s.hasNext()) {  
  11.         stringBuilder.append(" and ");  
  12.       }  
  13.     }  
  14.     return stringBuilder.toString();  
  15.   }  
  16. }  
 

After:

Java代码 
  1. public class ShoppingList {  
  2.   private List<Item> items = ...;  
  3.   
  4.   ...  
  5.   
  6.   public String toString() {  
  7.     return Joiner.on(" and ").join(items);  
  8.   }  
  9. }  

 

6,Maps, Sets and Lists

 

泛型是好的,不过他们有些过于罗嗦。

 

Before:

Java代码 
  1. Map<CustomerId, BillingOrderHistory> customerOrderHistoryMap   
  2.     = new HashMap<CustomerId, BillingOrderHistory>();  

 After:

Java代码 
  1. Map<CustomerId, BillingOrderHistory> customerOrderHistoryMap   
  2.     = Maps.newHashMap();  

 

Maps, Sets and Lists 包含了工厂方法来创建集合对象。

 

另一个例子,Before:

Java代码 
  1. Set<String> workdays = new LinkedHashSet<String>();  
  2. workdays.add("Monday");  
  3. workdays.add("Tuesday");  
  4. workdays.add("Wednesday");  
  5. workdays.add("Thursday");  
  6. workdays.add("Friday");  

 OR:

Java代码 
  1. Set<String> workdays = new LinkedHashSet<String>(  
  2.   Arrays.asList("Monday""Tuesday""Wednesday""Thursday""Friday"));  

 After:

Java代码 
  1. Set<String> workdays = Sets.newLinkedHashSet(  
  2.   "Monday""Tuesday""Wednesday""Thursday""Friday");  

 

Google Collections 对于Maps, Sets, Lists, Multimaps, Multisets 都提供了工厂方法 。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多