Java IdentityHashMap指南

770 阅读2分钟

在本教程中,我们将详细学习JavaIdentityHashMap以及IdentityHashMapHashMap的区别。

在比较键(或值)IdentityHashMap 使用引用平等而不是对象平等。换句话说,在一个IdentityHashMap ,当且仅当(k1==k2) ,两个键k1k2 被认为是相等的。我们可以IdentityHashMap使用可变的键,因为引用平等并不随对象的状态而改变。

目录

  1. 1.身份哈希图的介绍

  2. 2.使用IdentityHashMap工作

  3. 3.HashMap和IdentityHashMap之间的区别

  4. 4.身份哈希图用例

  5. 5.总结

  6. IdentityHashMap简介


IdentityHashMap 类(存在于java.util包中)是一个 基于HashTable的 Map 接口的实现,从*Java 1.4版本*开始就已经存在。

  • 这个类不是一个通用的Map 实现。尽管这个类实现了Map 接口,但它违反了Map的一般契约,在比较对象时使用equals() 方法。它使用引用平等(==)来搜索地图中的键。这个类只在需要引用平等的情况下使用。
  • IdentityHashMap内部使用System.identityHashCode()方法进行计算。
  • IdentityHashMap具有与HashMap几乎相同的特性,包括构造函数和方法。然而,就性能而言,与HashMap相比,它提供了更好的性能,因为它使用了 HashTable内联探测技术,而不是*HashMap*使用的链式技术
  • 它的迭代器在迭代过程中试图修改地图时抛出ConcurrentModificationException
  • 它不是一个线程安全的类。使用*Collections.synchronizedMap()*来获得这个类的线程安全引用。

Java集合 中, 类被声明如下。

public class IdentityHashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Serializable, Cloneable

如上所示,它实现了Map 接口并扩展了AbstractMap 类。

IdentityHashMap

2.使用IdentityHashMap工作

2.1 创建IdentityHashMap

我们可以通过使用以下构造函数来创建IdentityHashMap

  • IdentityHashMap(): 用来创建一个初始默认容量为21的空地图。
  • IdentityHashMap(int initialCapacity): 用来创建一个具有给定初始容量的空地图。
  • IdentityHashMap(Map m): 用于创建一个新的IdentityHashMap ,其条目与指定地图相同。
IdentityHashMap<String, String> map = new IdentityHashMap<>();

IdentityHashMap<String, String> map = new IdentityHashMap<>(16);

Map<String, String> map = new HashMap<String, String>() {{
    put("key1", "value1");
   put("key2", "value2");
}};
IdentityHashMap<String, String> map = new IdentityHashMap<>(map);

2.2IdentityHashMap方法

该类中一些经常使用的方法是。

  • Object put(key, value): 向地图中插入一个键值对。
  • Object get(key): 返回地图中指定的值。
  • boolean containsKey(key):根据是否在地图中找到指定的键,返回truefalse
  • boolean containsValue(value): 类似于*containsKey()*方法,它寻找的是指定的值而不是key。
  • Set keySet(): 返回存储在地图中的所有键的集合
  • Set entrySet(): 返回存储在地图中的所有映射的集合
  • Value remove(Object key): 删除指定键的键值对。
  • int size(): 返回地图的大小,等于地图中存储的键值对的数量。

2.3IdentityHashMap 例子

让我们快速介绍一个创建其实例的例子 ,以及我们如何使用上面描述的方法。

//Creating IdentityHashMap
IdentityHashMap<Integer, String> map = new IdentityHashMap<>();

//Adding values to map using put()
map.put(1, "A");
map.put(2, "B");

map.put(3, "C");
System.out.println(map);

//Getting a value from the map
String value = map.get(2);
System.out.println(value);

//Checking if a key or value present in the map
System.out.println(map.containsKey(3));
System.out.println(map.containsValue("Z"));

//Removing an entry
map.remove(3);
System.out.println(map);

//Finding map size
System.out.println(map.size());

//Iterating over the map
for(Map.Entry<Integer, String> entry : map.entrySet())
{
    System.out.println(entry.getKey() + " :: " + entry.getValue());
}

注意,IdentityHashMap支持null 键和值。

IdentityHashMap<String, String> map = new IdentityHashMap<>();

map.put(null, "Some Value");   //Null key 
map.put("Some Key", null);      //Null value

3.HashMapIdentityHashMap之间的区别

3.1 引用平等性

在比较键 (和值)时,IdentityHashMap使用引用相等(==)而不是Map的 equals()方法。让我们通过一个例子来理解。

// Two similar keys but different instances in memory
Integer key1 = new Integer(10);
Integer key2 = new Integer(10);

// Same keys in IdentityHashMap
IdentityHashMap<Integer, String> identityHashMap = new IdentityHashMap<>();
identityHashMap.put(key1, "India");
identityHashMap.put(key2, "USA");

System.out.println("Identity HashMap : " + identityHashMap);

// Same keys in HashMap
HashMap<Integer, String> hashMap = new HashMap<>();
hashMap.put(key1, "India");
hashMap.put(key2, "USA");

System.out.println("HashMap : " + hashMap);

注意程序的输出。HashMap拒绝了第二个键,因为它使用*equals()*方法对键进行了比较,对于这两个键来说,其值都是10。IdentityHashMap使用的是引用相等,而两个键都是单独存储在内存中的,所以它们的引用将是不相等的。

当我们把键值对放到HashMap中时,它就会更新之前的条目,我们就会得到一个存储在Map中的单一条目。

Identity HashMap : {10=USA, 10=India}
HashMap : {10=USA}

3.2 可变键

我们可以在IdentityHashMap 中使用可变键,而对于HashMap ,我们总是建议使用不可变的键

让我们通过一个例子来理解,创建一个可变类 "Vehicle"。定义必要的访问器方法,hashCode()和equals()方法。

class Vehicle {

  private String name;
  private int year;

  public Vehicle(String name, int year) {
    this.name = name;
    this.year = year;
  }

  //Getters and Setters

  @Override
  public String toString() {
    return "Vehicle{" +
        "vehicleName='" + name + '\'' +
        ", modelYear=" + year +
        '}';
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    Vehicle vehicle = (Vehicle) o;

    if (Objects.equals(year, vehicle.year)) return false;
    return Objects.equals(name, vehicle.name);
  }

  @Override
  public int hashCode() {
    int result = name != null ? name.hashCode() : 0;
    result = 31 * result + year;
    return result;
  }
}

我们将向地图添加一些键值对,然后改变键和值的状态。然后,我们将使用改变后的键来获取条目,这应该会返回原来的值。

Vehicle vehicle = new Vehicle("Honda", 2015);

Map<Vehicle, String> identityHashMap1 = new IdentityHashMap<>();
identityHashMap1.put(vehicle, "Old Vehicle");

// Changing key state
vehicle.setName("Modified Vehicle");
vehicle.setYear(2022);

// Getting value for key vehicle from the map
System.out.println( identityHashMap1.get(vehicle) );   //Prints 'Modified Vehicle'

4.身份哈希图 的用例

IdentityHashMap用于少数情况,我们在使用这个类时必须小心。

它有助于构建特定的框架,包括。

  • Spring Bean或Singleton类型,因为它们精确地管理某些类型的一个实例
  • 为一组易变的对象维护代理对象
  • 类对象,因为它们也可以通过引用进行比较。
  • 基于一个对象引用的缓存实例
  • 保持一个有引用的 对象的内存图

5.总结

我们了解了Java中的IdentityHashMap,它的内部工作原理以及它与HashMap的区别。我们还介绍了涉及HashMap和 IdentityHashMap的实际例子,以及它们在按键比较方面的不同表现。

学习愉快

源代码在Github上

这篇文章有帮助吗?

如果你喜欢这篇文章,请告诉我们。这是我们改进的唯一方法。

没有

相关帖子。

  1. Java ConcurrentMap指南
  2. Java TransferQueue - Java LinkedTransferQueue类
  3. Java 迭代列表实例
  4. Java TreeMap类
  5. Java PriorityQueue类
  6. Java PriorityBlockingQueue类