Душевная пытка: как работает Java substring()?

Java
Душевная пытка: как работает Java substring()?

Просматривая Programcreek, я наткнулся на несколько небольших, но мощных тем. Например: Javasubstring()Как работает метод? Подобные душераздирающие темы заслуживают более пристального внимания.

Кроме того, что я хочу вам сказать, так это то, что процесс исследования очень интересен.Это похоже на поиск сокровища в лабиринте.Сначала я был немного ошеломлен, но после некоторых тщательных исследований я не только найду сокровище , но также будет своего рода Чувство открытия велико.

Для подавляющего большинства младших программистов или ветеранов, которые не обращают внимания на «внутренние навыки», они часто остаются на уровне «знания правды и незнания почему» — они могут использовать это, но говорить о базовых принципах , они могут только я могу почесать голову и растопырить вопросительное лицо руками.

Длительное время,яВсегда был на этом уровне. Но я решил его изменить, потому что «внутренняя прочность» подобна закладке фундамента: только хорошо заложив фундамент, можно построить высотное здание, способное выдержать испытания. Я пользуюсь этой возможностью, чтобы присоединиться к вам и подробно рассмотреть, как работает функция substring() в Java. Внимание, готовы сражаться с монстрами для обновления!

01. Что делает substring()

subдаsubtractаббревиатура, такsubstringБуквально означает «вычесть строку». Такой анализ, кажется ли, что название метода весьма специфично?

substring()Полное написаниеsubstring(int beginIndex, int endIndex). Этот метод возвращает новую строку между начальным нижним индексом исходной строкиbeginIndexи конечный индексendIndex-1между.

String cmower = "沉默王二,一枚有趣的程序员";
cmower = cmower.substring(0, 4);
System.out.println(cmower);

Вывод программы:

沉默王二

Зачем? Позвольте мне кратко объяснить.

Нижние индексы Java нумеруются с 0 (я не уверен, есть ли язык программирования, начинающийся с 1), что отличается от привычки нумерации с 1 в нашей повседневной жизни. Java делает это по следующим причинам:

Java основана на языке C, а индексы языка C начинаются с 0 — это звучит как бред. Настоящая причина в том, что нижний индекс не является нижним индексом, в языке указателей (C) это на самом деле смещение, смещение от начальной позиции. Первый элемент находится в начале, поэтому его смещение равно 0.

Кроме того, есть еще один способ сказать это. В первые дни компьютерные ресурсы были относительно скудны, и 0 в качестве начального индекса было более эффективным для компиляции, чем 1 в качестве начального индекса.

Узнав причину и снова взглянув на приведенный выше код, она вдруг станет ясной. Для строки символов «Silent King Er, интересный программист» нижний индекс «Shen» равен 0, нижний индекс «Mo» равен 1, нижний индекс «Wang» равен 2, а нижний индекс «Er» равен 2. , Нижний индекс равен 3, поэтомуcmower.substring(0, 4)Возвращаемая строка — «Silent King II», включая начальный индекс, но не конечный индекс.

02. Что именно происходит при вызове substring()?

Перед этим мы узнали: [Строки неизменяемы](), поэтому при вызовеsubstring()метод возвращает фактически новую строку. Тогда адресная ссылка переменной cmower изменится, как показано на рисунке ниже.

Чтобы убедиться в том, что приведенная выше картина полностью верна, давайте взглянем на JDK 7.substring()исходный код.

public String(char value[], int offset, int count) {
    //check boundary
    this.value = Arrays.copyOfRange(value, offset, offset + count);
}

public String substring(int beginIndex, int endIndex) {
    //check boundary
    int subLen = endIndex - beginIndex;
    return new String(value, beginIndex, subLen);
}

Видно, что,substring()пройти черезnew String()Возвращается новый строковый объект, который передается при создании нового объектаArrays.copyOfRange()Копируется новый массив символов.

Но JDK 6 отличается. Говоря о JDK 6, некоторые читатели могут выразить недовольство, JDK 6? Какая сейчас эпоха, JDK 13 вышел, хорошо? Но я хочу вам сказать, что сравнение и анализ исходного кода JDK очень полезны для обучения.

Не правда ли, есть поговорка, если вы хотите узнать успешного человека, вы должны сосредоточиться не только на том, что произошло после того, как он разбогател, но и на том, что он делал до этого.

Просто следуйте за мной и посмотрите, что есть в JDK 6substring()исходный код.

//JDK 6
String(int offset, int count, char value[]) {
    this.value = value;
    this.offset = offset;
    this.count = count;
}

public String substring(int beginIndex, int endIndex) {
    //check boundary
    return  new String(offset + beginIndex, endIndex - beginIndex, value);
}

substring()Сам метод не сильно отличается от JDK 7, и оба проходятnew String()Возвращается новый строковый объект. ноString()Этот конструктор очень отличается, JDK 6 просто изменил два свойства (offsetа такжеcount), значение не изменилось.

PS:valueэто массив, который фактически хранит символы,offsetэто индекс первого элемента в массиве,countэто количество символов в массиве.

Что это значит?

перечислитьsubstring()Хотя создается новая строка, значение строки по-прежнему указывает на тот же массив в памяти, как показано на следующем рисунке.

03. Почему изменился конструктор JDK 7?

Посмотрев исходный код JDK 6 и JDK 7, у вас может возникнуть такое сомнение: зачем менять JDK 7? Нехорошо всем использовать один и тот же массив строк, чтобы не занимать новое место в памяти. Фактически?

Если есть очень длинная строка, которая может обернуться вокруг земли, когда нам нужно вызватьsubstring()При перехвате небольшого сегмента строки могут возникнуть проблемы с производительностью. Поскольку эта небольшая строка относится ко всему массиву длинных и очень длинных символов, массив очень длинных и длинных символов не может быть переработан, а память всегда занята, что может привести к утечке памяти.

PS: Утечка памяти — это отказ программы освободить память, которая больше не используется из-за небрежности или ошибки.

Как бороться с этой скрытой опасностью до появления JDK 7? Ответ таков.

cmower = cmower.substring(0, 4) + "";

Почему, почему, почему еще один "+""" решает утечку памяти? Некоторые читатели могут не поверить в это, поэтому позвольте мне взять вас на анализ.

Во-первых, мы проходимJADДекомпилируйте байт-код, и приведенная выше строка кода станет следующей.

 cmower = (new StringBuilder(String.valueOf(cmower.substring(0, 4)))).toString();

Оператор "+" эквивалентен синтаксическому сахару. После добавления пустой строки она будет преобразована JDK в объект StringBuilder, который сгенерирует новый массив символов при обработке строки, поэтомуcmower = cmower.substring(0, 4) + "";После выполнения этой строки кода cmower указывает на иsubstring()Другой массив символов, чем до вызова.

PS: Если вы не понимаете, как работает оператор "+", обратитесь к моей предыдущей статье "Позор, в сращивании строк Java так много поз.", я не буду повторяться здесь, чтобы не быть обыгранным старыми читателями.

04. Наконец

Подводя итог, JDK 7 и JDK 6substring()Сам метод не сильно изменился, но конструктор класса String сильно отличается: JDK 7 будет повторно копировать массив символов, а JDK 6 — нет, поэтому JDK 6 выполняет более длинные строки.substring()может вызвать утечку памяти.


Ну что же, читатели и друзья, вышеизложенное и есть все содержание этой статьи.Я вижу, что все лучшие программисты здесь, второй брат должен дать вам большой палец вверх👍. Если вам это не нравится и вы хотите увидеть больше, я порекомендую вам еще несколько статей.

Душевная пытка: создайте строку Java, используйте "" или конструктор

Пытка души: почему строки Java неизменяемы?

Пытка души: как проверить, содержит ли массив Java определенное значение?

Если вы нашли эту статью полезной,Просите лайки, комментарии и внимание, давайте развивать хорошую привычку вместе! Кроме того, я составил список книг, которые должны прочитать ведущие Java-программисты, и вы можете получить его бесплатно, ответив на ключевое слово «Java».