Обычный вопрос от тех, кто начинает с 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”