Java 为什么substring()方法是substring(开始索引(包含),结束索引(独占))

Java 为什么substring()方法是substring(开始索引(包含),结束索引(独占)),java,string,Java,String,为什么子字符串的起始参数作为索引,第二个参数作为起始长度 换句话说 1 2 3 | 4 5 <=== Length from beginning A B C D E 0 | 1 2 3 4 <=== Index 1 2 3 | 4 5编写代码时的拇指法则是,从消费者处获取最大数量或输入。获得所需的输出变得更加容易 源代码就是答案。它们都是开始索引和结束索引 public String substring(int beginIndex

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

换句话说

1   2   3 | 4   5 <=== Length from beginning

A   B   C   D   E

0 | 1   2   3   4 <=== Index

1 2 3 | 4 5编写代码时的拇指法则是,从消费者处获取最大数量或输入。获得所需的输出变得更加容易

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

   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    }
公共字符串子字符串(int-beginIndex,int-endIndex){
1942 if(beginIndex<0){
1943年抛出新StringIndexOutOfBoundsException(beginIndex);
1944        }
1945如果(结束索引>计数){
1946抛出新的StringIndexOutOfBoundsException(endIndex);
1947        }
1948 if(beginIndex>endIndex){
1949抛出新的StringIndexOutOfBoundsException(endIndex-beginIndex);
1950        }
1951返回((beginIndex==0)和(&(endIndex==count))?这是:
1952新字符串(偏移量+开始索引,结束索引-开始索引,值);
1953    }

简单地说,它只是提到从何处到何处要将其子串。

它是一个开始和结束索引

对我来说,这似乎非常合乎逻辑,但是如果你愿意,你可以用一个非常简单的计算从起点和长度的角度来考虑:

"ABCDEFGH".substring(start, start + length);
它允许你有这种灵活性。

关于“为什么”的问题可能被认为是哲学或学术性的,并引发了“这就是它的本来面目”的回答

然而,从更一般、抽象的角度来看,在考虑备选方案时,这是一个有效的问题:可以想象这种方法的两种形式:

String substringByIndices(int startIndex, int endIndex);

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

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

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

选择指数是包含性还是排他性将增加一些可能性,使人们不得不到处摆弄
+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 (int i=startIndex; i<endIndex; i++) { ... }
返回此列表中指定的fromIndex(包含)和toIndex(独占)之间部分的视图

这是符合这一惯例的。如果您必须混合一些API,其中结束索引有时是包含的,有时是独占的,那么这很容易出错。

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

如果您看看这两个数字如何与代码一起工作,通过将字符从一个数组复制到另一个数组来创建子字符串,那么原因就显而易见了

鉴于:

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];
char[]substring=new char[end-start];
for(int i=start;i
请注意,没有通过加/减1进行调整-这些数字正是循环所需要的。循环实际上也可以在没有减法的情况下进行编码:

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

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

我相信这是一个开始索引和结束索引?我问为什么。。。当被问到这个问题时。。。我不想只告诉任何人“这就是它的工作方式。”“使结束索引成为独占的有什么好处?”这让我们可以以一种方式使用子字符串
“ABCDE”。子字符串(开始,开始+长度)。看看您的示例:要获得
BC
,您可以尝试使用
intstart=1这样的变量;int length=2
,通过使用上述公式,它将与
“ABCDE”相同。子字符串(1,3)。我认为在两个索引之间选择是愚蠢的。更直观的是有一个开始索引(包括)和一个长度(不是结束索引)。给数学家带来痛苦背后有一个理由吗?如果按索引进行,为什么前面的索引是包含的,而末尾的索引是独占的?我认为这只是一个正常的CS惯例。就像它是一个for循环
i=0;i@shinjw虽然这个选择在很大程度上是任意的,但它使得将字符串拆分为多个部分更加简单:
intstartidx=0,midIdx=5,endIdx=10;String first=foo.substring(startIdx,midIdx);字符串秒=foo.substring(midIdx,endIdx)
@shinjw如果开始索引是包含的,而结束索引是独占的,那么您可以查看新字符串的长度。endindex减去startindex。@StuPointerException对于每一个选择,我们都能想象出比其他选择更直观的场景。我们觉得这更直观,因为我们学会了这样思考。到目前为止,与for循环的一致性是我见过的最好的解释
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];
for (int i = start, j = 0; i < end; i++)
    substring[j++] = string[i];