我写了一个n-ary树ADT工作正常。但是,我需要将其序列化存储在变量调用类中。例如。
DomTree<String> a = Data.createTreeInstance("very_large_file.xml");
String x = a.toString();
我编写的方法完全符合我的需要,但是在非常大的输入上它需要永远(在100MB xml文件上20分钟) - 我已经定时方法并从xml文件构建树很快,但如上所示调用toString()非常慢。
@Override
public String toString(){
return printTree(this);
}
public String printTree(AbstractTree<E> tree){
if (tree.isLeaf()){
return tree.getNodeName();
}else{
String tStr = tree.getNodeName() + "(";
int i = 0;
Iterator<AbstractTree<E>> child = tree.getChildren().iterator();
while (i < tree.getChildren().size() - 1){
tStr += printTree(child.next()) + ", ";
i++;
}
tStr += printTree(child.next()) + ")";
return tStr;
}
}
我猜这是建立字符串的方式而不是遍历树的方式?有更好的方法吗?
更新:按照Skaffman的例子,下面的代码为非常大的输入提供outOfMemoryError。
@Override
public String toString(){
StringBuilder buffer = new StringBuilder();
printTree(this, buffer);
return buffer.toString();
}
public String printTree(AbstractTree<E> tree, StringBuilder buffer){
if (tree.isLeaf()){
return tree.getNodeName();
}else{
buffer.append(tree.getNodeName());
buffer.append("(");
int i = 0;
Iterator<AbstractTree<E>> child = tree.getChildren().iterator();
while (i < tree.getChildren().size() - 1){
buffer.append(printTree(child.next(), buffer));
buffer.append(", ");
i++;
}
buffer.append(printTree(child.next(), buffer));
buffer.append(")");
return buffer.toString();
}
}
更新:现在使用Skaffmans示例完美地运作
答案 0 :(得分:15)
这样的字符串连接速度非常慢。使用StringBuilder。
@Override
public String toString(){
StringBuilder buffer = new StringBuilder();
printTree(this, buffer);
return buffer.toString();
}
public void printTree(AbstractTree<E> tree, StringBuilder buffer){
if (tree.isLeaf()){
buffer.append(tree.getNodeName());
} else {
buffer.append(tree.getNodeName());
buffer.append("(");
int i = 0;
Iterator<AbstractTree<E>> child = tree.getChildren().iterator();
while (i < tree.getChildren().size() - 1){
printTree(child.next(), buffer);
buffer.append(", ");
i++;
}
printTree(child.next(), buffer);
buffer.append(")");
}
}
答案 1 :(得分:4)
不要在循环中使用字符串连接。它不会扩展。
使用StringBuilder,这不会一直生成新对象,比如字符串连接..
void print() {
StringBuilder sb = new StringBuilder();
sb.append("hello");
sb.append(" World!");
System.out.println(sb.toString());
}
答案 2 :(得分:3)
查看StringBuilder,不要使用简单的连接,并将StringBuilder传递给整个过程(或使其成为全局过程)。
答案 3 :(得分:3)
让我说字符串连接缓慢的原因是字符串是不可变的。这意味着每次编写“+ =”时,都会创建一个新的String。这意味着你建立你的字符串的方式是在最坏的情况下,O(n 2 )。那是因为如果你一次+ ='ed 1 char,那么构建一个新字符串的成本将是2 + 3 + 4 + ... + n,即O(n 2 )。
使用StringBuilder作为其他建议(通过较慢但线程安全的StringBuffer)。
我想我应该补充一点,StringBuilder会给你O(n)摊销时间,因为它就像幕后的矢量一样,因为它是可变的。所以在那里建立你的字符串,然后调用toString()。
StringBuilder builder = new StringBuilder();
builder.append("blah"); // append more as needed.
String text = builder.toString();
我还想补充一点,这个问题在Python中是类似的。 python中的习惯用法是将所有字符串附加到列表中,然后加入列表。 "".join(the_list)
。
更新:正如比尔所指出的那样,连接不是所有邪恶的根源。一个字符串连接很好,甚至可以优化! (它们也是最坏的情况线性)。但是,当您在循环中连接时,如上所述,性能将随着迭代次数的增加而急剧变化。在这种情况下,我的上述分析是完美的,因为我特别声明它是“最坏情况”,这意味着你不假设没有优化。 (JVM甚至无法在循环中优化连接,也可以在外部优化连接)。
答案 4 :(得分:2)
如果探查者确认您的瓶颈是字符串连接,您有两种选择:
绳索是Strings的高性能替代品。 “绳索:字符串的替代”中详细描述的数据结构提供了比String和StringBuffer渐近更好的性能,用于常见的字符串修改,如prepend,append,delete和insert。与字符串一样,绳索是不可变的,因此非常适合用于多线程编程。
答案 5 :(得分:-1)
您可能希望将String.intern()视为减少内存使用的一种方法。这将使用字符串池中的interned String。如果您有许多重复的字符串,它可能会更快。关于实习字符串here
的更多信息