章
目
录
在本教程中,我们将详细探讨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的实际示例以及它们在键比较方面的不同行为。