【问题标题】:Why is substring() method substring(start index(inclusive), end index (exclusive)) [closed]为什么 substring() 方法是 substring(start index(inclusive),end index(exclusive)) [关闭]
【发布时间】:2014-12-25 04:48:26
【问题描述】:

为什么子字符串以起始参数为索引,第二个参数为从头开始的长度?

换句话说

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);

为什么会这样?

编辑:使结束索引独占有什么好处?

【问题讨论】:

  • 我相信它是一个开始索引和一个结束索引?
  • 我在问为什么......当被问到这个问题时......我不想告诉任何人“这就是它的完成方式。”
  • 使结束索引独占有什么好处?”这让我们以"ABCDE".substring(start,start+length); 的方式使用子字符串。看看你的例子:要获得 BC,你可以尝试使用像 int start = 1; int length = 2 这样的变量,通过使用提到的公式将与 "ABCDE".substring(1,3); 相同。
  • 我认为在两个索引之间进行选择无论如何都是愚蠢的。拥有 start 索引(包括)和 length(不是结束索引)更加直观。
  • 给数学家带来痛苦

标签: java string


【解决方案1】:

关于“为什么”的问题可以被视为哲学或学术问题,并引发“就是这样”的答案。

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

String substringByIndices(int startIndex, int endIndex);

String substringByLength(int startIndex, int length);

在这两种情况下,设计空间中还有另一个维度,即索引是inclusive还是exclusive

首先,请注意所有版本基本相同。在调用现场,根据方法的实际语义更改调用​​通常很简单:

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

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

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

第二个例子已经说明了为什么选择使用 inclusive 起始索引和 exclusive 结束索引可能是个好主意:很容易切出一定长度,无需考虑任何+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-loops 之类的东西一致,你通常有

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

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


但是,无论做出何种选择,无论其理由如何:重要的是要做到

一致

贯穿整个 API。

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

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

返回此列表在指定 fromIndex(包括)和 toIndex(不包括)之间部分的视图。

with 符合这个约定。如果您必须混合 API,其中结束索引有时包含有时排他,这将很容易出错。

【讨论】:

  • 与for循环一致,是我目前看到的最好的解释
【解决方案2】:

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

在我看来,这似乎很合乎逻辑,但是如果您愿意,可以使用非常简单的计算从开始和长度的角度来考虑它:

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

它为您提供了这种灵活性。

【讨论】:

    【解决方案3】:

    与其说是“从开始的长度”,而是“不包括结束索引”。

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

    给定:

    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 的。

    【讨论】:

      【解决方案4】:

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

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

         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    }
      

      简单来说,就是从哪里到哪里要子串。

      【讨论】:

      • 这背后有什么理由吗?如果按索引来做,为什么前面的索引是包容的,而结束的索引是排斥的?
      • 我认为这只是一个普通的 CS 惯例。就好像它是一个 for 循环 i = 0; i&lt;n; i++ n 是独占的
      • @shinjw 虽然选择在很大程度上是任意的,但它使得将字符串拆分为多个部分更加简单:int startIdx = 0, midIdx = 5, endIdx=10; String first = foo.substring( startIdx, midIdx ); String second = foo.substring( midIdx, endIdx );
      • @shinjw 如果起始索引是包含而结束索引是排除的,你可以看看新字符串的长度是多少。 endindex 减去 startindex。
      • @StuPointerException 对于每一种选择,我们都可以想象它们看起来比其他选择更直观的场景。我们觉得这更直观,因为我们学会了这样思考。
      猜你喜欢
      • 1970-01-01
      • 2016-07-29
      • 2013-10-13
      • 1970-01-01
      • 2016-07-12
      • 2013-08-15
      • 1970-01-01
      • 1970-01-01
      • 2017-06-24
      相关资源
      最近更新 更多