1 hashmap hashcode kotlin data-class
我是 Kotlin 的新手,我正在尝试理解它,我刚刚编写了一个简单的示例,该示例展示了如何将数据类与映射一起使用有点棘手,因为在我看来,数据类有一种奇怪的行为。默认情况下,他们根据类的每个属性定义 hashCode()。但他们没有定义默认的 equals() 方法。这给我带来了很多困惑,因为我创建了一个以数据类作为键的 HashMap,但我没有重写 hashCode() 和 equals()。我的数据类有一个 MutableList 成员。当我将一个元素放入映射中时,只要我没有将元素添加到 MutableList 中,我就会使用 map.get(dataObject) 检索它。之后,即使数据对象仍然相同,并且我使用map.keys(map.keys.indexOf(dataObject)有效)发现它,由于hashCode(),map.get(dataObject)失败了。
我可以使用普通类或添加 hashCode() 和 equals() 来修复它,从 hashCode() 中删除 MutableList,但我想知道,由于默认行为,重写 hashCode() 和 equals() 是否应该是“强制”与数据类一起使用,否则将它们与地图一起使用可能会导致错误。
我还能做些什么来避免这个问题吗?
package cards
data class Player(val name: String, var cards: MutableList<Card>) {
constructor(name: String): this(name, mutableListOf())
//I don't need to define equals, so pointers are checked. But if I don't override hashCode, as it's based
//on every property, the hashCode is calculated considering the content of the MutableList!
// override fun hashCode(): Int {
// return name.hashCode()
// }
}
data class Card(val name: String, val suite: String)
class Game(val players: List<Player>) {
val cardMap: MutableMap<Player, MutableList<Card>> = mutableMapOf()
fun putIntoMapAndGiveCards() {
val newCards = cardMap.getOrDefault(players[0], mutableListOf())
newCards.add(Card(name = "Four", suite = "Clubs"))
cardMap[players[0]] = newCards
//This changes the default hashCode - I can use data classes in a list, but not in a map, because maps are
//based on it.
players[0].cards.add(Card(name = "Five", suite = "Clubs"))
}
fun getFromMap(): MutableList<Card>? {
val player = players[0]
assert(player != null, { "Player from list failure" })
val indexOfPlayer = cardMap.keys.indexOf(player)
assert(indexOfPlayer == 0, { "Player is in the map" })
//Without overriding hashCode, cards is null!
val cards = cardMap.get(players[0])
assert(cards != null, { "Cards from map failure" })
return cards
}
}
fun main() {
val player1 = Player(name = "John")
val game = Game(mutableListOf(player1))
game.putIntoMapAndGiveCards()
game.getFromMap()
?: throw Exception( """Map.get() failure because Player is a data class.
| A data class by default builds its hashCode with every property. As it contains a MutableList,
| the hashCode changes when I add elements to the list. This means that I can't find the element using get()
""".trimMargin())
println("Test finished!")
}
Run Code Online (Sandbox Code Playgroud)
默认情况下,他们根据类的每个属性定义 hashCode()。但他们没有定义默认的 equals() 方法
这是不正确的。数据类根据数据类的主构造函数中声明的属性一致地生成两者(顺便说一句,同样如此equals())。hashCode()toString()
这是您的类的equals反编译代码:hashCodePlayer
public int hashCode() {
String var10000 = this.name;
int var1 = (var10000 != null ? var10000.hashCode() : 0) * 31;
List var10001 = this.cards;
return var1 + (var10001 != null ? var10001.hashCode() : 0);
}
public boolean equals(@Nullable Object var1) {
if (this != var1) {
if (var1 instanceof Player) {
Player var2 = (Player)var1;
if (Intrinsics.areEqual(this.name, var2.name) && Intrinsics.areEqual(this.cards, var2.cards)) {
return true;
}
}
return false;
} else {
return true;
}
}
Run Code Online (Sandbox Code Playgroud)
您的问题是您cards在主构造函数中声明了可变列表,因此它是生成的equals和的一部分hashCode。
解决方案是cards将此属性移至类的主体中(因为它不是玩家“核心数据”的一部分,而是状态的一部分):
data class Player(val name: String) {
val cards: MutableList<Card> = mutableListOf()
}
Run Code Online (Sandbox Code Playgroud)
这样,生成的equals/hashCode对将仅基于name属性。
显然,另一种选择是覆盖两者equals并hashCode手动仅考虑其中name,但这很乏味而且不太惯用。
我想知道,由于默认行为,重写 hashCode() 和 equals() 是否应该对数据类“强制”,因为否则将它们与 Map 一起使用可能会导致错误。
我认为您误诊了默认行为。所以我想说,相反,重写equals/hashCode对于数据类来说实际上并不是很惯用,通常应该避免。
只要主构造函数中的数据不可变,在映射中使用数据类通常是安全的。
你真的不应该var与可变集合混合。它创建了两种更改集合的方法,这是非常意外且容易出错的。您应该使用 aval MutableList或 a var List,这样您只能通过突变更改列表,或者只能通过赋值更改列表,但不能同时更改两者。
如果要将新值插入到映射中,则不应使用getOrDefault+ 将值分配给键。相反,getOrPut直接使用,因此将插入默认值,无需额外工作。
为什么你们都使用a和 acards上的属性?看起来您现在有 2 个可以独立更改的状态,因为这些卡列表是独立的。PlayerMap<Player, List<Card>>
| 归档时间: |
|
| 查看次数: |
1107 次 |
| 最近记录: |