【问题标题】:Remove a Character From String in Java在Java中从字符串中删除一个字符
【发布时间】:2022-06-15 07:56:15
【问题描述】:

我正在尝试将一个字符串与其自身连接并从结果字符串中删除所有大写字母

这是我的代码:

public String removeCapitals(String A) {
    StringBuilder B = new StringBuilder(A+A);
    int n = B.length();

    for(int i=0; i<n; i++){
        if(B.charAt(i)>='A' && B.charAt(i)<='Z'){
            B.deleteCharAt(i);
        }
    }

    return B.toString();
}

我收到异常说:

Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 6
at java.lang.AbstractStringBuilder.charAt(AbstractStringBuilder.java:237)
at java.lang.StringBuilder.charAt(StringBuilder.java:76)
at Solution.removeCapitals(Solution.java:10)
at Main.main(Main.java:190)

谁能帮我理解这个问题。

【问题讨论】:

  • n 包含开始循环之前 StringBuilder 的长度。当你调用B.deleteCharAt时,StringBuilder的长度不再和n的值一样。
  • 问题是您将“n”定义为 B 的长度(假设为 6),然后在循环时从 B 中删除一个或多个字符(因此其长度小于 6),但循环仍然上升到 6,因此它到达了一个点,它触及一个不再存在的索引(例如 6 本身),导致字符串越界异常

标签: java string algorithm stringbuilder stringindexoutofbounds


【解决方案1】:

如果至少有一次删除成功,在某些时候您的代码将尝试访问超过 StringBuilder 长度的无效索引。

这是因为变量n 保持不变。您应该将条件更改为绑定到StringBuilder 的当前大小,并在每次删除时减少索引,或向后迭代(如另一个答案所示)。

条件B.charAt(i)&gt;='A' &amp;&amp; B.charAt(i)&lt;='Z'也可以替换为:

Character.isUpperCase(b.charAt(i))

哪个更具描述性。

可能是这样的:

 public static String removeCapitals(String a) {
    
    StringBuilder b = new StringBuilder(a + a);
    
    for (int i = 0; i < b.length(); i++) {
        if (Character.isUpperCase(b.charAt(i))) {
            b.deleteCharAt(i); // which can be combined with the next line `b.deleteCharAt(i--);` - previous value of `i` would be used in the call `deleteCharAt()` and variable `i` will hold a value decremented by 1
            i--;
        }
    }
    return b.toString();
}

方法deleteCharAt() 在线性时间内运行,因为它会移动底层数组字节中的所有后续字符。每个大写字母都会触发这些变化,在最坏的情况下,它会导致二次整体时间复杂度O(n ^ 2)

您可以在不使用循环和StringBuilder 的情况下使您的方法更高效、更简洁。此代码将在线性时间 O(n) 内运行。

public static String removeCapitals(String a) {
    
    return a.replaceAll("\\p{Upper}", "").repeat(2);
}

【讨论】:

    【解决方案2】:

    当您删除一个字符时,您会更改 StringBuilder 的长度。但是n 仍然具有原始长度。所以你最终会超过StringBuilder的大小。所以从头开始,然后向后移动。这样,任何删除都将出现在after(基于相对索引)下一个位置,因此索引将在修改后的StringBuilder 大小内。此外,从末尾删除效率更高,因为在 StringBuilder 中要做的复制更少。

    public String removeCapitals(String A) {
        StringBuilder B = new StringBuilder(A+A);
        int n = B.length(); 
    
        for(int i=n-1; i>=0; i--){
            if(B.charAt(i)>='A' && B.charAt(i)<='Z'){
                B.deleteCharAt(i);
            }
        }
    
        return B.toString();
    }
    

    【讨论】:

      【解决方案3】:

      如果只是从字符串中删除大写字符。替代解决方案只需创建另一种方法 replaceAll() + regex

      private static String removeCapitals(String A){
          if (!A.isEmpty() && !A.equals("")) {
              String B = A + A;
              String newStr = B.replaceAll("([A-Z])", "");
              return newStr;
          } else {
              return null;
          }
      }
      

      【讨论】:

        【解决方案4】:

        为您的任务提供更短的解决方案。

        String a = "ABcdEF";
        String b = "";
        for (int i = 0; i < a.length(); i++) {
            if(a.toLowerCase().charAt(i) == a.charAt(i))
               b+=a.charAt(i);
            }
            System.out.println(b);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2023-03-14
          • 2023-01-28
          • 2013-08-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多