Guava Collections 可以帮助你的代码更简短精炼,更重要是它增强了代码的可读性。
本文使用的Guava版本如下:
<!--guava-->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>25.1-jre</version>
</dependency>
Immutable Collections:
不可变对象有很多优点,包括:
- 当对象被不可信的库调用时,不可变形式是安全的;
- 不可变对象被多个线程调用时,不存在竞态条件问题
- 不可变集合不需要考虑变化,因此可以节省时间和空间。所有不可变的集合都比它们的可变形式有更好的内存利用率(分析和测试细节);
- 不可变对象因为有固定不变,可以作为常量来安全使用。
JDK也提供了Collections.unmodifiableXXX方法把集合包装为不可变形式,但我们认为不够好:
- 还在使用 Collections.unmodifiableXXX() ? Immutable Collections 这才是真正的不可修改的集合
- 重要提示:所有Guava不可变集合的实现都不接受null值
/**
* @Title: Immutable Collections: 真正的不可修改的集合
* @methodName: ImmutableTest
* @param
* @return void
* @Description:
*
* @author: 王延飞
* @date: 2018-06-28 6:07
*/
public static void ImmutableTest() {
// 大家都用过 Collections.unmodifiableXXX() 来做一个不可修改的集合。
// 例如你要构造存储常量的 Set,你可以这样来做 :
Set<String> set = new HashSet<String>(Arrays.asList(new String[]{"RED", "GREEN"}));
Set<String> unmodifiableSet = Collections.unmodifiableSet(set);
System.out.println("Collections.unmodifiableXXX():"+unmodifiableSet);
// 这看上去似乎不错,因为每次调 unmodifiableSet.add() 都会抛出一个 UnsupportedOperationException。
// 感觉安全了?慢!如果有人在原来的 set 上 add 或者 remove 元素会怎么样?
// 结果 unmodifiableSet 也是被 add 或者 remove 元素了。而且构造这样一个简单的 set 写了两句长的代码。
// 下面看看 ImmutableSet 是怎么来做地更安全和简洁 :
ImmutableSet<String> immutableSet = ImmutableSet.of("RED", "GREEN");
System.out.println("ImmutableSet:"+immutableSet);
// 就这样一句就够了,而且试图调 add 方法的时候,它一样会抛出 UnsupportedOperationException。
// 重要的是代码的可读性增强了不少,非常直观地展现了代码的用意。
// 如果像之前这个代码保护一个 set 怎么做呢?你可以 :
ImmutableSet<String> immutableSet2 = ImmutableSet.copyOf(set);
}
Multiset: 看看如何把重复的元素放入一个集合
可以用两种方式看待Multiset:
- 没有元素顺序限制的ArrayList<E>
- Map<E, Integer>,键为元素,值为计数
/**
* @param
* @return void
* @Title: Multiset: 把重复的元素放入集合
* @methodName: MultiSetTest
* @Description: Multiset 并没有实现 java.util.Set 接口,它更像是一个 Bag。
* 普通的 Set 就像这样 :[car, ship, bike],
* 而 Multiset 会是这样 : [car x 2, ship x 6, bike x 3]。
* @author: FLY
* @date: 2018-06-25 10:31
*/
public static void MultiSetTest() {
//create a multiset collection
Multiset<String> multiset = HashMultiset.create();
multiset.add("a");
multiset.add("b");
multiset.add("c");
multiset.add("d");
multiset.add("a");
multiset.add("b");
multiset.add("c");
multiset.add("b");
multiset.add("b");
multiset.add("b");
//print the occurrence of an element
System.out.println("Occurrence of 'b' : " + multiset.count("b"));
//print the total size of the multiset
System.out.println("Total Size : " + multiset.size());
//get the distinct elements of the multiset as set(去重后的元素)
Set<String> set = multiset.elementSet();
//display the elements of the set
System.out.println("Set [");
for (String s : set) {
System.out.println(s);
}
System.out.println("]");
//display all the elements of the multiset using iterator
Iterator<String> iterator = multiset.iterator();
System.out.println("MultiSet [");
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
System.out.println("]");
//display the distinct elements of the multiset with their occurrence count
System.out.println("MultiSet [");
for (Multiset.Entry<String> entry : multiset.entrySet()) {
System.out.println("Element: " + entry.getElement() + ", Occurrence(s): " + entry.getCount());
}
System.out.println("]");
//remove extra occurrences
multiset.remove("b", 2);
//print the occurrence of an element
System.out.println("Occurence of 'b' : " + multiset.count("b"));
}
输出结果
Occurrence of 'b' : 5
Total Size : 10
Set [
a
b
c
d
]
MultiSet [
a
a
b
b
b
b
b
c
c
d
]
MultiSet [
Element: a, Occurrence(s): 2
Element: b, Occurrence(s): 5
Element: c, Occurrence(s): 2
Element: d, Occurrence(s): 1
]
Occurence of 'b' : 3
Multimaps: 一个 key 对应多个 value
- 每个有经验的Java程序员都在某处实现过Map<K, List<V>>或Map<K, Set<V>>,并且要忍受这个结构的笨拙。例如,Map<K, Set<V>>通常用来表示非标定有向图。Guava的 Multimap可以很容易地把一个键映射到多个值。换句话说,Multimap是把键映射到任意多个值的一般方式。
/**
* @param
* @return void
* @Title: Multimap: 在 Map 的 value 里面放多个元素
* @methodName: MultimapTest
* @Description: Muitimap 就是一个 key 对应多个 value 的数据结构。Muitimap 是 :{k1=[v1, v2, v3], k2=[v7, v8],....}。
* @author: FLY
* @date: 2018-06-23 7:04
*/
public static void MultimapTest() {
Multimap<String, Integer> map = HashMultimap.create(); //Multimap是把键映射到任意多个值的一般方式
map.put("FLY", 1); //key相同时不会覆盖原value
map.put("FLY", 2);
map.put("FLY", 3);
map.put("ALEX", 4);
map.put("ALEX", 5);
System.out.println(map); //{ALEX=[4, 5], FLY=[1, 2, 3]}
System.out.println(map.get("FLY")); //返回的是集合 // [1, 2, 3]
System.out.println(map.size()); //返回所有”键-单个值映射”的个数,而非不同键的个数 // 5
System.out.println(map.keySet().size()); //返回不同key的个数 // 2
Map<String, Collection<Integer>> mapView = map.asMap();
System.out.println(mapView);// {ALEX=[4, 5], FLY=[1, 2, 3]}
}
BiMap: 保证 value 也不重复
BiMap<K, V>是特殊的Map:
- 可以用 inverse()反转BiMap<K, V>的键值映射
- 保证值是唯一的,因此 values()返回Set而不是普通的Collection
/**
* @param
* @return void
* @Title: BiMap: 双向 Map
* @methodName: BiMapTest
* @Description: 它的特点是它的 value 和它 key 一样也是不可重复的,换句话说它的 key 和 value 是等价的。
* 如果你往 BiMap 的 value 里面放了重复的元素,就会得到 IllegalArgumentException。
* @author: FLY
* @date: 2018-06-25 6:28
*/
public static void BiMapTest() {
BiMap<Integer, String> empIDNameMap = HashBiMap.create();
empIDNameMap.put(new Integer(101), "Mahesh");
empIDNameMap.put(new Integer(102), "Sohan");
empIDNameMap.put(new Integer(103), "Ramesh");
// empIDNameMap.put(new Integer(104), "Mahesh"); // 报错: java.lang.IllegalArgumentException: value already present: Mahesh
//Emp Id of Employee "Mahesh"
System.out.println(empIDNameMap.inverse().get("Mahesh"));
}
MapMaker: 超级强大的 Map 构造类
/**
* @Title: MapMaker: 超级强大的 Map 构造工具
* @methodName: MapMakerTest
* @param
* @return void
* @Description:
*
* @author: 王延飞
* @date: 2018-06-27 6:51
*/
public static void MapMakerTest() {
//MapMaker 是用来构造 ConcurrentMap 的工具类。它可以用来构造ConcurrentHashMap:得到线程安全的hashMap
//ConcurrentHashMap with concurrency level 8
ConcurrentMap<String, Object> map1 = new MapMaker()
.concurrencyLevel(8)
.makeMap();
}
Collections2: 过滤器-Filter
/**
* @Title: Collections2.filter() 方法过滤集合中不符合条件的元素
* @methodName: Collections2FilterTest
* @param
* @return void
* @Description:
*
* @author: 王延飞
* @date: 2018-06-28 7:02
*/
public static void Collections2FilterTest() {
ArrayList<Integer> list = Lists.newArrayList(1,2,5,6,8,9,4,11,23,45,56,78);
// 过滤一个 List<Integer> 里面大于 10 的元素 :
Collection<Integer> filterCollection =
Collections2.filter(list, new Predicate<Integer>(){
@Override
public boolean apply(Integer input) {
return input <= 10;
}});
System.out.println("filterCollection:"+filterCollection); // filterCollection:[1, 2, 5, 6, 8, 9, 4]
}
Collections2: 转换器-Transform
/**
* @Title: 转换器
* @methodName: Collections2TransformTest
* @param
* @return void
* @Description: 利用 Collections2.transform() 方法来转换集合中的元素
*
* @author: 王延飞
* @date: 2018-06-28 7:37
*/
public static void Collections2TransformTest() {
// 把一个 Set<Integer> 里面所有元素都转换成带格式的 String 来产生新的 Collection<String>:
Collection<Integer> set = Lists.newArrayList(1123,4590,5667,7800);
Collection<String> transformtCollection =
Collections2.transform(set, input -> new DecimalFormat("#,###").format(input));
System.out.println("transformtCollection:"+transformtCollection); // transformtCollection:[1,123, 4,590, 5,667, 7,800]
}