您好我正在编写一个将String解析为单个组件的程序,但是当我尝试测试它时,我得到一个内存不足错误。我觉得好像我的for / while循环是无限的,但我似乎无法找到原因。
//for loop to loop through char of string
for(int i=0; i<expressionString.length(); i++) {
//cast char into ascii int
int ascii = (int) charAt(i);
//appending to token if one of singly operator symbols: *,/,(,),[,]
if(ascii == 40 || ascii == 41 || ascii == 42 || ascii == 47 || ascii == 91 || ascii == 93){
token.append((char) ascii);
tokenList.add(token.toString());
} //append if +, -
else if(ascii == 43 || ascii == 45) {
token.append((char) ascii);
//check next char if + or /, if so append to token again
int nextChar = (char) charAt(i+1);
if(nextChar == 43 || nextChar == 45) {
token.append((char) nextChar);
}
tokenList.add(token.toString());
} //appending to token if it's a num
else if ( ascii >= 48 || ascii <=57) {
token.append((char) ascii);
//check if next char is a num
while ((int) charAt(i+1) >= 48 || (int) charAt(i+1) <= 57) {
//increment i in for loop to check
i++;
token.append((int) charAt(i));
}
tokenList.add(token.toString());
}
//
}
如果这是我的代码错误,请告诉我,因为我似乎无法找到问题所在。谢谢!
答案 0 :(得分:1)
以下是您在该循环中所做的简化版本。
public class Main {
public static void main(String[] args) {
String str = "ABCDE";
StringBuilder sb = new StringBuilder();
List<String> list = new ArrayList<>();
for (char c : str.toCharArray()) {
sb.append(c);
list.add(sb.toString()); // <-- Problem! This adds the *entire* contents of the StringBuilder as a new String to the list.
}
System.out.println(list);
}
}
此程序打印
[A, AB, ABC, ABCD, ABCDE]
这是因为每次我们将char
附加到StringBuilder
时,我们会将StringBuilder
的整个内容添加为新String
转到ArrayList
。
现在假设我们将"ABCDE"
替换为长度为String
的{{1}},例如我们将第一行更改为
1000000
我们现在正尝试创建长度从String str = Stream.generate(() -> "A").limit(1000000).collect(Collectors.joining()); // String of length 1000000
到String
的1000000个1
个对象,并且结果可预测。
1000000
如何解决?这取决于你想要做什么(我们没有所有的上下文),但我怀疑你不需要Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOfRange(Arrays.java:3664)
at java.lang.String.<init>(String.java:207)
at java.lang.StringBuilder.toString(StringBuilder.java:407)
at my_package.Main.main(Main.java:17)
和StringBuilder
。
答案 1 :(得分:1)
正如我在评论中指出的那样,你在追加StringBuilder
而没有删除任何内容的事实是可疑的。
StringBuilder
只是char[]
的一个包装器,它会在必要时自动调整大小以容纳您尝试追加的新文本。您可以在堆栈跟踪中看到在其中一个自动调整大小期间发生了OOM。
这个问题的一个解决方案就是最初分配一个足够大的缓冲区,然后调整大小不需要发生,直到StringBuilder
附加了更多文本:
StringBuilder token = new StringBuilder(MAXIMUM_EXPECTED_SIZE);
问题在于可能很难确定MAXIMUM_EXPECTED_SIZE
;此外,你可能在大部分时间都在浪费大量的内存,而你在缓冲区附近的文本数量附近也是如此。
在您将文字转移到token
后,您似乎并不想将文字保留在tokenList
中。您可以使用以下命令从缓冲区中明确删除它:
token.delete(0, token.length());
// or
token.setLength(0);
(实际上,这并没有删除数据,只是允许后续追加来覆盖它)
但这仍然是浪费:你根本不需要StringBuilder
。
考虑如何处理这些数字:
if ( ascii >= 48 || ascii <=57) {
token.append((char) ascii);
//check if next char is a num
while ((int) charAt(i+1) >= 48 && (int) charAt(i+1) <= 57) {
// ^^ NB
//increment i in for loop to check
i++;
token.append((int) charAt(i));
}
tokenList.add(token.toString());
}
你在这里尝试做的是在i
个字符(包括)和j
个字符(不包括)之间追加所有内容,其中{{1} }指向字符串的结尾,或指向非数字字符。所以你可以这样做:
j
您可以为其他附加标记执行类似操作。这只是切断了'中间人&#34; if ( ascii >= 48 || ascii <=57) {
int j = i + 1;
//check if next char is a num
while (j < expressionString.length() && charAt(j) >= '0' && charAt(j) <= '9') {
j++;
}
tokenList.add(expressionString.subString(i, j));
i = j;
}
,显然避免了重新分配内部缓冲区的问题。