Рубрики
Без рубрики

Ява: ¿ Почему Нет работает ли сравнение строк для меня? Литералы, классы и репозиторий строк

Обычный вопрос от тех, кто начинает с Java (и даже на собеседованиях на должности… С пометкой java, испанский.

Обычный вопрос от тех, кто начинает с Java//(и даже в собеседованиях на должности, использующие этот язык) – это сравнение между текстовыми строками. Если вы пришли из других языков, вы можете привыкнуть сравнивать строки с оператором равенства |/==//или просто это может показаться вам самым очевидным и очевидным способом сделать это.

Однако давайте рассмотрим такой код:

String s1 = new String("campusMVP");
String s2 = new String("campusMVP");
System.out.println(s1 == s2); //Devuelve false

Хотя строки идентичны, и вы получаете два идентичных объекта//String/|, сравнение возвращает false |/🤔

В этом другом подобном примере:

String s1 = "campusMVP";
String s2 = new String("campusMVP");
System.out.println(s1 == s2); //Devuelve false

возвращает//false//тоже.

Однако это:

String s1 = "campusMVP";
String s2 = "campusMVP";
System.out.println(s1 == s2); //Devuelve true

возвращает true .

Более того, более распространенным случаем было бы сравнение результата функции, возвращающей строку, с другой любой строкой. Примерно:

String s1 = "OK";
if (s1 == FuncionQueDevuelveCadena()) {
    System.out.println("¡Coinciden!");
}
else {
    System.out.println("No coinciden"));
}

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

Что это из-за этого?

Работа строк в Java-хранилище или пул строк

Строки в Java-это отдельный вид. У них есть много деталей, которые необходимо знать, чтобы понять их (например | что//неизменны|/, или Окончательный в Java, или которые внутренне могут быть//закодированы в памяти различными способами|/). Знание их хорошо-незаменимый шаг к овладению языком.

Простая концепция, как только вы это понимаете, но не все ясно это//способ их создания//в зависимости от того, как они объявлены.

В основном у нас есть два способа объявить строку: с литералом или созданием экземпляра класса:

String literal = "campusMVP";
String clase = new String("campusMVP");

В обоих случаях мы получаем одно и то же: класс String со строкой внутри (которая представляет собой не что иное, как массив символов в памяти). Тем не менее, есть большая разница между тем, чтобы сделать это в той или иной форме.

Когда строка объявляется способом//литерал |/в первый раз, JVM//помещает ее в специальное пространство с именем//репозиторий строк//(//string pool |/на английском языке). Этот репозиторий содержит одну//копию//различных строк, объявленных как литералы в коде. Здесь важным словом является “уникальный”. Поскольку строки неизменяемы, если вы объявляете более одного раза одну и ту же строку, не имеет смысла иметь ее несколько раз в памяти, поэтому в следующий раз, когда вы объявляете ее то, что делает JVM, – это перейти в репозиторий строк и найти его,//возвращая ссылку на тот же объект String//что в первом заявлении. ГЛАЗ: это тот же объект, а не копия. ГЛАЗ: это тот же объект, а не копия.

String s1 = "campusMVP";
String s2 = "campusMVP";
String s3 = new String(s1);
System.out.println(s1 == s2); //true
System.out.println(s1 == "campusMVP"); //true

Во всех случаях сравнение проводится успешно. Во всех случаях сравнение проводится успешно. во всех случаях используется один и тот же объект String точно . В строке 1, в первом заявлении, он хранится в репозитории строк. В строке 2 второе объявление литерала, JVM идет в репозиторий и посмотрите, существует ли та же строка, что и раньше. Поскольку в этом случае он существует, он возвращает ссылку на ту же строку, что и в строке 1. Вот почему они одинаковы! Потому что они не только представляют одни и те же символы, но и фактически являются одним и тем же классом (и указывают на один и тот же массив в памяти). В строке 4, когда сравнение выполняется в |/s1 и тот же литерал, то же самое происходит снова: JVM находит литерал в репозиторий строк и возвращает ту же ссылку, поэтому это true снова.

Вы можете увидеть визуально лучше на этом рисунке, который пытается изобразить ситуацию:

Это работает даже с комбинацией нескольких литералов. JVM достаточно умен, чтобы сделать эти две строки одинаковыми:

String s1 = "campusMVP";
String s2 = "campus" + "MVP";
System.out.println(s1 == s2); //true

Хотя это может показаться, что есть 3 строки в репозитории строк, и это будет возвращать два разных объекта.

Объявление строк как классов

Однако, когда мы объявляем строку, используя класс (с new String () ), операция отличается. Что вы делаете, это выделить в памяти пространство, необходимое для строки, и возвращает класс, который позволяет обрабатывать его. Он не проходит через репозиторий строк для чего-либо.

Вот почему этот фрагмент:

String s1 = new String("campusMVP");
String s2 = new String("campusMVP");
System.out.println(s1 == s2); //Devuelve false

возвращает//false//в сравнении. Это две разные строки , хотя внутренне они представляют одни и те же символы:

Примечание : я знаю, абсурдно объявлять строки таким образом, обертывая литерал классом/| String . И на самом деле, вы редко увидите, что это делается там. Но есть множество способов получить классы//String//в переменную: от преобразования литерала до извлечения их из базы данных. Я просто приведу пример таким образом, чтобы понять, что я хочу объяснить, а не указывать, что это рекомендуемый способ объявления строк.

¿ Что делать, если у нас есть две литеральные строки, и мы хотим сравнить их с |/== без учета регистра?

Мы могли бы сделать это:

String s1 = "campusMVP";
String s2 = "campusMVP";
System.out.println(s1.toLowerCase() == s2.toLowerCase()); //false!

В этом случае сравнение возвращает//false|/, поскольку мы получаем две одинаковые строки, но представлены двумя разными объектами. Оператор = =//сравнивает ссылки на объекты//и в этом случае, хотя данные, которые они содержат, одинаковы, это не один и тот же объект. Даже будучи s1 и s2 тот же объект//String |/| операция toLowerCase() то, что он делает, это генерировать новую строку из той же исходной строки, но это два разных объекта String в памяти. Это как в предыдущем случае. Не будучи литералами, они не извлекаются из строкового репозитория, хотя уже они там существуют.

Вот почему так опасно сравнивать строки с|/==//. Хотя строки идентичны, как то, что сравнивается, – это ссылки на объекты Строка//и не базовое значение, они возвращают только//true//для равных строк при обращении к одному и тому же литералу.

Вывод : никогда не используйте оператор = = |/для сравнения строк, если вы хотите преуспеть в Java.

И тогда как мне сравнить строки с безопасностью?

Ну |/с помощью метода//equals() у вас есть все объекты. Фактически, он унаследован от//Object/|, “корневого” базового класса всех существующих. Этот метод делает сравнение внутреннего состояния двух объектов, чтобы увидеть, если они равны, в то время как |/== то, что он делает, это сравнить ссылки.

В случае строк, что он делает, это сравнить, что два массивов символов, содержащих объекты//String//мы сравниваем равны, что действительно то, что нас интересует, как правило.

Таким образом, чтобы безопасно сравнить две строки, вы должны использовать этот метод:

String s1 = new String("campusMVP");
String s2 = new String("campusMVP");
System.out.println(s1.equals(s2)); //Devuelve true
System.out.println(s1.equals("campusMVP")); //Devuelve true
System.out.println("campusMVP".equals(s1)); //Devuelve true

Но это то, что |/не имеет значения, как мы получим конечную строку//: всегда возвращает//true//если строки, представляющие два объекта, равны, например:

String s1 = new String("campusMVP");
String s2 = new String("CAMPUSMVP");
System.out.println(s1.equals("campus" + "MVP")); //Devuelve true
System.out.println(s1.toLowerCase().equals(
            s2.toLowerCase()
        )); //Devuelve true

В этом случае мы можем сравнить две строчные строки, чтобы увидеть, если они одинаковы.

Вывод 2|/: сравнение строк в Java всегда делайте их с помощью//equals ()//.

” Интернализация ” объектов строкового типа

Это уже почти любопытство больше, чем что-либо еще. Но это то, что, хотя это не то, что используется очень часто, есть возможность сделать объекты типа String которые у нас есть, “интернализуются”, чтобы они стали частью pool//текстовых строк, как если бы мы объявили их литералами.

Это достигается благодаря методу intern ()//, который имеет строки. Этот метод делает то же самое, что и при объявлении строкового литерала: он переходит в строковый репозиторий и смотрит, существует ли там та же строка (если нет, создает ее), и возвращает ссылку на ранее существовавшую строку вместо создания новой.

Если мы вызываем//intern ()//, например, это:

String s1 = new String("campusMVP");
String s2 = new String("campusMVP");
System.out.println(s1.intern() == s2.intern()); //Devuelve true

Он вернет//true//поскольку//intern () возвращает в результате вызова ту же ссылку на ту же строку из pool//строк.

То есть, intern() что он делает, так это отделяет объект от исходных данных и назначает ему те же данные, что и в репозитории строк. Визуально это было бы примерно так (обратите внимание на строку s3 ):

Для чего это стоит |/intern () ? Ну, как я уже говорил, вы действительно редко будете использовать его, но это может помочь вам сэкономить много памяти в тех случаях, когда вы собираетесь генерировать много строк и большое количество которых будет одинаковым.

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

Как//дополнительное техническое примечание//об этом сказать, что до Java 7, уже много лет назад (он был выпущен в 2011 году), доступное пространство для хранения строк в pool было ограничено и, кроме того, не входило в//сборку мусора . Это означало , что если вы злоупотребляете “interning”, вы можете получить исключение типа OutOfMemory , что может привести к катастрофе в вашем приложении. Вот почему во многих статьях, которые вы найдете по этой теме, и которые устарели, они рекомендуют вам не использовать//intern() . Реальность такова, что, начиная с Java 7, и таким образом, практически все приложения Java, который вы найдете там |//репозиторий строк сохраняется уже в "куча" (или//куча|/) (т. е. в динамической памяти), поэтому нет проблем с границами и, кроме того, может быть восстановлен сборщиком мусора. Кроме того, в Java 13 (сентябрь 2019) также разрешено//возврат неиспользуемой динамической памяти в операционную систему|/, что делает ее еще более оптимизированной.

Короче

Текстовые строки являются “странными” почти на всех языках, но в Java особенно, поскольку они могут действовать как литералы и как объекты, но в то же время они имеют различия при использовании и особенно при сравнении.

Поэтому при сравнении строк следует избегать использования оператора//== |/(который сравнивает ссылки на объекты, а не их данные) и вместо этого использует метод equals () . Кроме того, важно знать внутреннюю работу строк, их последствия и то, как мы можем оптимизировать использование памяти при обработке больших (или многих) строк с одинаковыми значениями.

Вы оставили/| небольшая игровая площадка//таким образом, вы можете проверить работу все объяснил и попробовать внести изменения и посмотреть, как они работают.

Надеюсь, вы найдете это полезным!

Оригинал: “https://dev.to/campusmvp/java-por-que-no-me-funciona-la-comparacion-de-cadenas-literales-clases-y-el-repositorio-de-cadenas-57o4”