Java TreeMap:“放置”之后是否存在其他对象?

时间:2018-07-30 14:25:44

标签: java equals comparator hashcode treemap

我将使用自己的比较器从TreeMap派生的类用作LinkedHashMap中的键。使用这种结构,我发现一些无法解释的怪异行为。也许你们其中之一可以提供帮助。我试图用原语重现我的问题。当我创建原始类型的TreeMap时,自然排序顺序就足够了,并且在TreeMap的构造函数中不需要比较器,对吧?!

这是MWE:

package treemapputtest;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;

public class TreeMapPutTest {

    public static void main(String[] args) {

        System.out.println("simple:");
        simpleTest();

        System.out.println("\n\ncomplex:");
        complexTest();
    }

    private static void simpleTest(){

        TreeMap<Integer,String> map = new TreeMap<>();

        System.out.println("map: " + map.hashCode() + " | " + Integer.toHexString(map.hashCode()));

        map.put(1, "a");

        System.out.println("map: " + map.hashCode() + " | " + Integer.toHexString(map.hashCode()));

        map.put(2, "b");

        System.out.println("map: " + map.hashCode() + " | " + Integer.toHexString(map.hashCode()));

    }

    private static void complexTest(){

        TreeMap<Integer,String> internalMap = new TreeMap<>();
        internalMap.put(1, "a");
        internalMap.put(2, "b");

        System.out.println("prior: " + internalMap.hashCode() + " | " + Integer.toHexString(internalMap.hashCode()));

        LinkedHashMap<TreeMap<Integer,String>,Double> myMap = new LinkedHashMap<>();
        myMap.put(internalMap, 1.0);

        doSomethingWithMyInternalMap(myMap.keySet().iterator().next());

        System.out.println("after:");
        for (Map.Entry<TreeMap<Integer,String>,Double> entry : myMap.entrySet()){
            System.out.println("  " + Integer.toHexString(entry.getKey().hashCode()));
        }

    }

    private static void doSomethingWithMyInternalMap(TreeMap<Integer,String> intern){
        intern.put(3, "c");
    }
}

输出为:

simple:
map: 0 | 0
map: 96 | 60
map: 192 | c0

complex:
prior: 192 | c0
after:
  120

所以我的问题是:当我向TreeMap中添加内容时,hashCode()的结果为什么会改变?仅对于TreeMap来说,这没什么大不了的,但是由于这会创建一个“新对象” /更改了对旧对象的引用,因此在LinkedHashMap中更新TreeMap后,我会出错。

Object API表示hashCode():

  

hashCode的一般约定是:在Java应用程序执行期间,每次在同一对象上多次调用hashCode时,只要在该对象的equals比较中不使用任何信息,则hashCode方法必须始终返回相同的整数。修改。

在TreeMap中添加其他内容是否会更改TreeMap的equals()方法中的某些内容?我是否必须以某种方式覆盖equals()hashCode()

3 个答案:

答案 0 :(得分:2)

我认为您对hashCode有误解。让我们在此处引用的文本中强调一点:

  

hashCode的一般约定是:在Java应用程序执行过程中,每次在同一对象上多次调用hashCode方法时,hashCode方法必须始终返回相同的整数,前提是不提供用于进行等于比较的信息。该对象已修改

每当在地图中添加(或删除)数据时,都在更改其equals方法中使用的信息-空地图并不等于其中包含[1->a]的地图,并且带有[1->a]的地图不等于带有[1->a; 2->b]的地图。

这与创建新对象无关,并且对旧地图的引用发生更改。如果您调用System.identityHashCode(map)而不是map.hashCode(),则无论调用多少次put,对象引用都不会改变。

答案 1 :(得分:1)

  

在TreeMap中添加其他内容是否会更改TreeMap的equals()方法中的某些内容?

是的,因为地图的contract of equals

  

将指定对象与此映射进行比较以确保相等。如果给定对象也是一个映射并且两个映射表示相同的映射,则返回true。更正式地说,如果m1.entrySet()。equals(m2.entrySet()),则两个映射m1和m2表示相同的映射

因此,映射的每个条目都用于检查是否相等,因此还用于计算hashCode。并添加一个条目,从而修改了hashCode。

您期望hashCode是某种对象的不可变标识符。不是。完全没有。

答案 2 :(得分:1)

  

在TreeMap中添加其他内容是否会更改TreeMap的equals()方法中的某些内容?我必须以某种方式重写equals()和hashCode()吗?

为什么不呢?新的TreeMap为空。因此,根据您的推理,如果其equals()hashCode()方法未根据TreeMap的内容进行调整,则TreeMap的所有实例(从空开始)都将具有相同的哈希码,因为它与创建时计算出来的相同,里面没有任何条目,无论之后添加了什么。

大多数集合的equals()hashCode()函数会根据其内容进行调整。这样,可以将元素SetList与元素SetList的另一个实例进行比较,如果equals()包含元素,则返回true相同的元素(在List中以相同的顺序)。同样,对于Map,内部equals()实现会确保检查与该Map实现等效的内容。