2

the code below is from an assignment in my text book for a simple program that takes a user's entered name and capitalizes the first letter of the first and last name. The code works properly, but I do not understand why the name.substring() works correctly. Specifically, I am interested in how the block from lines 24 - 29 works. If the user enters the name "Johnny Johnson", then i should contain the value 7 going into line 29. If i does contain 7, then shouldn't name = name.substring(0, i) contain "Johnny J" which should make line 29 actually store "Johnny JJohnson" in String name? But instead it actually stores "Johnny Johnson" as it should.

My second question comes from messing around with this code to see different results. If I change the first part of line 29 to name = name.substring(0, i-1) I get the error (using Eclipse):

Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 15 at java.lang.String.charAt(String.java:558) at RepairName.main(RepairName.java:17)

Why does the error appear on line 17 instead of line 29? Actually, why do I get an error at all because i-1 isn't actually changing the value of i correct? I assumed it had something to do with the loop but since the value of i wasn't changed I didn't know why it would.

Sorry if this was a long-winded question. I'm new to Java and pretty new to programming (obviously), but I appreciate any insight you all can give. Thanks!

 1  import javax.swing.*;
 2  
 3  public class RepairName 
 4  {
 5  public static void main(String[] args)
 6  {
 7      String name, saveOriginalName;
 8          int stringLength;
 9          int i;
10          char c;
11          name = JOptionPane.showInputDialog(null, "Please enter your first and last name");
12      
13          saveOriginalName = name;
14          stringLength = name.length();
15          for (i = 0; i < stringLength; i++)
16          {
17                  c = name.charAt(i);
18                  if (i == 0)
19                  {
20                          c = Character.toUpperCase(c);
21                          name = c + name.substring(1, stringLength);
22                  }
23                  else
24                      if(name.charAt(i) == ' ')
25                          {
26                              i++;
27                                  c = name.charAt(i);
28                                  c = Character.toUpperCase(c);
29                                  name = name.substring(0, i) + c + name.substring(i+1, stringLength);
30                          }
31          }
32          JOptionPane.showMessageDialog(null, "Original name was " + saveOriginalName + "\nRepaired name is " + name);
33 }
34
35 }

7 Answers 7

3

From the String.subString(int, int) javadoc:

public String substring(int beginIndex, int endIndex)

Returns a new string that is a substring of this string. The substring begins at the specified beginIndex and extends to the character at index endIndex - 1. Thus the length of the substring is endIndex-beginIndex.

Heres the link: http://download.oracle.com/javase/1.5.0/docs/api/java/lang/String.html#substring(int, int)

If in doubt look at the javadocs :D

As for your second quesstion, again the javadocs ( http://download.oracle.com/javase/1.5.0/docs/api/java/lang/String.html#charAt(int) ) for charAt(int) help you out:

Throws: IndexOutOfBoundsException - if the index argument is negative or not less than the length of this string.

If you use i-1 in substring you are decreasing the size of name by 1 each time it finds a ' '. This means it will iterate 15 times, but the name will only be 14 characters long after the ' ' is found.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks so much for explaining that! I didn't even think about the first iteration of the loop... I was just thinking about the last one. I've got a lot to learn. thanks for the link
Minor note: We have 1.6 now (but substring didn't change, of course).
True, lol i just googled java string api and got the first hit :D
how does the value of i get changed to -1? name = name.substring(0, i - 1) does not do the same thing to i as i = i - 1 or i-- does it?
@knobcreekman if you alter it to i-1 you will shorten the name by one character, since stringLength is not calculated each loop, it will iterate over a character that does not exist (in your example, 15 iterations but only 14 characters, hence why it says string index out of range: 15) also altered answer.
1

To quote the substring Javadoc,

The substring begins at the specified beginIndex and extends to the character at index endIndex - 1.

In other words, endIndex is not included in the result.

As to your second question, it has to do with you reducing the length of name inside the loop. Your loop condition (i < stringLength) and the fact that you look at name.charAt(i) assume that the length of name remains constant (or least does not decrease). This gets violated the moment you start shortening the string.

Comments

1

Hi You get the Exception because you iteratate over the string but in the for loop you are actually changing the strings length name = name.substring(0, i) + c + name.substring(i+1, stringLength); So while you are still in the loop the size of the name is no longer equal to the old stringLength.

Boro

Comments

1
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 15 at java.lang.String.charAt(String.java:558) at RepairName.main(RepairName.java:17)

Occurs because you are altering name on line 21 and 29 and keeping the length of name the same in stringLength

Comments

1

ad 1) The javadocs are a very good reference, here java.lang.String.substring (...):

substring

public String substring(int beginIndex, int endIndex)

Returns a new string that is a substring of this string. The substring begins at the specified beginIndex and extends to the character at index endIndex - 1. Thus the length of the substring is endIndex-beginIndex.

The benefit, in excluding the index of the end, is, that you can sequently iterate over values:

a [0] = x.substring (0, 5);
a [1] = x.substring (5, 10);
a [2] = x.substring (10, 15);

// or 

a [i] = x.substring (i*5, (i+1)*5);

2 Comments

thanks for the help! the second part is a little over my head though embarrassed
The end of one sequence (5, 10, ...) can be the beginning of the next sequence (..., 5, 10). Another benefit is, that you can use String.length as end-index without -1. Not very important, just convenience.
1

that's correct substring works in this way:

public String substring(int beginIndex, int endIndex)

Returns a new string that is a substring of this string. The substring begins at the specified beginIndex and extends to the character at index endIndex - 1. Thus the length of the substring is endIndex-beginIndex.

so for substring(0, 7) it takes 0..6 chars from string


Notice that this is really bad practice to modify iterate's value in for-loop (i++ inside for-loop-body). If after space you haven't any other chars, you'll encounter exception

Comments

1

ad 2)

You don't change i itself, but name gets truncated here:

name = name.substring (0, i-1) + c + name.substring(i+1, stringLength);

but the outer loop isn't informed about the new length.

ad 3) (Code-review):

You should declare and initialize your variables as late as possible, and mark as much as possible final. This tells you, that you don't have to worry about them, after they're initialized.

Given this code:

import javax.swing.*;

public class RepairName 
{
    public static void main(String[] args)
    {
        final String name = JOptionPane.showInputDialog(null, "Please enter your first and last name");
        final String saveOriginalName = name;
        final int stringLength = name.length ();
        System.out.println (name);
        for (int i = 0; i < stringLength; i++)
        {
                final char c = name.charAt (i);
                if (i == 0)
                {
                        char upper = Character.toUpperCase (c);
                        name = upper + name.substring (1, stringLength);
                }
                else
                    if (c == ' ')
                        {
                            i++;
                                final char c2 = name.charAt (i);
                                final char upper = Character.toUpperCase (c2);
                                name = name.substring (0, i-1) + upper + name.substring (i+1, stringLength);
                        }
        }
        JOptionPane.showMessageDialog (null, "Original name was " + saveOriginalName + "\nRepaired name is " + name);
    }
}

you would have get an error message, for name being final. Maybe this would have prevented you to accidentially get out of sync with the length.

modified, to have a mutable upName, but iterating over name:

    String upName = null;
    for (int i = 0; i < stringLength; i++)
    {
            final char c = name.charAt (i);
            if (i == 0)
            {
                    char upper = Character.toUpperCase (c);
                    upName = upper + name.substring (1, stringLength);
            }
            else
                if (c == ' ')
                    {
                        i++;
                            final char c2 = name.charAt (i);
                            final char upper = Character.toUpperCase (c2);
                            upName = upName.substring (0, i-1) + upper + name.substring (i+1, stringLength);
                    }
    }
    JOptionPane.showMessageDialog (null, "Original name was " + saveOriginalName + "\nRepaired name is " + upName);

gives you, for input "honky tonky" the output "HonkyTonky", which would have led you to the way, substring (from, to) works, maybe. :)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.