我正在尝试为Android应用程序实现二叉树,我希望能够将其序列化到设备上的文件中。不幸的是,我在尝试序列化时遇到了stackoverflow错误。
代码:
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;
}
}
我正在尝试在一个单独的类中序列化这个文本块:
try {
FileOutputStream fos = this.context.openFileOutput(FILE_NAME, Context.MODE_PRIVATE);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(this.tree);
oos.close();
//this.lastModified = getLastModified(FILE_NAME);
} catch (FileNotFoundException e) {
//File will get created, so this doesn't matter
} catch (IOException e) {
Log.d("BTreeModel Serialization Error", "Error serialization model.");
Log.e("Serialization error details", e.toString());
}
我注意到的是,如果我序列化到一个当前不存在的文件,那么序列化很好。第二次运行相同的程序时,再次序列化到磁盘时会导致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
从添加节点的活动:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//TextView averageHashMapTime = (TextView)findViewById(R.id.averageHashCollectionTime);
//averageHashMapTime.setText("Just a moment");
//TextView averageBtreeTime = (TextView)findViewById(R.id.averageBTreeCollectionTime);
//averageBtreeTime.setText("working");
HashMapModelCollection<Integer, Content> hashCollection = new HashMapModelCollection<Integer, Content>(this);
long totalTicks = 0;
final int totalIterations = 16;
BTreeModelCollection<Integer, Content> btreeCollection = new BTreeModelCollection<Integer, Content>(this);
totalTicks = 0;
for(int i = 0; i < totalIterations; i++) {
Content test = new Content();
test.setText(String.valueOf(i));
Random r = new Random();
int key = r.nextInt();
Date start = new Date();
btreeCollection.put(key, test);
Date end = new Date();
totalTicks += end.getTime() - start.getTime();
}
Log.d("Finished", "btree length: " + String.valueOf(totalTicks / totalIterations));
Toast.makeText(this, "btree length: " + String.valueOf(totalTicks / totalIterations), Toast.LENGTH_LONG).show();
//averageBtreeTime.setText(String.valueOf(totalTicks / totalIterations));
}
直接处理BTree对象的类:
public class BTreeModelCollection<K extends Serializable, V extends Model> implements IModelCollection<K, V>,
Serializable {
/**
*
*/
private static final long serialVersionUID = -3969909157515987705L;
private static final String FILE_NAME = "testCollection-5";
private BTree<V> tree;
private Context context;
public BTreeModelCollection(Context context) {
this.context = context;
}
@Override
public void clear() {
// TODO Auto-generated method stub
}
@Override
public boolean containsKey(Object key) {
return tree.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
// TODO Auto-generated method stub
return false;
}
@Override
public Set<java.util.Map.Entry<K, V>> entrySet() {
// TODO Auto-generated method stub
return null;
}
@Override
public V get(Object key) {
return getTree().find(key);
}
@Override
public boolean isEmpty() {
// TODO Auto-generated method stub
return false;
}
@Override
public Set<K> keySet() {
// TODO Auto-generated method stub
return null;
}
@Override
public void put(K key, V value) {
value.setKey(key);
getTree();
if (this.tree != null) {
this.tree.insert(new BTree<V>(value));
} else {
this.tree = new BTree<V>(value);
}
serialize();
}
@Override
public V remove(Object key) {
// TODO Auto-generated method stub
return null;
}
@Override
public int size() {
// TODO Auto-generated method stub
return 0;
}
@Override
public Collection<V> values() {
return getTree().getAllValues();
}
@Override
public void putAll(Map<? extends K, ? extends V> map) {
// TODO Auto-generated method stub
}
private BTree<V> getTree() {
if (this.tree == null) {
loadTree();
}
return tree;
}
@SuppressWarnings("unchecked")
private void loadTree() {
try {
FileInputStream fis = context.openFileInput(FILE_NAME);
ObjectInputStream ois = new ObjectInputStream(fis);
this.tree = (BTree<V>)ois.readObject();
ois.close();
} catch (FileNotFoundException e) {
this.tree = null;
} catch (StreamCorruptedException e) {
e.printStackTrace();
} catch (IOException e) {
this.tree = null;
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private void serialize() {
if (this.tree == null) {
Log.w("Serialization problem", "No collection to serialize to disk");
return;
}
try {
FileOutputStream fos = this.context.openFileOutput(FILE_NAME, Context.MODE_PRIVATE);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(this.tree);
oos.close();
//this.lastModified = getLastModified(FILE_NAME);
} catch (FileNotFoundException e) {
//File will get created, so this doesn't matter
} catch (IOException e) {
Log.d("BTreeModel Serialization Error", "Error serialization model.");
Log.e("Serialization error details", e.toString());
}
}
}
Model类:
public abstract class Model implements Serializable {
/**
*
*/
private static final long serialVersionUID = -5724152403115334006L;
private Serializable key;
void setKey(Serializable key) {
this.key = key;
}
Serializable getKey() {
return this.key;
}
}
答案 0 :(得分:0)
当递归没有正确的退出条件时,通常会发生这种情况。
答案 1 :(得分:0)
我已经复制了你的bug。看起来Android没有很好地处理深度递归,因为我在桌面上尝试了相同的程序,它的工作正常,我正在尝试。
根据评论中的讨论,我做了不涉及递归到Object级别的替代序列化。肯定有不止一种方法可以做到这一点,但我选择将结构序列化为JSON数据格式的String并将其写入输出流。请注意,我没有彻底测试过。这个答案的目的是提供一种你可能也可以采取的方法。
现在假设我的值是int,我认为JSON结构应该如下:
{ root : { "value" : 8 "left": { "value" : 3 "left" : { "value" : 1 } }, "right" : { "value" : 10 "right" : { "value": 14 } } } }
现在遵循该结构,我有在BTree类中构建树的方法:
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);
}
}
答案 2 :(得分:0)
以下是解决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);
}
}