关于java:为什么未为“ substring(startIndex,endIndex)”抛出“超出范围”

Why is “out of range” not thrown for 'substring(startIndex, endIndex)'

在Java中,我正在使用substring()方法,但不确定为什么它不会引发"索引不足"错误。

字符串abcde的索引从0到4,但是substring()方法将startIndex和endIndex作为参数,这是基于我可以调用foo.substring(0)并获取" abcde"的事实。

那么为什么substring(5)起作用? 该索引应超出范围。 有什么解释?

1
2
3
4
5
6
7
8
9
10
11
/*
1234
abcde
*/

String foo ="abcde";
System.out.println(foo.substring(0));
System.out.println(foo.substring(1));
System.out.println(foo.substring(2));
System.out.println(foo.substring(3));
System.out.println(foo.substring(4));
System.out.println(foo.substring(5));

此代码输出:

1
2
3
4
5
6
abcde
bcde
cde
de
e
     //foo.substring(5) output nothing here, isn't this out of range?

当我用6替换5时:

1
foo.substring(6)

然后我得到错误:

1
2
Exception in thread"main" java.lang.StringIndexOutOfBoundsException:
    String index out of range: -1

根据Java API文档,当起始索引大于字符串的长度时,子字符串将引发错误。

IndexOutOfBoundsException - if
beginIndex is negative or larger than
the length of this String object.

实际上,它们提供了一个与您非常相似的示例:

1
"emptiness".substring(9) returns"" (an empty string)

我想这意味着最好将Java String视为以下内容,其中将索引包装在|中:

1
|0| A |1| B |2| C |3| D |4| E |5|

也就是说,字符串同时具有开始索引和结束索引。


当您执行foo.substring(5)时,它将获得子字符串,该子字符串从" e"之后的位置开始,一直到字符串的末尾。顺便提及,开始位置和结束位置恰好相同。因此,为空字符串。您可以认为索引不是字符串中的实际字符,而是字符之间的位置。

1
2
3
4
        ---------------------
String: | a | b | c | d | e |
        ---------------------
Index:  0   1   2   3   4   5


我知道这个线程已经很老了,但是这是一个根本性的问题,我认为需要澄清。

这个问题是正确的。我将此视为Java String.substring(int beginIndex,int endIndex)方法中的软件故障。

http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#substring%28int,%20int%29。

从Java文档https://docs.oracle.com/javase/tutorial/java/nutsandbolts/arrays.html

Java Arrays

Java / C / C ++和我所知道的所有其他语言都不会将数组索引视为数组元素之间的"分隔符"。

参数:
beginIndex-起始索引(含)。
endIndex-结束索引(不包括)。

endIndex的名称错误,因为该语言不允许内存访问endIndex + 1处的地址(包括最后一个数组元素),或者endIndex定义不正确,并且必须为:
endIndex-结束索引(含)。

最可能的情况是第二个参数的名称错误。它应该是:
length-从beginIndex开始的所需字符串的长度。

我们知道,出于熟悉的目的,Gosling基于C / C ++语言编写了Java语法。从C ++字符串类http://www.cplusplus.com/reference/string/string/substr/中,我们看到方法定义为:

字符串substr(size_t pos = 0,size_t len = npos)const;

请注意,方法定义中的第二个参数的长度为'len'。


子字符串中包含的字符数(如果字符串较短,则使用尽可能多的字符)。

testString有10个字符,索引位置为0到9。将endIndex指定为10总是应抛出IndexOutOfBoundsException(),因为testString的endIndex不为10。

如果我们使用查看C ++方法的具体值在JUnit中测试该方法,则我们期望:

字符串testString =" testString";
assertThat(testString.substring(4,6),equalTo(" String")));

但是我们当然得到了期望:" String",但是是" St"

testString从索引0到'String'中char'g'的长度为10个字符。
如果我们使用10作为" endIndex"参数,

字符串testString =" testString";
assertThat(testString.substring(4,10),equalTo(" String")));

来自JUnit的"通过"。

如果我们将参数2重命名为" lengthOfSubstringFromIndex0",则不必执行endIndex-1计数,并且它永远不会抛出在指定endIndex 10(超出基础数组范围)时期望的IndexOutOfBoundsException()。 http://docs.oracle.com/javase/7/docs/api/java/lang/IndexOutOfBoundsException.html

这只是您必须记住此方法的特质的一次。第二个参数的名称不正确。 Java方法签名应为:

1
2
public String substring(int beginIndex,
           int lengthOfSubstringFromIndex0)

或者重新定义该方法以匹配C ++ string :: substr方法。当然,重新定义将意味着重写整个Internet,因此不太可能。


从String API javadoc:

1
2
3
4
5
6
7
8
9
10
public String substring(int beginIndex)
    Returns a new string that is a substring of this
    string. The substring begins with the"" character
    at the specified index and extends to the end of this string.

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.

例子:

1
2
3
4
5
6
"unhappy".substring(2) returns"happy"
"Harbison".substring(3) returns"bison"
"emptiness".substring(9) returns"" (an empty string)

"hamburger".substring(4, 8) returns"urge"
"smiles".substring(1, 5) returns"mile"

参数:

1
2
3
4
5
6
beginIndex - the beginning index, inclusive.
Returns:
the specified substring.
Throws:
IndexOutOfBoundsException - if beginIndex is negative or
larger than the length of this String object.

====

因此,这是设计使然。如果将索引作为字符串的大小,则它将返回空字符串。


这是因为子字符串函数返回一个"包含"子字符串。因此,索引5指向字符串末尾之前但字符串最后显示字符之后的位置。

这在文档中显示:
http://download.oracle.com/docs/cd/E17476_01/javase/1.4.2/docs/api/java/lang/String.html#substring(int)


substring(5)指向一个现有索引...它恰好指向一个空字符串。另一方面,substring(6)只是个疯狂的话题。 :)