Java IdentityHashMap详解指南

培训教学 潘老师 7个月前 (10-12) 152 ℃ (0) 扫码查看

在本教程中,我们将详细探讨Java的IdentityHashMap,并突出显示IdentityHashMap与HashMap之间的区别。

IdentityHashMap在比较键(或值)时使用引用相等性而不是对象相等性。换句话说,在IdentityHashMap中,两个键k1和k2仅当(k1==k2)时才被视为相等。您可以在IdentityHashMap中使用可变键,因为引用相等性不会随对象状态的改变而改变。

1.IdentityHashMap简介

IdentityHashMap类位于java.util包中,是Map接口的基于HashTable的实现,自Java版本1.4以来一直存在。

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

在Java集合中,该类的声明如下:

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

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

2.使用IdentityHashMap

2.1 创建IdentityHashMap

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

  • IdentityHashMap():用于创建具有初始默认容量21的空映射。
  • IdentityHashMap(int initialCapacity):用于创建具有给定初始容量的空映射。
  • IdentityHashMap(Map m):用于创建具有与指定Map相同条目的新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.2 IdentityHashMap方法

该类中包含的一些经常使用的方法包括:

  • Object put(key, value):将键-值对插入映射中。
  • Object get(key):返回映射中指定键的值。
  • boolean containsKey(key):根据指定键是否在映射中找到,返回true或false。
  • boolean containsValue(value):类似于containsKey()方法,它查找指定值而不是键。
  • Set keySet():返回映射中存储的所有键的集合。
  • Set entrySet():返回映射中存储的所有映射的集合。
  • Value remove(Object key):移除指定键的键-值对。
  • int size():返回映射的大小,即存储在映射中的键-值对数量。

2.3 IdentityHashMap示例

让我们快速介绍如何创建其实例以及如何使用上述方法的示例。

//创建 IdentityHashMap
IdentityHashMap<Integer, String> map = new IdentityHashMap<>();
//用put()新增值
map.put(1, "A");
map.put(2, "B");
map.put(3, "C");
System.out.println(map);
//获取值
String value = map.get(2);
System.out.println(value);
//判断key是否存在
System.out.println(map.containsKey(3));
System.out.println(map.containsValue("Z"));
//根据key移除一个键值对
map.remove(3);
System.out.println(map);
//map长度
System.out.println(map.size());
//遍历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.HashMap和IdentityHashMap之间的区别

3.1 引用相等性

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

//内存中有两个相似但不同的实例
Integer key1 = new Integer(10);
Integer key2 = new Integer(10);
//IdentityHashMap中的相同key
IdentityHashMap<Integer, String> identityHashMap = new IdentityHashMap<>();
identityHashMap.put(key1, "India");
identityHashMap.put(key2, "USA");
System.out.println("Identity HashMap : " + identityHashMap);
// HashMap中的相同key
HashMap<Integer, String> hashMap = new HashMap<>();
hashMap.put(key1, "India");
hashMap.put(key2, "USA");
System.out.println("HashMap : " + hashMap);

注意程序输出:

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

HashMap拒绝了第二个键,因为它使用equals()方法来比较键,而对于两个键,值都是10。IdentityHashMap使用引用相等性,因此两个键分别存储在内存中,因此它们的引用是不相等的。

当我们将键值对放入HashMap时,它会更新先前的条目,我们会得到一个存储在映射中的单个条目。

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");
// 改变key状态
vehicle.setName("Modified Vehicle");
vehicle.setYear(2022);
// 从map中获取vehicle的value
System.out.println( identityHashMap1.get(vehicle) );   //输出 'Old Vehicle'

4.IdentityHashMap用途

IdentityHashMap在罕见的用例中使用,我们在使用这个类时必须小心。

  • 它有助于构建特定的框架,包括:
  • Spring bean或Singleton类型,因为它们精确管理某些类型的一个实例
  • 维护一组可变对象的代理对象
  • 类对象,因为它们也可以通过引用进行比较
  • 基于对象引用的缓存实例
  • 保持具有引用的对象的内存图

5.结论

我们了解了Java中的IdentityHashMap,它的内部工作方式以及它与HashMap之间的区别。我们还涵盖了涉及HashMap和IdentityHashMap的实际示例以及它们在键比较方面的不同行为。


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/teach/9474.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】