概述
HashSet元素引用的对象的内容发生变化,会导致“元素不属于集合”的问题。事实上这个元素还在集合里,但是调用contains方法进行判断,得到的结果却是false。
正文
关于变化
这里所讲的变化是指元素引用的对象的内容的变化,但是对象还是这个对象。比如我们定义如下的field
private Set> cache = new HashSet >();
我们计划cache里的每一个元素都是一个Set<Integer>的集合。如果我们取出cache的一个元素,然后往这个元素集合中添加一个Integer元素。对于cache来说,这个元素还是这个元素,但是它的内容已经变化了。
关于校验标准
/** * 校验. * 从集合中取出的元素反而不属于该集合,则为无效. * @return */private boolean validate() { boolean flag = true; for ( Setele : cache ) { if (!cache.contains(ele)) { flag = false; System.out.println("无效的元素:" + ele); } } return flag;}
测试
我们分为三个测试用例:数据初始化测试、直接更新测试、移除新增测试。
一、数据初始化测试
1. 数据初始化
/** * 初始化数据. */private void init() { Integer[][] data = { {1, 2}, {3, 4}, {5}}; for (Integer[] ele : data) { ListeleList = Arrays.asList(ele); Set eleSet = new HashSet (eleList.size()); eleSet.addAll(eleList); cache.add(eleSet); } System.out.println(cache);}
2. 测试
@Testpublic void testInit() { init(); boolean flag = validate(); System.out.println("对初始化的数据进行校验,结果:" + flag);}
3. 输出结果
[[2, 1], [5], [4, 3]]对初始化的数据进行校验,结果:true
二、直接更新测试
1. 更新的方法
/** * 直接修改. */private void update() { for (Setele : cache) { if (ele.contains(5)) { ele.add(6); break; } } System.out.println(cache);}
2. 测试
@Testpublic void testUpdate() { init(); update(); boolean flag = validate(); System.out.println("对直接修改的数据进行校验,结果:" + flag);}
3. 输出结果
[[2, 1], [5], [4, 3]][[2, 1], [6, 5], [4, 3]]无效的元素:[6, 5]对直接修改的数据进行校验,结果:false
三、移除新增测试
1. 移除新增
/** * 移除添加. */private void removeThenAdd() { for (Setele : cache) { if (ele.contains(5)) { cache.remove(ele); ele.add(6); cache.add(ele); break; } } System.out.println(cache);}
2. 测试
@Testpublic void testRA() { init(); removeThenAdd(); boolean flag = validate(); System.out.println("对移除添加的数据进行校验,结果:" + flag);}
3. 输出结果
[[2, 1], [5], [4, 3]][[2, 1], [4, 3], [6, 5]]对移除添加的数据进行校验,结果:true
结论
我认为HashSet遍历元素和判断元素是否在集合中的机制是不同的,HashSet中的元素都有一个不同的hashcode,我们直接修改其中的元素,导致其内容和其hashcode对应不上,所以才会有上述的问题。