Почему из индекса включен, но конечный индекс является исключительным?
В методах API Java, например:
-
String.substring(int beginIndex, int endIndex)
-
String.subSequence(int beginIndex, int endIndex)
-
List.subList(int fromIndex, int toIndex)
Почему начальный индекс включен, но эксклюзивный индекс? Почему они не должны были разрабатываться как включительно?
Ответы
Ответ 1
Потому что:
- Java основана на C, и C делает это так.
- Это делает очиститель кода: если вы хотите захватить до конца объекта, пройдите
object.length
(однако объект реализует это, например, size() и т.д.) в параметр toIndex - нет необходимости добавлять/вычитать 1
Например:
String lastThree = str.substring(str.length() - 3, str.length());
Таким образом, очень очевидно, что происходит в коде (хорошая вещь).
EDIT Пример функции C, которая ведет себя так: strncat
из string.h
:
char *strncat(char *dest, const char *src, size_t n);
Значение параметра size_t
соответствует параметру java endPosition
в том, что они оба являются длиной объекта, но считая от 0, если они являются индексом, это будет один байт за пределами объекта.
Ответ 2
Проблема заключается в том, что они не использовали синтаксис C/С++.
Я думаю, что синтаксис C более читабельен. Например, если вы хотите взять подстроку с длиной 2, начиная с третьего элемента списка X, вы пишете
X.subList(2,3).
В Java, если вы хотите сделать подсписку ТОЛЬКО третьим элементом списка X, вам нужно написать X.subList(2,3).
Это действительно уродливо, кажется, что вы берете подсписку из двух элементов. С другой стороны, X.subList(2,2) - пустой список - довольно запутанный.
int startIndex = calculateStartIndex();
int endIndex = calculateEndIndex();
X.subList(startIndex, endIndex);
Собственно, если (startIndex == endIndex) → пустой список.
Ваш вопрос: почему, так вот мой ответ.
Что произойдет, если оба параметра в Java будут включены? у вас возникнет проблема, когда вам нужно взять пустой список, если вы вычислите второй индекс. Потому что в большинстве случаев индексы вычисляются и мы не записываем непосредственно числа в функции.
Для того, чтобы иметь пустой список, если вы не используете собрание (например: в случае отрицательного или нижнего endIndex возвращают пустой список), - но такая конвенция может скрыть ошибки в кодировании (нет разницы, если второй параметр - 1 или -100!):
int startIndex = calculateStartIndex(); // return 0
int endIndex = calculateEndIndex(); // return 0
X.subList(startIndex, endIndex); // this would return the 1st element of the list; how to get an empty list? Convenction needed!
// use a negative number to get empty list? And what if endIndex is negative because of a bug?
В этом случае C побеждает; более читабельными в любом случае, жаль, что они изменили это. Но это всего лишь дополнительный комментарий, который вы можете использовать.
Ответ 3
Он называется соглашением Dijkstra диапазонов [i, j>
(или в другой математической нотации [i, j)
). Таким образом вы можете иметь дело с диапазонами [a, b>, [b, c>, [c, d]
.
Утверждалось, что он несколько эффективнее, и на самом деле мы его программисты используют в:
for (int i = 0; i < n; ++i) { ... }
В таких диапазонах число элементов j - i
, поэтому да сохраняется -1/+ 1.
Он также используется в BitSet.set(from, to)
Ответ 4
В дополнение к тому, что сказал Богем:
Если это было эксклюзивно, чтобы получить первые 4
символы в String, вам нужно:
String firstFour = String.substring(0, 3);
Это довольно уродливо.