开始之前我先发一个题目:
有一份日志记录,每条记录的内容是一个url,每个url都是AAA/BBB/CCC形式,我现在想得到每个url按照AAA分组,并且在按照分组形势输出每个分组中的所有数据
以下是我第一次遇到这个问题的解法:
HashMap> ansFor4 = new HashMap<>(16); while((lineInformation = bufferedReader.readLine()) != null){ int o = key[1].indexOf("/", 1); if(o == -1) { continue; } ansFor4Str1 = key[1].substring(1, o); ansFor4Str2 = key[1].substring(o + 1); if(!ansFor4.containsKey(ansFor4Str1)){ ansFor4.put(ansFor4Str1, new HashSet<>()); } ansFor4.get(ansFor4Str1).add(ansFor4Str2);
思路很简单,创建一个Map
选择我比较熟悉的hash,从HashMultimap开始,进入代码:
// 以下三个是构造方法,都调用了对应的私有构造器
public static HashMultimap create() {
return new HashMultimap();
}
public static HashMultimap create(int expectedKeys, int expectedValuesPerKey) {
return new HashMultimap(expectedKeys, expectedValuesPerKey);
}
public static HashMultimap create(Multimap extends K, ? extends V> multimap) {
return new HashMultimap(multimap);
}
// 私有构造器
private HashMultimap() {
super(new HashMap());
}
private HashMultimap(int expectedKeys, int expectedValuesPerKey) {
super(Maps.newHashMapWithExpectedSize(expectedKeys));
Preconditions.checkArgument(expectedValuesPerKey >= 0);
this.expectedValuesPerKey = expectedValuesPerKey;
}
private HashMultimap(Multimap extends K, ? extends V> multimap) {
super(Maps.newHashMapWithExpectedSize(multimap.keySet().size()));
this.putAll(multimap);
}
选择最简单的一个,点进super看看?
会依次进入HashMultimapGwtSerializationDependencies、AbstractSetMultimap、AbstractMapBasedMultimap,最后的实现就在AbstractMapBasedMultimap中
// map的定义,如果你思考了上面那道题,一定不会对这个数据结构感到奇怪 private transient Map看看其他方法> map; // 构建 protected AbstractMapBasedMultimap(Map > map) { Preconditions.checkArgument(map.isEmpty()); this.map = map; } // Preconditions.checkArgument(boo)方法,异常处理 public static void checkArgument(boolean expression) { if (!expression) { throw new IllegalArgumentException(); } }
//返回set集合
Set createCollection() {
return Sets.newHashSetWithExpectedSize(this.expectedValuesPerKey);
}
// IO,输入输出
@GwtIncompatible
private void writeObject(ObjectOutputStream stream) throws IOException {
stream.defaultWriteObject();
Serialization.writeMultimap(this, stream);
}
@GwtIncompatible
private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
stream.defaultReadObject();
this.expectedValuesPerKey = 2;
int distinctKeys = Serialization.readCount(stream);
Map> map = Maps.newHashMap();
this.setMap(map);
Serialization.populateMultimap(this, stream, distinctKeys);
}
看到这里我很好奇,最重要的增删改查呢?接着在父类中寻找发现,增删改查是在AbstractMapBasedMultimap中实现了
put方法
// put的过程和我之前数据结构的增加操作基本相似
// 首先查看要加入的key在集合中是否存在,如果不存在,创建一个
// 如果存在,直接加入容器
public boolean put(@Nullable K key, @Nullable V value) {
Collection collection = (Collection)this.map.get(key);
if (collection == null) {
// 以下就是创建并且把value加入的过程
collection = this.createCollection(key);
if (collection.add(value)) {
++this.totalSize;
this.map.put(key, collection);
return true;
} else {
// 异常处理
throw new AssertionError("New Collection violated the Collection spec");
}
} else if (collection.add(value)) {
++this.totalSize;
return true;
} else {
return false;
}
}
get方法
public Collection实现区别get(@Nullable K key) { Collection collection = (Collection)this.map.get(key); // 如果没有找到对应集合,创建一个 if (collection == null) { collection = this.createCollection(key); } return this.wrapCollection(key, collection); }
在AbstractMapBasedMultimap中只是最基础的实现,你会看到这里面的方法虽然实现了但是还有很多重写。以put举例,有AbstractListMultimap以及AbstractSetMultimap两种重写
//AbstractListMultimap中的put方法
@CanIgnoreReturnValue
public boolean put(@Nullable K key, @Nullable V value) {
return super.put(key, value);
}
//AbstractSetMultimap中的put方法
@CanIgnoreReturnValue
public boolean put(@Nullable K key, @Nullable V value) {
return super.put(key, value);
}
这两不一样的吗?
对,不过他们的value实现不一样,一个是List,另一个则是Map。其中,AbstractSetMultimap的实现之一就是一开始的HashMultimap,而AbstractListMultimap的继承者是ArrayListMultimap等



