【Guava的用法】2. collection

创建List、Map

Before
Map <String, Map <Long, List <String >>> map =   new  HashMap <String, Map <Long,List <String >>>();  
 
After:(JDK7将实现该功能)
Map <String, Map <Long, List <String >>> map = Maps.newHashMap();  
 
 
 
Before:
List <String > list =   new  ArrayList <String >();  
list.add( "a" );  
list.add( "b" );  
list.add( "c" );  
list.add( "d" );  
 
After:
List <Integer > list = Lists.newArrayList( 1 ,   2 ,   3 );  

针对不可变集合:
ImmutableList <String > of = ImmutableList.of( "a" ,   "b" ,   "c" ,   "d" );  
ImmutableMap <String,String > map = ImmutableMap.of( "key1" ,   "value1" ,   "key2" ,   "value2" );  
 

Sets

HashSet setA = newHashSet( 1 ,   2 ,   3 ,   4 ,   5 );  
HashSet setB = newHashSet( 4 ,   5 ,   6 ,   7 ,   8 );  
   
SetView union = Sets.union(setA, setB);  
System.out.println( "union:" );  
for  (Integer integer : union)  
    System.out.println(integer);         
   
SetView difference = Sets.difference(setA, setB);  
System.out.println( "difference:" );  
for  (Integer integer : difference)  
    System.out.println(integer);        
   
SetView intersection = Sets.intersection(setA, setB);  
System.out.println( "intersection:" );  
for  (Integer integer : intersection)  
    System.out.println(integer);  


Maps

针对Map的用法:
MapDifference differenceMap = Maps.difference(mapA, mapB);  
  
differenceMap.areEqual();  
Map entriesDiffering = differenceMap.entriesDiffering();  
Map entriesOnlyOnLeft = differenceMap.entriesOnlyOnLeft();  
Map entriesOnlyOnRight = differenceMap.entriesOnlyOnRight();  
Map entriesInCommon = differenceMap. entriesInCommon();  
 

ImmutableList

Before:
public  Directions(Address from, Address to, List <Step > steps) {  
    this .from = from;  
    this .to = to;  
    this .steps = Collections.unmodifiableList( new  ArrayList<Step>(steps));  
}
  

After:
public  Directions(Address from, Address to, List <Step > steps) {  
    this .from = from;  
    this .to = to;  
    this .steps = ImmutableList.of(steps);  
}  
 

Iterables.getOnlyElement()

针对集合中只有一个元素的情况:
Iterables.getOnlyElement(...);
 
这个主要是用来替换Set.iterator.next()或 List.get(0), 而且在测试中使用非常方便, 如果出现0个或者2+则直接抛出异常

比较的最大最小值:
Comparators.max
Comparators.min

Iterables.concat()

Before:
public    boolean  orderContains(Product product) {  
  List <LineItem > allLineItems =   new  ArrayList <LineItem >();  
  allLineItems. addAll(getPurchasedItems());  
  allLineItems.addAll(getFreeItems());  
  
    for  (LineItem lineItem : allLineItems) {  
      if  (lineItem.getProduct() == product) {  
        return   true ;  
    }  
  }  
  
    return   false ;  
}
  

After:
public    boolean  orderContains(Product product) {  
    for  (LineItem lineItem : Iterables.concat(getPurchasedItems(), getFreeItems())) {  
      if  (lineItem.getProduct() == product) {  
        return   true ;  
    }  
  }    
    return   false ;  
}
  
 

Constraints.constrainedList()

给List操作注入约束逻辑, 比如添加不合法元素直接报错.

Before:
private    final  List <LineItem > purchases =   new  ArrayList <LineItem >();  
  
/**  
 * Don't modify this! Instead, call {@link #addPurchase(LineItem)} to add  
 * new purchases to this order.  
 */
   
public  List <LineItem > getPurchases() {  
    return  Collections.unmodifiableList(purchases);  
}  
  
public    void   addPurchase(LineItem purchase) {  
   Preconditions.checkState(catalog.isOffered(getAddress(), purchase.getProduct()));  
  Preconditions.checkState(purchase.getCharge().getUnits() >   0 );  
  purchases.add(purchase);  
}  
 
After:  
private    final  List <LineItem > purchases = Constraints.constrainedList(  
      new  ArrayList <LineItem >(),  
      new  Constraint <LineItem >() {  
        public    void  checkElement(LineItem element) {  
        Preconditions.checkState(catalog.isOffered(getAddress(), element.getProduct()));  
        Preconditions.checkState(element.getCharge().getUnits() >   0 );  
      }  
    });  
  
/**  
 * Returns the modifiable list of purchases in this order.  
 */
   
public  List <LineItem > getPurchases() {  
    return  purchases;  
}
 


不允许插入空值的Set(Constraints的用法):
Set <String > set = Sets.newHashSet();  
Set <String > constrainedSet = Constraints.constrainedSet(set, Constraints.notNull());  
constrainedSet.add( "A" );  
constrainedSet.add( null );   // NullPointerException here   
 

MultiMap

如果一个key对应多个value的Map, 你会怎么处理? 如果还在使用Map<K, List<V>>的话, 你就out了
使用MultiMap吧:
Multimap <Person, BlogPost > multimap = ArrayListMultimap.create();  
 
 
Multimap的另一个使用场景:
比如有一个文章数据的map:
List <Map <String, String >> listOfMaps = mapOf( "type" ,   "blog" ,   "id" ,   "292" ,   "author" ,   "john" );  
 
如果要按照type分组生成一个List
Multimap <String, Map <String, String >> partitionedMap = Multimaps.index(     
                 listOfMaps,                                                                                         
                  new  Function <Map <String, String >, String >() {                                   
                      public  String apply( final  Map <String, String > from) {                      
                          return  from.get( "type" );                                                               
                    }                                                                                                     
                });
   
 
Multimap的用法(允许多值的map):
Before:
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);  
}
 
After:
Multimap<Salesperson, Sale> multimap  =  new ArrayListMultimap <Salesperson,Sale >();  
  
public   void  makeSale(Salesperson salesPerson, Sale sale) {  
   multimap.put(salesperson, sale);  
}
  

Before:
public  Sale getBiggestSale() {  
  Sale biggestSale =  null ;  
    for  (List <Sale > sales : map.values()) {  
    Sale biggestSaleForSalesman  = Collections.max(sales, SALE_COST_COMPARATOR);  
      if  (biggestSale ==  null  || biggestSaleForSalesman.getCharge() > biggestSale().getCharge()) {  
      biggestSale = biggestSaleForSalesman;  
    }  
  }  
    return  biggestSale;  
}
  

After: (需要将map转换成multimap):
public  Sale getBiggestSale() {  
    return   Collections.max(multimap.values(), SALE_COST_COMPARATOR);  
}  
 
 
用来统计多值出现的频率:
Multimap <Integer, String > siblings = Multimaps.newHashMultimap();  
siblings.put( 0 ,   "Kenneth" );  
siblings.put( 1 ,   "Joe" );  
siblings.put( 2 ,   "John" );  
siblings.put( 3 ,   "Jerry" );  
siblings.put( 3 ,   "Jay" );  
siblings.put( 5 ,   "Janet" );  
  
for  ( int  i =   0 ; i <   6 ; i ++) {  
      int  freq = siblings.get(i).size();  
    System.out.printf( "%d siblings frequency %d\n" , i, freq);  
}
 

输出结果:
        0 siblings frequency 1
        1 siblings frequency 1
        2 siblings frequency 1
        3 siblings frequency 2
        4 siblings frequency 0
        5 siblings frequency 1
 

BiMap(双向map)

Before:
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);  
}
  

After:
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();  

 

排序

  • ComparisonChain

compareTo 傳回 1、0、-1 分別來告訴它,順序上物件比另一物件大、相等或小。

用三個值來表示順序,蠻方便的,不是嗎?並不是!有多少次你弄錯了1、0、-1 的意義呢?實際上,排序的要求還蠻多的,例如你可能想要排序時先依某人的姓來排,如果姓相同再依名來排,如果姓名都相同,再依他們居住地的郵遞區號來排,那你就可能會寫出像 compare/compareTo 中的程式碼:

class Person implements Comparable <Person > {
   private String lastName;
   private String firstName;
   private int zipCode;
 
   public int compareTo(Person other) {
     int cmp = lastName.compareTo(other.lastName);
     if (cmp != 0) {
       return cmp;
    }
    cmp = firstName.compareTo(other.firstName);
     if (cmp != 0) {
       return cmp;
    }
     return Integer.compare(zipCode, other.zipCode)
  }
}
 
 
你覺得 compareTo 好讀嗎?如果你學過 SQL,應該知道有 ORDER BY 這東西,相較之下, compareTo 的邏輯並不清楚,如果你使用 Guava 的  ComparisonChain,可以寫成這樣:
import com.google.common.collect.ComparisonChain;
 
class Person implements Comparable <Person > {
   private String lastName;
   private String firstName;
   private int zipCode;
 
   public int compareTo(Person other) {
     return ComparisonChain.start()
             . compare(lastName, other.lastName)
             .compare(firstName, other.firstName)
             .compare(zipCode, other.zipCode)
           .result();
  }
}
 
 

ComparisonChainstartcompare 都會傳回 ComparisonChain 實例,在最後 result 計算結果時,就如原先 compareTo 的實作,會逐一比較要比較的對象,只要它們各自的 compareTo 不為 0 時就傳回結果。

  • Ordering

Ordering 本身就是Comparator,這看看它的類別定義就知道了:
 
public abstract class Ordering<T> extends Object <strong>implements Comparator<T>
 
Collections.sort(names,
          Ordering.natural().reverse().nullsFirst()
                  .onResultOf( new Function <String, Integer >() {
                     @Override
                      public Integer apply(String p) {
                          return p == null ? null : p.length();
                     }
                })
);
 
或者结合Java8 中的Lambda表达式:
Collections.sort(names,
          Ordering.natural().reverse().nullsFirst()
              .onResultOf(p - > p == null ? null : p.length())
);
 
    public Ordering onResultOf(Function function)  {
        return new ByFunctionOrdering(function, this);
    }
 
final class ByFunctionOrdering extends Ordering {
    ByFunctionOrdering(Function function, Ordering ordering)  {
        this.function = (Function)Preconditions.checkNotNull(function);
        this.ordering = (Ordering)Preconditions.checkNotNull(ordering);
    }
    public int compare(Object left, Object right)  {
        return ordering.compare(function.apply(left), function.apply(right));
    }
 
  • Comparators.fromFunction() 

Before:
public  Comparator <Product > createRetailPriceComparator( final  CurrencyConverter currencyConverter) {  
    return    new  Comparator <Product >() {  
        public    int  compare(Product a, Product b) {  
          return   getRetailPriceInUsd(a).compareTo(getRetailPriceInUsd(b));  
      }  
      public  Money getRetailPriceInUsd(Product product) {  
      Money retailPrice = product.getRetailPrice();  
        return  retailPrice.getCurrency() == CurrencyCode.USD  
           ? retailPrice  
           : currencyConverter.convert(retailPrice, CurrencyCode.USD);  
    }  
  };  
}
 

After: (感觉也没省多少):
public  Comparator <Product > createRetailPriceComparator( final  CurrencyConverter currencyConverter) {  
    return   Comparators.fromFunction( new  Function <Product,Money >() {  
      /** returns the retail price in USD */   
      public  Money apply(Product product) {  
      Money retailPrice = product.getRetailPrice();  
        return  retailPrice.getCurrency() == CurrencyCode.USD  
           ? retailPrice  
           : currencyConverter.convert(retailPrice, CurrencyCode.USD);  
    }  
  });  
}
  

Ranges

Range 的一些 static 方法與範圍的對照為:

(a..b) {x | a < x < b} open
[a..b] {x | a <= x <= b} closed
(a..b] {x | a < x <= b} openClosed
[a..b) {x | a <= x < b} closedOpen
(a..+∞) {x | x > a} greaterThan
[a..+∞) {x | x >= a} atLeast
(-∞..b) {x | x < b} lessThan
(-∞..b] {x | x <= b} atMost
(-∞..+∞) {x} all

實際上,範圍不是數列,也就是像 Range.closed(1, 20) 並沒有實際產生 1、2 ... 20 的整數數列,它就僅僅只是個…呃…範圍!如果想要取得的是範圍中的數字,那麼可以透過 ContiguousSet 類別 staticcreate 方法,呼叫時必須指定 Range 物件及一個 DiscreteDomain 物件,DiscreteDomain 物件定義了指定的範圍中,不連續元素間的關係以及 DiscreteDomain 的邊界。

由於經常打算取得的是整數,因此 DiscreteDomain 提供了 integerslongs 以及支援大數的 bigIntegers 三個 static 方法。例如,結合 RangeDiscreteDomain迭代 1 到 20 的數字,可以如下撰寫: 

for( int i : create(Range.closed( 1, 20), integers())) {
     // 做些事 ...           
}
 
 
 
 
 
 

 

猜你喜欢

转载自blog.csdn.net/vking_wang/article/details/17613451
今日推荐