一、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));
}
四、其它