【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())) {
    // 做些事 ...           
}
 
 
 
 
 
 

 
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读