异常描述
Exception in thread “main” java.lang.IllegalArgumentException: Multiple entries with same key: 1=1 and 1=1
异常场景
- 测试环境提测一个功能马上要验收完毕,突然提示下单失败,和测试各种解释与本次更新无关;但测试担心是本次更新的内容影响了整体流程拒绝发送验证报告;
- 查看日志发现,异常信息类似上面,Bean中的一个属性字段重复了,还是作为Map的Key值重复的,Map的Key值是Set,自动过滤重复数据,为何报错
异常模拟
// import com.google.common.base.Function;
// import com.google.common.collect.Maps;
public static void main(String[] args) {
List<Integer> integerList = Arrays.asList(1,2,3,4,5,1,2,3,4);
Map<Integer, Integer> map = Maps.uniqueIndex(integerList, new Function<Integer, Integer>() {
@Override
public Integer apply(Integer input) {
return input.intValue();
}
});
System.out.println(map);
}
源码分析
// 方法调用
public static <K, V> ImmutableMap<K, V> uniqueIndex(Iterable<V> values, Function<? super V, K> keyFunction) {
return uniqueIndex(values.iterator(), keyFunction);
}
// 将集合中的数据放入 TerminalEntry<K, V>[] entries;
public static <K, V> ImmutableMap<K, V> uniqueIndex(Iterator<V> values, Function<? super V, K> keyFunction) {
Preconditions.checkNotNull(keyFunction);
// 初始化 TerminalEntry<K, V>[] entries;
Builder builder = ImmutableMap.builder();
while(values.hasNext()) {
V value = values.next();
builder.put(keyFunction.apply(value), value);
}
return builder.build();
}
// size 大于 1 进入 default 方法
public ImmutableMap<K, V> build() {
switch(this.size) {
case 0:
return ImmutableBiMap.of();
case 1:
return ImmutableBiMap.of(this.entries[0].getKey(), this.entries[0].getValue());
default:
return new RegularImmutableMap(this.size, this.entries);
}
}
RegularImmutableMap(int size, TerminalEntry<?, ?>[] theEntries) {
this.entries = this.createEntryArray(size);
int tableSize = Hashing.closedTableSize(size, 1.2D);
this.table = this.createEntryArray(tableSize);
this.mask = tableSize - 1;
for(int entryIndex = 0; entryIndex < size; ++entryIndex) {
TerminalEntry<K, V> entry = theEntries[entryIndex];
K key = entry.getKey();
// 判断是否有重复数据的方法
// 将每个KEY取hash值与数组长度-1 取余
// 保证数组中每个下标的位置上都有数据
// 如果对新的KEY值Hash后在数组的该下标位置取到数据
// 代表该数据已经存在
int tableIndex = Hashing.smear(key.hashCode()) & this.mask;
ImmutableMapEntry<K, V> existing = this.table[tableIndex];
ImmutableMapEntry<K, V> newEntry = existing == null ? entry : new RegularImmutableMap.NonTerminalMapEntry(entry, existing);
this.table[tableIndex] = (ImmutableMapEntry)newEntry;
this.entries[entryIndex] = (ImmutableMapEntry)newEntry;
this.checkNoConflictInBucket(key, (ImmutableMapEntry)newEntry, existing);
}
}
private void checkNoConflictInBucket(K key, ImmutableMapEntry<K, V> entry, ImmutableMapEntry<K, V> bucketHead) {
// 当已存在数据不空时调用此方法
while(bucketHead != null) {
checkNoConflict(!key.equals(bucketHead.getKey()), "key", entry, bucketHead);
bucketHead = bucketHead.getNextInKeyBucket();
}
}
// 抛出相应异常
static void checkNoConflict(boolean safe, String conflictDescription, Entry<?, ?> entry1, Entry<?, ?> entry2) {
if (!safe) {
String var4 = String.valueOf(String.valueOf(conflictDescription));
String var5 = String.valueOf(String.valueOf(entry1));
String var6 = String.valueOf(String.valueOf(entry2));
throw new IllegalArgumentException((new StringBuilder(34 + var4.length() + var5.length() + var6.length())).append("Multiple entries with same ").append(var4).append(": ").append(var5).append(" and ").append(var6).toString());
}
}
异常反思
- 使用工具类之前一定要熟悉该工具类的使用场景;原来代码中使用此工具类的场景是商品完成下单统计不同的商品编号,但没有考虑到同一个商品一笔单中同时作为商品与赠品,同一个商品不同的价格,所以会记录两次