为什么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);
为什么会这样?
编辑:使结束索引独占有什么好处?
答案 0 :(得分:8)
关于“为什么”的问题可以被认为是哲学的或学术的,并且沿着“那就是它的方式”的方式挑起答案。
然而,从更一般的抽象观点来看,在考虑替代方案时,这是一个有效的问题:人们可以想象这种方法的两种形式:
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++) { ... }
开头是包含,结尾是独占。因此,这种选择很好地与通常的惯用语言模式相匹配。
然而,无论做出哪种选择,无论其如何合理:重要的是
贯穿整个API。
例如,List
界面包含方法subList(int, int):
List<E> subList(int fromIndex, int toIndex)
返回指定fromIndex(包含)和toIndex(独占)之间此列表部分的视图。
与此约定一致。如果你必须混合API,其中结束索引有时是包容性的,有时是独占的,这将是容易出错的。
答案 1 :(得分:4)
它是一个开始和结束索引。
对我而言,这似乎很合乎逻辑,但如果您愿意,可以使用一个非常简单的计算来考虑它的开始和长度:
"ABCDEFGH".substring(start, start + length);
它为您提供了这种灵活性。
答案 2 :(得分:1)
这不是“从一开始的长度”,而是“结束索引专属”。
如果您通过将字符从一个数组复制到另一个数组来查看这两个数字如何使用代码创建子字符串,原因很明显。
假设:
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的。
答案 3 :(得分:0)
编写代码时的Thumb规则是,从消费者那里获取最大数量或输入。获得所需的输出变得更加容易。
源代码就是答案。它们都是开始和结束索引。
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 }
简单来说,只是提到你想要将其分组的地方。