为什么是substring()方法的子字符串(起始索引(包含),结束索引(排除))



子字符串将起始参数作为索引,将第二个参数作为起始长度的原因是什么?

换句话说,

1   2   3 | 4   5 <=== Length from beginning
A   B   C   D   E
0 | 1   2   3   4 <=== Index

如果我想让substring()返回BC,我必须执行"ABCDE".substring(1,3);

为什么会出现这种情况?

编辑:使最终索引具有排他性的好处是什么?

关于"为什么"可以被认为是哲学的或学术的,并引发沿着";事情就是这样;。

然而,从更普遍、抽象的角度来看,当考虑替代方案时,这是一个有效的问题:可以想象这种方法的两种形式:

String substringByIndices(int startIndex, int endIndex);

String substringByLength(int startIndex, int length);

在这两种情况下,设计空间中都有另一个维度,即索引是包含还是排除

首先,请注意,所有版本基本上都是等效的。在调用站点,根据方法的实际语义更改调用通常很简单:

int startIndex = ...;
int endIndex = ...;
String s = string.substringByLength(startIndex, endIndex-startIndex);

int startIndex = ...;
int length = ...;
String s = string.substringByIndices(startIndex, startIndex+length);

索引是包含索引还是排除索引的选择会增加一些不得不到处摆弄+1-1的可能性,但这在这里并不重要。

第二个例子已经说明了为什么选择使用包含的开始索引和排除结束索引可能是个好主意:可以很容易地切出某个长度的子字符串,而不必考虑任何+1-1:

int startIndex = 12;
int length = 34;
String s = string.substringByIndices(startIndex, startIndex+length);
// One would expect this to yield "true". If the end index
// was inclusive, this would not be the case...
System.out.println(s.length() == length); 

这在某种程度上也可以被认为与for-循环一样,在那里你通常有

for (int i=startIndex; i<endIndex; i++) { ... }

开始是包含,结束是exclusive。因此,这种选择与通常的惯用语言模式非常匹配。


然而,无论做出哪种选择,也无论如何证明:成为很重要

一致的

整个API。

例如,List接口包含方法subList(int,int):

List<E> subList(int fromIndex, int toIndex)

返回此列表中介于指定的fromIndex(包含)和toIndex(排除)之间的部分的视图。

与符合这一惯例。如果必须混合API,其中结束索引有时是包容性的,有时是排他性的,这将是容易出错的。

它是一个开始和结束索引。

对我来说,这似乎很合乎逻辑,但如果你喜欢,你可以用一个非常简单的计算从开始和长度的角度来考虑:

"ABCDEFGH".substring(start, start + length);

它允许您具有这种灵活性。

与其说是"从开始的长度",不如说是"结束索引独占"。

如果你看看这两个数字如何与代码一起工作,通过将字符从一个数组复制到另一个数组来创建子字符串,原因就很明显了。

给定:

int start; // inclusive
int end; // exclusive
char[] string;

现在看看在复制数组元素时使用这些数字有多容易:

char[] substring = new char[end - start];
for (int i = start; i < end; i++)
    substring[i - start] = string[i];

请注意,没有通过加/减1进行调整——这些数字正是循环所需要的。实际上,循环也可以在没有减法的情况下进行编码:

for (int i = start, j = 0; i < end; i++)
    substring[j++] = string[i];

选择这些数字是"机器友好的",这是C语言设计时的方式,Java是基于C的。

编写代码时的拇指规则是,从使用者处获取最大数量或输入。获得所需输出变得更加容易。

源代码就是答案。它们都是起始索引和结束索引。

   public String substring(int beginIndex, int endIndex) {
1942        if (beginIndex < 0) {
1943            throw new StringIndexOutOfBoundsException(beginIndex);
1944        }
1945        if (endIndex > count) {
1946            throw new StringIndexOutOfBoundsException(endIndex);
1947        }
1948        if (beginIndex > endIndex) {
1949            throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
1950        }
1951        return ((beginIndex == 0) && (endIndex == count)) ? this :
1952            new String(offset + beginIndex, endIndex - beginIndex, value);
1953    }

简单地说,它只是提到从哪里到哪里你想把它子字符串。

相关内容

最新更新