[英]Are Java Records suitable to represent the nodes of a graph?
表示圖的經典方法之一(如在圖論中)是:
class Node {
String value;
List<Node> children;
// constructor, equals, etc. are omitted
}
問題是關於使用Java 14 中引入的新記錄功能
具體來說,對於像 DFS 這樣的算法使用如下聲明是否有任何潛在的缺陷:
record Node(String value, List<Node> children) {}
一個潛在的問題是關於記錄提供的equals
/ hashCode
方法。 我想,在Node
的情況下,記錄提供的實現可能會導致無限遞歸(StackOverflow)。
例如,考慮下面使用Set
集合的代碼。
import java.util.*;
class SO {
static void dfs(Node n, Set<Node> visited) {
if (n == null) return;
System.out.println("visited " + n.value());
for (Node child : n.children()) {
if (visited.contains(child)) continue;
visited.add(child);
dfs(child, visited);
}
}
public static void main(String[] args) {
var a = new Node("a", new ArrayList<>());
var b = new Node("b", new ArrayList<>());
a.children().add(b);
b.children().add(a);
dfs(a, new HashSet<>());
}
}
record Node(String value, List<Node> children) {}
此代碼導致 StackOverflow:
% java SO.java
visited a
Exception in thread "main" java.lang.StackOverflowError
at Node.hashCode(SO.java:25)
at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:595)
at java.base/java.util.ArrayList.hashCode(ArrayList.java:582)
at java.base/java.util.Objects.hashCode(Objects.java:103)
at Node.hashCode(SO.java:25)
... <omitted> ...
這是否意味着使用記錄通常不適合圖形算法? 如果不是,如何在與記錄一起使用時正確實現此類算法?
堆棧跟蹤揭示了堆棧溢出錯誤的原因。
問題是您創建的循環關系 - a 是 b 的孩子,b 是 a 的孩子 - 在計算節點的哈希碼時會導致無限循環。
record
使用所有字段計算其哈希碼。 反過來, List
使用其所有元素的哈希碼計算其哈希碼。 因為 a 的列表包含 b,而 b 的列表包含 a,所以傳遞給 HashSet 方法的每個對象的哈希碼計算都會無限下降。
解決方法是覆蓋equals()
和hashCode()
以不涉及children
,可能通過return super.hashCode();
ETC
或者不要定義循環關系。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.