简体   繁体   中英

New to programming - ***Updated*** PracticeProblem throwing StringIndexOutOfBoundsException

I'm trying to teach myself Java and I've recently been doing online practice problems with a built in compiler. My code is working great for most of the conditions except for two in which the string length is less than two.

The URL to this specific practice problem is: http://codingbat.com/prob/p123384

Problem: Given a string, return a new string where the first and last chars have been exchanged.

Example

frontBack("code") should be→ "eodc"
frontBack("a") should be→ "a"
frontBack("ab") should be→ "ba"

Here's my code:

    public String frontBack(String str) {

  char firstChar = str.charAt (0);

  char lastChar = str.charAt (str.length()-1);

  String middle = str.substring(1, str.length()-1);

    if (str.length()>=3){

  return lastChar + middle + firstChar;

  }

 else {

return new StringBuilder(str).reverse().toString();

}

}

These are the two conditions that error

frontBack("a") should be→ "a"

"Exception:java.lang.StringIndexOutOfBoundsException: String index out of range: -1 (line number:4)"


frontBack("") should be→ ""

"Exception:java.lang.StringIndexOutOfBoundsException: String index out of range: 0           (line number:2)"

Here's the solution, the code that works

public String frontBack(String str) {
  if (str.length() <= 1) return str;

  String mid = str.substring(1, str.length()-1);

  // last + mid + first
  return str.charAt(str.length()-1) + mid + str.charAt(0);
}

What's the difference between my code and the solution?

Any help would be great. I'm confused on this one since my else statement simply returns the original string. Why would any of the variables (frontChar, middle, lastChar) affect the original string I'm returning? Thanks in advance!

First I want to say thank you to everyone that helped me!*

I've reworked my code and narrowed it down to one simple difference. That difference is the placement of an explicit statement to account for strings equal to or less than one. This apparently can't be handled implicitly by a catch-all else statement. Still not exactly sure why? Here is the same code with one minor difference; Explicit vs Implicit

This code works... Explicitly return str if string length is less than or equal to 1.

public String frontBack(String str) {

  // This line below is the only difference 

  if (str.length() <= 1) {
   return str;
  }

  char firstChar = str.charAt (0);

  char lastChar = str.charAt (str.length()-1);

  String middle = str.substring(1, str.length()-1);


  if (str.length()>=2){

  return lastChar + middle + firstChar;

  }

  else {
  return str;
  }
}

This code doesn't work... Implicitly return str with an else statement if string length is less than or equal to 1.

public String frontBack(String str) {

  char firstChar = str.charAt (0);

  char lastChar = str.charAt (str.length()-1);

  String middle = str.substring(1, str.length()-1);


  if (str.length()>=2){

  return lastChar + middle + firstChar;

  }

  else {
  return str;
  }
}

You need to program to defend


1.

for one character String

  String middle = str.substring(1, str.length()-1);

this would be

str.substring(1, 0)

which is invalid


2.

for empty string ( length = 0 ) your code will attempt to look at index = -1 which is invalid, or even index = 0 is invalid


3.

what if str is null

You always have to think of boundary conditions:

str is null? str is empty("") str is 1 char? also bear in mind that Java's substring is inclusive on start position but exclusive on the end.

    public String frontBack(String str) {
      if (str == null || str.length()<2){
          //if str is null - return empty. else return the string itself
          return str == null ? "" : str;
      }else{
         //do the actual first last swap
         return  str.charAt(str.length()-1)+ str.substring(1,str.length()-1) + str.charAt(0);
      } 
    }

I've reworked my code and narrowed it down to one simple difference. That difference is the placement of an explicit statement to account for strings equal to or less than one. This apparently can't be handled implicitly by a catch-all else statement. Still not exactly sure why?

It has nothing to do with implicit or explicit. The problem is simply the sequence in which the lines are executed within the method, which is top to bottom.

By the time it gets to your if/else in the "implicit" case, the three lines above, such as char firstChar = str.charAt(0); have already happened (or already failed). For an empty string it will blow up with a StringIndexOutOfBounds exception immediately on that line (look at the line number of the exception), and the method will stop executing then as it throws the exception out of the method. (The technical term for this is abrupt completion .) If that exception is thrown, the below if/else won't happen, so it cannot retroactively prevent the exception afterwards. That code will work fine if you move the three string-using statements from the top of the method to inside the 'if' branch of the if/else, so that they are not executed when not wanted.

In the "explicit" case, you added if (str.length() <= 1) return str; at the top of the method, where it can prevent the lines below from executing, because the return statement will exit the method before the attempts to use the non-existent characters of the string. That statement wouldn't help if it was lower down in the method.


I just want to add something about 'null', since other answerers on this page have complained that your code doesn't defend against a null argument. It's good to think about how your method will cope with peculiar input, but here, your code already does correctly defend against a null argument, because when you try to call charAt or length or substring on null, it will throw a NullPointerException out of the method, as it should.

Sometimes null should be checked for and allowed, but here it should not. If called without a valid string, what might frontBack be expected to return? Some might say it should return null, some might say it should return the empty string, and some might say it should coerce it to a string and thus return "luln" . There is no fully sensible or unambiguously correct way to handle it that will work for all callers. So, frontBack should take the stance that null is not an acceptable input. Then it is the caller's fault if they pass in null, and if the caller passes in garbage, it's correct that they should get the garbage thrown back at them (metaphorically, of course; you will actually throw an exception object, not the original input) so they are forced to deal with it properly. Tolerating and making excuses for bad callers by explicitly coercing garbage to valid input is the wrong thing to do because it hides errors and it makes the program's behavior more subtle.

If the method had side effects , such as modifying a data structure, writing to a file, or calling other methods with side effects, then null input could be dangerous, because the method might complete some of the side effects without using str . Then, trying to use str would at some point throw the exception, leaving things half-finished and in an invalid state. Ideally, the side effects should be observed to happen either entirely or not at all. In that case, it would be good to check for null and throw the exception yourself at the top of the method:

if (str == null) throw new NullPointerException();

That won't make a difference in this simple method because it has no side effects (it is purely functional ) and because it already unconditionally calls methods on str , which will cause the same exception to be thrown anyway.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM