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 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);
- }
现在:
- 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);
- }
- }
现在:
- Multiset<String> tags = HashMultiset.create();
- for (BlogPost post : getAllBlogPosts()) {
- tags.addAll(post.getTags());
- System.out.println(tags.toString());
- }
Multiset API
- int count(Object element);
-
- int add(E element, int occurrences);
-
- 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);
- }
现在:
- 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;
- }
用了Multimaps:
- 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");
-
-
- 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
- = new ImmutableBiMapBuilder<Integer,String>()
- .put(1, "Hydrogen")
- .put(2, "Helium")
- .put(3, "Lithium")
- .getBiMap();
上篇讲到google collections的几个比较主要的点,今天我们来看看其提供的几个小的但是相当有用的东西。
1,Preconditions
Preconditions 提供了状态校验的方法。
Before:
- public Delivery createDelivery(Order order, User deliveryPerson) {
-
- if(order.getAddress() == null) {
- throw new NullPointerException("order address");
- }
-
- if(!workSchedule.isOnDuty(deliveryPerson, order.getArrivalTime())) {
- throw new IllegalArgumentException(
- String.format("%s is not on duty for %s", deliveryPerson, order));
- }
-
- return new RealDelivery(order, deliveryPerson);
- }
After:
- public Delivery createDelivery(Order order, User deliveryPerson) {
- Preconditions.checkNotNull(order.getAddress(), "order address");
- Preconditions.checkArgument(
- workSchedule.isOnDuty(deliveryPerson, order.getArrivalTime()),
- "%s is not on duty for %s", deliveryPerson, order);
-
- return new RealDelivery(order, deliveryPerson);
- }
2,Iterables.getOnlyElement
Iterables.getOnlyElement 确保你的集合或者迭代器包含了刚好一个元素并且返回该元素。如果他包含0和2+元素,它会抛出RuntimeException。一般在单元测试中使用。
Before:
- public void testWorkSchedule() {
- workSchedule.scheduleUserOnDuty(jesse, mondayAt430pm, mondayAt1130pm);
-
- Set<User> usersOnDuty = workSchedule.getUsersOnDuty(mondayAt800pm);
- assertEquals(1, usersOnDuty.size());
- assertEquals(jesse, usersOnDuty.iterator().next());
- }
After:
- public void testWorkSchedule() {
- workSchedule.scheduleUserOnDuty(jesse, mondayAt430pm, mondayAt1130pm);
-
- Set<User> usersOnDuty = workSchedule.getUsersOnDuty(mondayAt800pm);
- assertEquals(jesse, Iterables.getOnlyElement(usersOnDuty));
- }
Iterables.getOnlyElement比Set.iterator().getNext()和List.get(0)描述的更为直接。
3,Objects.equal
Objects.equal(Object,Object) and Objects.hashCode(Object...)提供了内建的null处理,能使你实现equals() 和hashCode()更加简单。
Before:
- public boolean equals(Object o) {
- if (o instanceof Order) {
- Order that = (Order)o;
-
- return (address != null
- ? address.equals(that.address)
- : that.address == null)
- && (targetArrivalDate != null
- ? targetArrivalDate.equals(that.targetArrivalDate)
- : that.targetArrivalDate == null)
- && lineItems.equals(that.lineItems);
- } else {
- return false;
- }
- }
-
- public int hashCode() {
- int result = 0;
- result = 31 * result + (address != null ? address.hashCode() : 0);
- result = 31 * result + (targetArrivalDate != null ? targetArrivalDate.hashCode() : 0);
- result = 31 * result + lineItems.hashCode();
- return result;
- }
After:
- public boolean equals(Object o) {
- if (o instanceof Order) {
- Order that = (Order)o;
-
- return Objects.equal(address, that.address)
- && Objects.equal(targetArrivalDate, that.targetArrivalDate)
- && Objects.equal(lineItems, that.lineItems);
- } else {
- return false;
- }
- }
-
- public int hashCode() {
- return Objects.hashCode(address, targetArrivalDate, lineItems);
- }
4,Iterables.concat()
Iterables.concat() 连结多种集合 (比如ArrayList和HashSet) 以至于你能在一行代码里遍历他们:
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;
- }
5,Join
Join 是用分隔符分割字符串变得非常容易。
Before:
- public class ShoppingList {
- private List<Item> items = ...;
-
- ...
-
- public String toString() {
- StringBuilder stringBuilder = new StringBuilder();
- for (Iterator<Item> s = items.iterator(); s.hasNext(); ) {
- stringBuilder.append(s.next());
- if (s.hasNext()) {
- stringBuilder.append(" and ");
- }
- }
- return stringBuilder.toString();
- }
- }
After:
- public class ShoppingList {
- private List<Item> items = ...;
-
- ...
-
- public String toString() {
- return Joiner.on(" and ").join(items);
- }
- }
6,Maps, Sets and Lists
泛型是好的,不过他们有些过于罗嗦。
Before:
- Map<CustomerId, BillingOrderHistory> customerOrderHistoryMap
- = new HashMap<CustomerId, BillingOrderHistory>();
After:
- Map<CustomerId, BillingOrderHistory> customerOrderHistoryMap
- = Maps.newHashMap();
Maps, Sets and Lists 包含了工厂方法来创建集合对象。
另一个例子,Before:
- Set<String> workdays = new LinkedHashSet<String>();
- workdays.add("Monday");
- workdays.add("Tuesday");
- workdays.add("Wednesday");
- workdays.add("Thursday");
- workdays.add("Friday");
OR:
- Set<String> workdays = new LinkedHashSet<String>(
- Arrays.asList("Monday", "Tuesday", "Wednesday", "Thursday", "Friday"));
After:
- Set<String> workdays = Sets.newLinkedHashSet(
- "Monday", "Tuesday", "Wednesday", "Thursday", "Friday");
Google Collections 对于Maps, Sets, Lists, Multimaps, Multisets 都提供了工厂方法 。
|