为什么我的程序会抛出StringIndexOutOfBounds异常?

3

我正在编写一个程序来计算:

1^1 + 2^2 + 3^3 + ... + 1000^1000 = ?

计算完成后,我希望除了最后十位数字之外,删除答案中的所有数字,并将最终十位数字打印在屏幕上。为了删除其他所有数字,我正在使用 StringBuilder.deleteCharAt() 方法,但是这给我带来了一些问题。我提供了我的代码如下:

import java.math.BigInteger;
public class problemFourtyEight {
  public static void main(String [] args) {
    BigInteger answer = new BigInteger(""+0);
    for(int i = 0; i <= 1000; i++) {
      BigInteger temp = new BigInteger(""+i);
      temp = temp.pow(i);
      answer = answer.add(temp);
    }
    System.out.println(answer.toString().length());
    StringBuilder sb = new StringBuilder((answer.toString()));
    for(int k = 0; k <= (answer.toString().length() - 10); k++) {
      sb.deleteCharAt(k);  //Line 13 is here.
    }
    String finalAnswer = sb.toString();
    System.out.println(finalAnswer);
  }
}

抛出的异常是:
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 1501
    at java.lang.AbstractStringBuilder.deleteCharAt(AbstractStringBuilder.java:797)
    at java.lang.StringBuilder.deleteCharAt(StringBuilder.java:253)
    at problemFourtyEight.main(problemFourtyEight.java:13)

通过打印答案的长度(在去除任何字符之前),它告诉我数字的长度为3001,所以我不明白为什么String索引 1,501被说成是OutOfBounds


4
你为什么不用substring()函数?你意识到为了获取最后10个字符,你正在执行多少次操作和字符串复制吗?请注意优化代码。 - JB Nizet
4个回答

6
您的错误在于以下代码:
StringBuilder sb = new StringBuilder((answer.toString()));
for(int k = 0; k <= (answer.toString().length() - 10); k++) {
  sb.deleteCharAt(k);  //Line 13 is here.
}

由于字符串生成器的可变性,随着字符串长度的减少,其效率更高。您需要:

System.out.println(answer.substring(answer.length()-10))

这也具有速度优势,如果这对你很重要。

1

如果您检查了StringBuilder对象的长度,那么这段代码可能会(在某种程度上)起作用,但是现在您检查的是原始字符串的长度,而删除的是StringBuilder中的字符。每次删除后,StringBuilder都会变短,因此最终sbk索引超出了边界。

StringBuilder sb = new StringBuilder((answer.toString()));
for(int k = 0; k <= (sb.length() - 10); k++) { // <-- make sure to test the length of the thing you are mutating
  sb.deleteCharAt(0);  // Delete first character of remaining sb
}

但是,是的,这里应该更喜欢使用substring()

1
当您删除第i个字符时,第i+1个字符将成为新的第i个字符。因此,您的查找应始终删除第一个字符:
for(int k = 0; k <= (answer.toString().length() - 10); k++) {
  sb.deleteCharAt(0);
}

例如:

例如:

        StringBuilder sb = new StringBuilder("abcdef");
        sb.deleteCharAt(0);
        sb.deleteCharAt(1);
        sb.deleteCharAt(2);
        sb.deleteCharAt(3); // throws java.lang.StringIndexOutOfBoundsException: String index out of range: 3
        System.out.println(sb.toString());

        sb = new StringBuilder("abcdef");
        sb.deleteCharAt(0);
        sb.deleteCharAt(0);
        sb.deleteCharAt(0);
        sb.deleteCharAt(0);
        System.out.println(sb.toString()); // prints "ef"

0
我认为这是因为每次从字符串中删除一个字符时,字符串缓冲区会重新调整大小,导致您的字符串变短。因此,在从3001个字符中删除1500个字符后,您的字符串缓冲区将存储从0到1500的字符,而索引1501超出了范围。
您可以使用substring打印最后10位数字。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接