为何HashMap<Boolean,String>仅能存储2条数据?如何解决?
嘿,这个问题其实很好理解,咱们拆解来看:
问题的核心原因
Boolean类型本质上只有两个可能的取值:true和false。而HashMap的核心规则是key必须唯一——当你用同一个key调用put()方法时,新的value会直接覆盖掉之前存在的value。
看你代码里的逻辑:
if (!metadatas.containsValue(indexable.indexName())) { metadatas.put(indexable.dictionary(), indexable.indexName()); }
这里的判断逻辑有漏洞:就算indexName不在map的value里,但只要dictionary(布尔值)这个key已经存在,新的indexName还是会把旧的value覆盖掉。而且containsValue()的效率特别低,因为它要遍历整个map的所有value才能判断,数据量越大越慢。
所以不管你循环多少次,最终map里最多只能有两条数据:一条key是true,一条是false。
两种可行的解决方案
根据你的业务需求,我给你两种常见的处理思路:
1. 更换唯一的Key类型
如果你的indexName是唯一标识,或者你需要用唯一值作为key,那可以把HashMap的key换成String(比如直接用indexName当key),或者自定义一个包含dictionary和indexName的对象作为key(记得重写equals()和hashCode()方法)。
比如改成以indexName为key的版本:
public Map<String, Boolean> findMetadata(String scanPackage) { Map<String, Boolean> metadatas = new HashMap<>(); ClassPathScanningCandidateComponentProvider provider = createComponentScanner(); for (BeanDefinition beanDef : provider.findCandidateComponents(scanPackage)) { try { Class<?> cl = Class.forName(beanDef.getBeanClassName()); Indexable indexable = cl.getAnnotation(Indexable.class); logger.info("---------------------------- " + indexable.dictionary() + " " + indexable.indexName()); // 直接存入,indexName作为唯一key,不会被覆盖(如果indexName可能重复,再加个判断即可) metadatas.put(indexable.indexName(), indexable.dictionary()); } catch (ClassNotFoundException e) { logger.error(ERROR + e); } } return metadatas; }
2. 用Map<Boolean, List>分组存储
如果你的需求是把同一个dictionary(布尔值)对应的所有indexName都保存下来,那应该用布尔值作为key,value用字符串列表。这样同一个布尔key下就能存储多个indexName了。
示例代码:
public Map<Boolean, List<String>> findMetadata(String scanPackage) { Map<Boolean, List<String>> metadatas = new HashMap<>(); ClassPathScanningCandidateComponentProvider provider = createComponentScanner(); for (BeanDefinition beanDef : provider.findCandidateComponents(scanPackage)) { try { Class<?> cl = Class.forName(beanDef.getBeanClassName()); Indexable indexable = cl.getAnnotation(Indexable.class); boolean dictionary = indexable.dictionary(); String indexName = indexable.indexName(); logger.info("---------------------------- " + dictionary + " " + indexName); // 先获取对应布尔值的列表,不存在就新建一个 List<String> indexNames = metadatas.getOrDefault(dictionary, new ArrayList<>()); // 避免重复添加同一个indexName if (!indexNames.contains(indexName)) { indexNames.add(indexName); metadatas.put(dictionary, indexNames); } } catch (ClassNotFoundException e) { logger.error(ERROR + e); } } return metadatas; }
如果用Java 8及以上版本,还可以用computeIfAbsent简化这段逻辑:
// 替换上面的列表处理代码 metadatas.computeIfAbsent(dictionary, k -> new ArrayList<>()); List<String> indexNames = metadatas.get(dictionary); if (!indexNames.contains(indexName)) { indexNames.add(indexName); }
小提示
- 尽量别用
containsValue(),HashMap的value没有索引,每次调用都要遍历整个map,数据量大的时候性能会很差。 - 选哪种方案取决于你的业务:如果
indexName是唯一的,第一种更高效;如果需要按布尔值分组存多个值,第二种更贴合需求。
内容的提问来源于stack exchange,提问作者Aymen Kanzari

