Java 序列化二叉树时的StackOverflowException
我正在尝试为Android应用程序实现一个二叉树,我希望能够将其序列化到设备上的一个文件中。不幸的是,我在尝试序列化它时遇到了stackoverflow错误 代码:Java 序列化二叉树时的StackOverflowException,java,android,serialization,binary-tree,Java,Android,Serialization,Binary Tree,我正在尝试为Android应用程序实现一个二叉树,我希望能够将其序列化到设备上的一个文件中。不幸的是,我在尝试序列化它时遇到了stackoverflow错误 代码: class BTree<V extends Model> implements Serializable { /** * */ private static final long serialVersionUID = 4944483811730762415L; private V value; private B
class BTree<V extends Model> implements Serializable {
/**
*
*/
private static final long serialVersionUID = 4944483811730762415L;
private V value;
private BTree<V> left;
private BTree<V> right;
public BTree(V value) {
this.value = value;
}
public BTree(V value, BTree<V> left, BTree<V> right) {
this.value = value;
this.left = left;
this.right = right;
}
private int getValue() {
return this.value.hashCode();
}
public void insert(BTree<V> node) {
if (this.getValue() >= node.getValue()) {
if (this.left == null) {
this.left = node;
} else {
this.left.insert(node);
}
} else {
if (this.right == null) {
this.right = node;
} else {
this.right.insert(node);
}
}
}
public boolean containsKey(Object key) {
return this.find(key) != null;
}
public V find(Object key) {
if (key.hashCode() == this.getValue()) {
return this.value;
} else if (key.hashCode() > this.getValue() && this.left != null) {
return this.left.find(key);
} else if (key.hashCode() < this.getValue() && this.right != null) {
return this.right.find(key);
} else {
return null;
}
}
public ArrayList<V> getAllValues() {
ArrayList<V> values = new ArrayList<V>();
if (this.left != null) {
values.addAll(this.left.getAllValues());
}
if (this.right != null) {
values.addAll(this.right.getAllValues());
}
return values;
}
}
我注意到的是,如果我序列化到一个当前不存在的文件,它可以很好地序列化。第二次运行同一程序时,再次序列化到磁盘时,会导致StackOverflowException
以下是logcat的输出:
09-07 05:29:42.011: ERROR/AndroidRuntime(916): FATAL EXCEPTION: main
09-07 05:29:42.011: ERROR/AndroidRuntime(916): java.lang.StackOverflowError
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.util.IdentityHashMap.getModuloHash(IdentityHashMap.java:435)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.util.IdentityHashMap.findIndex(IdentityHashMap.java:419)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.util.IdentityHashMap.get(IdentityHashMap.java:371)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.dumpCycle(ObjectOutputStream.java:471)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeObjectInternal(ObjectOutputStream.java:1739)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1689)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1653)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeFieldValues(ObjectOutputStream.java:1143)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:413)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeHierarchy(ObjectOutputStream.java:1241)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeHierarchy(ObjectOutputStream.java:1205)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeNewObject(ObjectOutputStream.java:1575)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeObjectInternal(ObjectOutputStream.java:1847)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1689)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1653)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeFieldValues(ObjectOutputStream.java:1143)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:413)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeHierarchy(ObjectOutputStream.java:1241)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeNewObject(ObjectOutputStream.java:1575)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeObjectInternal(ObjectOutputStream.java:1847)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1689)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1653)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeFieldValues(ObjectOutputStream.java:1143)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:413)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeHierarchy(ObjectOutputStream.java:1241)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeNewObject(ObjectOutputStream.java:1575)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeObjectInternal(ObjectOutputStream.java:1847)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1689)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1653)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeFieldValues(ObjectOutputStream.java:1143)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:413)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeHierarchy(ObjectOutputStream.java:1241)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeNewObject(ObjectOutputStream.java:1575)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeObjectInternal(ObjectOutputStream.java:1847)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1689)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1653)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeFieldValues(ObjectOutputStream.java:1143)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:413)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeHierarchy(ObjectOutputStream.java:1241)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeNewObject(ObjectOutputStream.java:1575)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeObjectInternal(ObjectOutputStream.java:1847)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1689)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1653)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeFieldValues(ObjectOutputStream.java:1143)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:413)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeHierarchy(ObjectOutputStream.java:1241)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeNewObject(ObjectOutputStream.java:1575)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeObjectInternal(ObjectOutputStream.java:1847)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1689)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1653)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeFieldValues(ObjectOutputStream.java:1143)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:413)
09-07 05:29:42.011: ERROR/AndroidRuntime(916): at java.io.ObjectOutputStream.writeHierarchy(ObjectOutputStream.j
这通常发生在递归没有正确的退出条件时 我要复制你的错误。Android似乎不能很好地处理深度递归,因为我在桌面上尝试了相同的程序,并且它与我正在尝试的大小配合得很好 根据评论中的讨论,我做了不涉及对象级递归的替代序列化。实现这一点的方法肯定不止一种,但我选择将结构序列化为JSON数据格式的字符串,并将其写入输出流。请注意,我没有彻底测试它。这个答案的目的是提供一种您可能也可以采用的方法 现在假设我的值是int,我认为JSON结构应该如下所示: { root : { "value" : 8 "left": { "value" : 3 "left" : { "value" : 1 } }, "right" : { "value" : 10 "right" : { "value": 14 } } } } 相反地,要读回它:
private void readJSONObject(String json) throws JSONException, IllegalAccessException, InstantiationException {
JSONObject baseJSON = new JSONObject(json);
JSONObject rootElement = baseJSON.getJSONObject("root");
readJSONObject(this, rootElement);
}
private void readJSONObject(BTree btree, JSONObject jsonElement) throws JSONException, IllegalAccessException, InstantiationException {
int nodeValue = jsonElement.getInt("value");
btree.value.setKey(nodeValue);
if(jsonElement.has("left")) {
JSONObject leftJSON = jsonElement.getJSONObject("left");
Model m = this.value.getClass().newInstance();
this.left = new BTree((V) m);
readJSONObject(this.left, leftJSON);
}
if (jsonElement.has("right")){
JSONObject rightJSON = jsonElement.getJSONObject("right");
V m = (V)this.value.getClass().newInstance();
this.right = new BTree(m);
readJSONObject(this.right, rightJSON);
}
}
最后,我应用自定义序列化:
private void writeObject(ObjectOutputStream oos)
throws IOException {
try {
oos.defaultWriteObject();
JSONObject root = buildJSONObject(this);
oos.writeObject(root.toString());
oos.writeObject(this.value);
} catch(Exception e) {
// handle exception
throw new RuntimeException(e);
}
}
// assumes "static java.util.Date aDate;" declared
private void readObject(ObjectInputStream ois)
throws ClassNotFoundException, IOException {
ois.defaultReadObject();
String jsonString = (String)ois.readObject();
this.value = (V)ois.readObject();
try {
readJSONObject(jsonString);
} catch (Exception e) {
// handle exception
throw new RuntimeException(e);
}
}
下面是解决StackOverflowException问题的修订类的代码。它确实有一个关键缺点:对于不平衡的树,它的效率非常低,因为它必须反复调整内部数组的大小
class BTree<V extends Model> implements Serializable {
/**
*
*/
private static final long serialVersionUID = -7602392759811243945L;
private static final int MINIMUM_CAPACITY = 15;
private Model[] values;
private int depth = 3;
public void insert(V newValue) {
int index = 0;
while(true) {
ensureSpotExists(index);
Model value = values[index];
if (value == null) {
values[index] = newValue;
return;
} else if (newValue.getKey().hashCode() < value.getKey().hashCode()) {
index = (2 * index) + 1;
} else if (newValue.getKey().hashCode() > value.getKey().hashCode()) {
index = (2 * index) + 2;
} else {
values[index] = newValue;
return;
}
}
}
protected void ensureSpotExists(int index) {
if (this.values == null) {
this.values = new Model[MINIMUM_CAPACITY];
} else if (this.values.length < index + 1) {
Model[] temp = this.values;
this.values = new Model[getSize(++depth)];
for(int i = 0; i < temp.length; i++) {
this.values[i] = temp[i];
}
}
}
protected static int getSize(int depth) {
int size = 0;
for(int i = 0; i <= depth; i++) {
size += Math.pow(2, i);
}
return size;
}
public boolean containsKey(Object key) {
return this.find(key) != null;
}
public V find(Object key) {
return null;
}
void replace(Object key, V value) {
return;
}
public List<Model> getAllValues() {
return Arrays.asList(this.values);
}
}
读完标题后,我认为这与网站有关P@Corey你能包括建立树的代码吗?似乎可以通过使用insert方法插入相同的节点来实现循环引用/constructor@mom-我已添加了我能找到的所有正在调用的代码行。请检查您的程序是否在任何数量的节点上失败,或者是否在较小数量的节点上工作,以及在大量节点上是否失败。在前一种情况下,肯定有一些循环引用正在消耗堆栈内存。@Santosh-它似乎确实适用于16个节点以下的情况。虽然我没有考虑过循环引用,但我会进一步调查。不幸的是,这也不起作用。它在同一点上给出了stackoverflow错误。不过还是不错的尝试。我对工作抱有希望。我确实让它与一个基于数组的BTree一起工作,我将很快在这里发布…或者树太深,也称为链表。序列化链表也可以做同样的事情 { root : { "value" : 8 "left": { "value" : 3 "left" : { "value" : 1 } }, "right" : { "value" : 10 "right" : { "value": 14 } } } }
private JSONObject buildJSONObject(BTree root) throws JSONException {
JSONObject baseJSON = new JSONObject();
JSONObject rootElement = new JSONObject();
rootElement.put("value", root.getValue());
baseJSON.put("root", rootElement);
buildJSONObject(root, rootElement);
return baseJSON;
}
private void buildJSONObject(BTree currentNode, JSONObject jsonElement) throws JSONException {
jsonElement.put("value", currentNode.getValue());
if(currentNode.left != null) {
JSONObject leftJSON = new JSONObject();
jsonElement.put("left", leftJSON);
leftJSON.put("value", currentNode.left.getValue());
buildJSONObject(currentNode.left, leftJSON);
}
if (currentNode.right != null ){
JSONObject rightJSON = new JSONObject();
jsonElement.put("right", rightJSON);
rightJSON.put("value", currentNode.right.getValue());
buildJSONObject(currentNode.right, rightJSON);
}
}
private void readJSONObject(String json) throws JSONException, IllegalAccessException, InstantiationException {
JSONObject baseJSON = new JSONObject(json);
JSONObject rootElement = baseJSON.getJSONObject("root");
readJSONObject(this, rootElement);
}
private void readJSONObject(BTree btree, JSONObject jsonElement) throws JSONException, IllegalAccessException, InstantiationException {
int nodeValue = jsonElement.getInt("value");
btree.value.setKey(nodeValue);
if(jsonElement.has("left")) {
JSONObject leftJSON = jsonElement.getJSONObject("left");
Model m = this.value.getClass().newInstance();
this.left = new BTree((V) m);
readJSONObject(this.left, leftJSON);
}
if (jsonElement.has("right")){
JSONObject rightJSON = jsonElement.getJSONObject("right");
V m = (V)this.value.getClass().newInstance();
this.right = new BTree(m);
readJSONObject(this.right, rightJSON);
}
}
private void writeObject(ObjectOutputStream oos)
throws IOException {
try {
oos.defaultWriteObject();
JSONObject root = buildJSONObject(this);
oos.writeObject(root.toString());
oos.writeObject(this.value);
} catch(Exception e) {
// handle exception
throw new RuntimeException(e);
}
}
// assumes "static java.util.Date aDate;" declared
private void readObject(ObjectInputStream ois)
throws ClassNotFoundException, IOException {
ois.defaultReadObject();
String jsonString = (String)ois.readObject();
this.value = (V)ois.readObject();
try {
readJSONObject(jsonString);
} catch (Exception e) {
// handle exception
throw new RuntimeException(e);
}
}
class BTree<V extends Model> implements Serializable {
/**
*
*/
private static final long serialVersionUID = -7602392759811243945L;
private static final int MINIMUM_CAPACITY = 15;
private Model[] values;
private int depth = 3;
public void insert(V newValue) {
int index = 0;
while(true) {
ensureSpotExists(index);
Model value = values[index];
if (value == null) {
values[index] = newValue;
return;
} else if (newValue.getKey().hashCode() < value.getKey().hashCode()) {
index = (2 * index) + 1;
} else if (newValue.getKey().hashCode() > value.getKey().hashCode()) {
index = (2 * index) + 2;
} else {
values[index] = newValue;
return;
}
}
}
protected void ensureSpotExists(int index) {
if (this.values == null) {
this.values = new Model[MINIMUM_CAPACITY];
} else if (this.values.length < index + 1) {
Model[] temp = this.values;
this.values = new Model[getSize(++depth)];
for(int i = 0; i < temp.length; i++) {
this.values[i] = temp[i];
}
}
}
protected static int getSize(int depth) {
int size = 0;
for(int i = 0; i <= depth; i++) {
size += Math.pow(2, i);
}
return size;
}
public boolean containsKey(Object key) {
return this.find(key) != null;
}
public V find(Object key) {
return null;
}
void replace(Object key, V value) {
return;
}
public List<Model> getAllValues() {
return Arrays.asList(this.values);
}
}