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] }