Map.Entry 本是古典 何须时尚 2024-04-18 18:13 32阅读 0赞 ### 文章目录 ### * Map.Entry * * K getKey() * V getValue() * V setValue(V value) * boolean equals(Object o) * int hashCode() * comparingByKey() * comparingByValue( ) * comparingByKey(Comparator<? super K> cmp) * comparingByValue(Comparator<? super V> cmp) # Map.Entry # > `Map` 接口下面的 `Entry` 接口。 该接口,定义一个键值对实体接口。`Map.entrySet` 方法返回的 `Set` 集合中的实体就是实现这个 它。只有一种方法可以获得 `Map.Entry` 对象的引用,那就是通过集合的迭代器。并且 `Map.entry` 只在迭代期间有效,更加准确的是意思是,如果在获得迭代器以后,修改了集合,那么 `Map.Entry` 的行为是未定义的1。除非调用 `Map.Entry` 的 `setValue` 设置下修改的值。 `API`文档中的这段话,说的让我有点费解。修改集合以后,`Map.Entry` 的行为是未定义的,LZ 做了实验,发现并没有触发到什么非法,未定义的操作。 Map<String, String> stringMap = new HashMap<>(16); stringMap.put("key1", "value1"); stringMap.put("key2", "value2"); stringMap.put("key3", "value3"); stringMap.put("key4", "value4"); stringMap.put("key5", "value5"); stringMap.put("key6", "value6"); Iterator<Map.Entry<String, String>> iterator = stringMap.entrySet().iterator(); Map.Entry<String, String> next = iterator.next(); stringMap.remove(next.getKey()); stringMap.put(next.getKey(),"value7"); 就如上面的代码所示,在得到 `Entry` 以后,对集合进行了修改,也没有触发什么非法的状态,抛出什么异常来。**这里的未定义,其实是一个很无懈可击的答案,既然是未定义的,那么它们做出的任何行为,都是可以被理解的,所以它没抛出什么异常,那也是对的,抛出异常也是对的,你不应该单方面的任认为它应该怎样怎样,因为它是未定义,不同的实现有不同的反应**。 而且这里的合法与非法,是针对 `Entry` 的值来说,在你获取以后,有人又修改了集合的内容,这时候你获取的 `Entry` 的内容,也会随之改变,但是你可能不知道集合被修改过,所以这里的合法与非法,是 `Entry` 是否可以再被信任的问题,所以想要修改值的时候,应该用 `entry` 的 `setValue()` 方法,显示的去改。 -------------------- ## K getKey() ## 返回实体对应的 `key` 。 可能抛出的异常 `IllegalStateException` ,这个异常可以 **选择性** 的实现。如果实现了,则异常的抛出条件:如果对应的 `entry` 已经被移除了,则抛出该异常。 比如,`HashMap` 的 `Entry` 就没有实现抛出该异常: static class Node<K,V> implements Map.Entry<K,V> { ... public final K getKey() { return key; } ... } 而`EnumMap` 则实现了该异常,并且遵守了异常抛出条件: private class Entry implements Map.Entry<K,V> { ... public K getKey() { checkIndexForEntryUse(); return keyUniverse[index]; } ... private void checkIndexForEntryUse() { if (index < 0) throw new IllegalStateException("Entry was removed"); } } -------------------- ## V getValue() ## 返回 `entry` 实体对应的 `value` 。 如果集合中此 `entry` 的映射关系已经被移除,即使是通过 `iterator` 的 `remove` 方法,`getValue()` 方法的返回值也是 **未定义**。因此,不同的实现,对此方法有不同的做法,`HashMap` 对其没做什么,正常返回值,即使映射关系被删除了。`EnumMap` 则抛出异常。 可能抛出的异常 `IllegalStateException` ,这个异常可以 **选择性** 的实现。如果实现了,则异常的抛出条件:如果对应的 `entry` 已经被移除了,则抛出该异常。 -------------------- ## V setValue(V value) ## 替换当前 `entry` 的 `value` 为传进来的给定的 `value` ,(`map` 中对应的 `value` 也被改变)。如果集合中 `entry` 的映射关系已经被通过迭代器的 `remove()` 方法移除,则调用这个方法的行为是 **未定义** 的。看具体的实现如何操作。同样的 `HashMap` 对此行为,返回正确的值。`EnumMap` 则抛出异常。 返回设置值之前,当前 `entry` 对应的值。 可能抛出的异常: 1. `UnsupportedOperationException` :如果集合不支持 `put` 操作,则抛出此异常。 2. `ClassCastException`:如果传入的参数,不能转换存储到集合中,则抛出此异常,类型转换异常。 3. `NullPointerException`:如果集合不允许存入 `null` ,其传入的参数确实是 `null` ,则抛出此异常。 4. `IllegalArgumentException`:如果传入的值的某些属性,阻止其存入集合中,则抛出此异常。 5. `IllegalStateException` :此异常可选择是否实现。如果 `entry` 已经被移除了,则抛出此异常。 -------------------- ## boolean equals(Object o) ## 将传入的参数对象与当前的 `entry` 比较,如果传入的对象也是一个 `entry` 类型,并且它们具有相同的映射关系,则返回 `true` 。 更确切的说,相同的映射关系,应该写成下面的代码: `key` ,`value` 分别相等。 (e1.getKey()==null ? e2.getKey()==null : e1.getKey().equals(e2.getKey())) && (e1.getValue()==null ? e2.getValue()==null: e1.getValue().equals(e2.getValue())) 这样做以后,可以确保 `equals` 方法在不同的 `Map.Entry` 实现之前都能正确的工作。 -------------------- ## int hashCode() ## 返回当前 `entry` 的哈希码。`entry` 的哈希码计算方法如下: (e.getKey()==null ? 0 : e.getKey().hashCode()) ^ (e.getValue()==null ? 0 : e.getValue().hashCode()) 这样做,确保 `e1.equals(e2)` 时,`e1.hashCode()==e2.hashCode()` ,当前前提是,这个两个 `entry` 的 `KV` 的 `hashCode` 方法一致 。 -------------------- > 下面几个方法是 1.8 添加进来的。属于静态方法 ## comparingByKey() ## public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() { return (Comparator<Map.Entry<K, V>> & Serializable) (c1, c2) -> c1.getKey().compareTo(c2.getKey()); } 返回一个 `Comparator` ,该比较器对 `entry` 的 `key`进行 **自然排序**,即按照字典顺序,**0-9,a-z** 。 返回的比较器,实现了 `serializable` 接口。代码中 `(Comparator<Map.Entry<K, V>> & Serializable)` 是强转的含义。强转可以这样写,转为二者的结合,但是 `&` 后面必须是 **接口** 。 可能抛出的异常:`NullPointerException` ,如果比较的 `entry` 的 `key` 是 `null`,则抛出此异常。 -------------------- ## comparingByValue( ) ## public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K,V>> comparingByValue() { return (Comparator<Map.Entry<K, V>> & Serializable) (c1, c2) -> c1.getValue().compareTo(c2.getValue()); } 返回一个 `Comparator` ,该比较器对 `entry` 的 `key`进行 **自然排序** 。 返回的比较器,实现了 `serializable` 接口。 可能抛出的异常:`NullPointerException` ,如果比较的 `entry` 的 `key` 是 `null`,则抛出此异常。 -------------------- ## comparingByKey(Comparator<? super K> cmp) ## public static <K, V> Comparator<Map.Entry<K, V>> comparingByKey(Comparator<? super K> cmp) { Objects.requireNonNull(cmp); return (Comparator<Map.Entry<K, V>> & Serializable) (c1, c2) -> cmp.compare(c1.getKey(), c2.getKey()); } 返回一个比较器,该比较器对 `entry` 的 `key` 进行比较,根据传入的比较器。如果传入的比较器实现了 `serializable` 接口,那么返回的比较器也一并实现该接口。 -------------------- ## comparingByValue(Comparator<? super V> cmp) ## public static <K, V> Comparator<Map.Entry<K, V>> comparingByValue(Comparator<? super V> cmp) { Objects.requireNonNull(cmp); return (Comparator<Map.Entry<K, V>> & Serializable) (c1, c2) -> cmp.compare(c1.getValue(), c2.getValue()); } 返回一个比较器,该比较器对 `entry` 的 `value` 进行比较,根据传入的比较器。如果传入的比较器实现了 `serializable` 接口,那么返回的比较器也一并实现该接口。 -------------------- -------------------- 1. 可以参考下 [codeRanch 上面的回答][codeRanch] 。真是令人惊叹,上面关于这个疑问的讨论,还是十七年前的回答,当时的他们又是人几何年呢。如果也如我一样,那十七年过去了,现在也是不惑之年了。 ↩︎ [codeRanch]: https://coderanch.com/t/322896/java/Map-Entry-object-invalid-map
还没有评论,来说两句吧...