MultiMap、BidiMap及LazyMap的使用
一、MultiMap
在日常的开发工作中,我们有的时候需要构造像Map<K, List<V>>或者Map<K, Set<V>>这样比较复杂的集合类型的数据结构,以便做相应的业务逻辑处理。
但是像Map<String, List<StudentScore>> StudentScoreMap = new HashMap<String, List<StudentScore>>()这样的数据结构,自己实现起来太麻烦,你需要检查key是否存在,不存在时则创建一个,存在时在List后面添加上一个。这个过程是比较痛苦的,如果你希望检查List中的对象是否存在,删除一个对象,或者遍历整个数据结构,那么则需要更多的代码来实现。
Guava的Multimap就提供了一个方便地把一个键对应到多个值的数据结构。让我们可以简单优雅的实现上面复杂的数据结构,让我们的精力和时间放在实现业务逻辑上,而不是在数据结构上,下面我们具体来看看Multimap的相关知识点。
上面的代码和数据结构用Multimap来实现,代码结构清晰简单了很多吧,具体代码如下:
/**** * 所谓MultiMap,就是说一个key不在是简单的指向一个对象,而是一组对象, * add()和remove()的时候跟普通的Map无异,只是在get()时返回一个Collection, * 利用MultiMap,我们就可以很方便的往一个key上放数量不定的对象,也就实现了一对多。 */ public class MutliMapTest { public static void main(String... args) { Multimap<String, String> myMultimap = ArrayListMultimap.create(); // Adding some key/value myMultimap.put("Fruits", "Bannana"); myMultimap.put("Fruits", "Apple"); myMultimap.put("Fruits", "Pear"); myMultimap.put("Vegetables", "Carrot"); // Getting the size int size = myMultimap.size(); System.out.println(size); // 4 // Getting values Collection<string> fruits = myMultimap.get("Fruits"); System.out.println(fruits); // [Bannana, Apple, Pear] Collection<string> vegetables = myMultimap.get("Vegetables"); System.out.println(vegetables); // [Carrot] // Iterating over entire Mutlimap for(String value : myMultimap.values()) { System.out.println(value); } // Removing a single value myMultimap.remove("Fruits","Pear"); System.out.println(myMultimap.get("Fruits")); // [Bannana, Pear] // Remove all values for a key myMultimap.removeAll("Fruits"); System.out.println(myMultimap.get("Fruits")); // [] (Empty Collection!) List<String> list = new ArrayList<String>(); List<String> valuelist = new ArrayList<String>(); list.add("123"); list.add("456"); list.add("789"); MultiMap multiMap = new MultiHashMap(); multiMap.put("Sean", list); multiMap.put("Sean", "C/C++"); multiMap.put("Sean", "OO"); multiMap.put("Sean", "Java"); multiMap.put("Sean", ".NET"); multiMap.remove("Sean", "C/C++"); System.out.println("Sean's skill set: " + multiMap.get("Sean")); Iterator itet = ((Collection) multiMap.get("Sean")).iterator(); while(itet.hasNext()) { Object obj = itet.next(); if(obj instanceof List) { valuelist = (List<String>)obj; for(Object value:valuelist) { System.out.println("obj1:"+value); } }else if(obj instanceof String) { System.out.println("value:"+obj.toString()); } } } }
Multimap也支持一系列强大的视图功能:
1、asMap把自身Multimap<K, V>映射成Map<K, Collection<V>>视图。这个Map视图支持remove和修改操作,但是不支持put和putAll。严格地来讲,当你希望传入参数是不存在的key,而且你希望返回的是null而不是一个空的可修改的集合的时候就可以调用asMap().get(key)。(你可以强制转型asMap().get(key)的结果类型-对SetMultimap的结果转成Set,对ListMultimap的结果转成List型-但是直接把ListMultimap转成Map<K, List<V>>是不行的。)
2、entries视图是把Multimap里所有的键值对以Collection<Map.Entry<K, V>>的形式展现。
3、keySet视图是把Multimap的键集合作为视图
4、keys视图返回的是个Multiset,这个Multiset是以不重复的键对应的个数作为视图。这个Multiset可以通过支持移除操作而不是添加操作来修改Multimap。
5、values()视图能把Multimap里的所有值“平展”成一个Collection<V>。这个操作和Iterables.concat(multimap.asMap().values())很相似,只是它返回的是一个完整的Collection。
尽管Multimap的实现用到了Map,但Multimap<K, V>不是Map<K, Collection<V>>。因为两者有明显区别: 1.Multimap.get(key)一定返回一个非null的集合。但这不表示Multimap使用了内存来关联这些键,相反,返回的集合只是个允许添加元素的视图。 2.如果你喜欢像Map那样当不存在键的时候要返回null,而不是Multimap那样返回空集合的话,可以用asMap()返回的视图来得到Map<K, Collection<V>>。(这种情况下,你得把返回的Collection<V>强转型为List或Set)。 3.Multimap.containsKey(key)只有在这个键存在的时候才返回true。 4.Multimap.entries()返回的是Multimap所有的键值对。但是如果需要key-collection的键值对,那就得用asMap().entries()。 5.Multimap.size()返回的是entries的数量,而不是不重复键的数量。如果要得到不重复键的数目就得用Multimap.keySet().size()。
注:MultiMap是非线程安全的,转化为线程这全对象时可以参考:
private ListMultimap<Long,Long> map = ArrayListMultimap.create();
private Multimap<Long,Long> syncMap = Multimaps.synchronizedMultimap(map);
二、BidiMap
/**** * 所谓BidiMap,直译就是双向Map,可以通过key找到value, * 也可以通过value找到key,这在我们日常的代码-名称匹配的时候很方便: * 因为我们除了需要通过代码找到名称之外,往往也需要处理用户输入的名称,然后获取其代码。 * 需要注意的是BidiMap当中不光key不能重复,value也不可以。 */ public static void demoBidiMap() { System.out.println(StringUtils.center(" demoBidiMap ", 40, "=")); BidiMap bidiMap = new DualHashBidiMap(); bidiMap.put("BJ", "Beijing"); bidiMap.put("SH", "Shanghai"); bidiMap.put("GZ", "Guangzhou"); bidiMap.put("CD", "Chengdu"); System.out.println("Key-Value: BJ = " + bidiMap.get("BJ")); System.out.println("Value-Key: Chengdu = " + bidiMap.getKey("Chengdu")); System.out.println(StringUtils.repeat("=", 40)); }
三、LazyMap
/**所谓LazyMap,意思就是这个Map中的键/值对一开始并不存在,当被调用到时才创建. * 我们这样来理解:我们需要一个Map,但是由于创建成员的方法很“重”(比如数据库访问), * 或者我们只有在调用get()时才知道如何创建,或者Map中出现的可能性很多很多, * 我们无法在get()之前添加所有可能出现的键/值对, * 我们觉得没有必要去初始化一个Map而又希望它可以在必要时自动处理数据 * */ @SuppressWarnings(value = {"unchecked"}) public static void demoLazyMap() { System.out.println(StringUtils.center(" demoLazyMap ", 40, "=")); Factory factory = new Factory() { public Object create() { return new Date(); } }; Map lazy = LazyMap.decorate(new HashMap(), factory); System.out.println("map:"+lazy);//lazy为空 System.out.println(lazy.get("123")); System.out.println(lazy.get("345")); System.out.println(StringUtils.repeat("=", 40)); }
四、其它