| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Java: Общие вопросы > Быстрый способ сравнить строки |
| Автор: DenWPF 21.9.2010, 14:27 |
| Вот как быстрее всего сравнить две строки? equals - он как сравнивает по символьно? |
| Автор: Joil 21.9.2010, 15:03 | ||
|
| Автор: DenWPF 21.9.2010, 15:15 |
| по символьный, значит быстрей уже не как. ну гуд. спасибо. |
| Автор: Joil 21.9.2010, 18:27 |
Ну почему, если строки очень большие то я думаю быстрее будет взять хэш от каждой строки и сравнить хэши. |
| Автор: DenWPF 21.9.2010, 18:42 |
| а как хэш генерируется? |
| Автор: jk1 21.9.2010, 19:39 | ||
|
| Автор: DenWPF 21.9.2010, 20:14 |
| ну я в плане механизме. |
| Автор: jk1 21.9.2010, 21:37 | ||||
Есть кстати еще хороший способ быстрого сравнения, заключается он в вызове метода intern() у всего, что хотите в будущем сравнивать и пользоваться тем, что он вернет. После этого строки можно сравнивать по ссылкам через оператор ==, а это куда быстрее посимвольного сравнения. http://habrahabr.ru/blogs/java/79913/. |
| Автор: Skipy 22.9.2010, 09:50 | ||
У неравных строк могут быть равные хеши, это не противоречит контракту. Обратное неверно. Так что НЕравенство выяснить можно по хеш-коду. Но если они равны - это ни о чем не говорит. То же верно и про хеш-функции (хотя попасть в 128 бит того же MD5 намного сложнее, чем в 32 бита hashCode). Кстати, подсчет хеша займет порядочно времени, имхо, сравнить быстрее. Можно, конечно, попользоваться методом intern. Но во-первых, это надо делать аккуратно, во-вторых, одно сравнение все-таки будет (когда в буфере строку будут искать), в-третьих - при интенсивном использовании рискуете получить OutOfMemory:PermGen - буфер сильно разрастется. |
| Автор: Skipy 22.9.2010, 12:52 | ||||||||
Потому что как только Вы начнете сравнивать строки через == - Вам ВЕЗДЕ надо будет использовать intern. Очень легко это забыть. Плюс есть строки, которые создаются не Вами, их тоже придется обрабатывать.
Какой ссылки? Вы себе представляете, как работает intern? Почитайте документацию: http://download.oracle.com/javase/6/docs/api/java/lang/String.html#intern()
as determined by the equals(Object) method означает, что для определения наличия в пуле вызывается equals. Более того, происходит ПОИСК, что означает как минимум несколько сравнений, даже при оптимизациях. Таким образом, на каждый вызов intern как минимум один раз equals вызовется. А скорее всего - больше одного раза. И с ростом размера пула возможен рост времени поиска.
А с чего бы ему разрастаться? В буфер попадают строковые литералы и результаты вызова intern. Если не пользоваться intern - там будут только строковые литералы. Ну и если кто-то другой кладет строки через intern - они тоже, но на это Вы никак не влияете. |
| Автор: danilka 22.9.2010, 13:55 | ||||||
Нет ну понятно что я везде не буду использовать такое сравнение...
Просто у меня был мысль что после того как у строки вызван метод intern() и соответствующий инстанс в пуле найден, почему бы в строке не сохранить ссылку на него...
Мне почему-то казалось что строковые литералы и результаты вызова intern() это одно и тоже. |
| Автор: Skipy 22.9.2010, 16:43 | ||||||||||||
Вот именно. Тогда в каждом месте, где Вы будете использовать ==, необходимо будет обеспечить наличие intern-ированной строки. Т.е. ОЧЕНЬ аккуратно отслеживать, где у Вас строки из пула, а где нет. Это сложно.
В какой строке? У Вас есть объект - String. Он содержит массив char-ов, длину строки и смещение в массиве. Всё. Никаких ссылок на intern-строки он не содержит и содержать не может. Потому как в общем случае в пуле аналогичной строки нет.
Вам казалось. Строковые литералы - это те строки, которые Вы в коде описываете как "строка". Каждый такой объект преобразуется в String и помещается в пул.
Вот этот тест вернет true. Но к intern это не имеет никакого отношения, этот метод тут, как видите, не вызывается. Всё делается на стадии компиляции и загрузки классов. |
| Автор: danilka 22.9.2010, 17:46 | ||
Под строкой я и имел ввиду объект класса String. То есть Вы хотите сказать что можно создать строку так чтобы в пуле не было соответствующего ей объекта intern()? Поделитесь первоисточником плиз где описано как хранятся строковые литералы, и пул для intern-строк и в какой момент они заполняются. |
| Автор: Evgeni68 24.9.2010, 19:45 | ||||
Данный код создаст объект типа String не помещая строку в пул. |
| Автор: Skipy 24.9.2010, 20:05 | ||||||||
Ну, сам строковый литерал "Hello world!" в пуле таки будет. Так что при сравнении по equals с вновь созданной строкой вернется именно он. Не совсем удачный пример. А вот так - строки точно не будет в пуле (разве что случайно такая окажется
|
| Автор: Evgeni68 24.9.2010, 22:10 | ||||
Да, действительно, пример не совсем удачный. Но смысл от того что литерал будет в пуле?
Выводом будет FALSE. Метод equals в классе String сначала проверяет равенство ссылок, а затем обычное сравнение строк по символам. В итоге никакого преимущества в скорости от того, что литерал в пуле - нет. Или я чего-то не допонимаю? Поправьте. |
| Автор: Skipy 27.9.2010, 12:02 | ||||||
Так лучше? А то Вы как-то упустили, что test2 указывает не на тот экземпляр, который в пуле. |
| Автор: danilka 28.9.2010, 09:43 | ||
Тут Вы имеете ввиду пул строковых литералов или пул intern()? |
| Автор: Skipy 28.9.2010, 11:54 | ||
Пул - он один. Еще раз объясняю - строковые литералы существуют только в исходном коде. При компиляции они помещаются в пул строк. И в него же во время исполнения помещаются отсутствующие в нем строки при вызове на них метода intern(). |
| Автор: Evgeni68 28.9.2010, 21:39 | ||||||||
Так гораздо лучше! Просто я хотел сказать, что создавать новый инстанс строки, передавая в конструктор литерал - мягко говоря не рационально. |
| Автор: danilka 29.9.2010, 09:43 | ||||
То есть правильно ли я понял? Представим что каждая из приведенных ниже строк кода выполняется в новом приложении (пул строк пустой)
|
| Автор: Skipy 29.9.2010, 13:31 | ||||||
Ага. И Вы на эти грабли наступили... Рассказываю. Допустим, у Вас есть строка. Большая, в несколько тысяч символов. Вы выбираете подстроку, символов 10. Исходная строка выбрасывается. Вопрос. Освобождается ли память, которую занимала эта подстрока? А вот не освобождается! В качестве базы строки берется текущий массив char-ов. И просто выставляется смещение и длина. Но при этом ВСЯ исходная строка, фактически, остается в памяти. Это оптимизация, чтобы избежать копирования (и память дополнительная нужна, и время). А вот конструктор String(String) - он КОПИРУЕТ строку. Т.е. если Вы результаты Вашего substring передадите в копирующий конструктор, несмотря на внешнюю бессмысленность этого действия, - у Вас останется массив из нужных 10 символов. А исходный массив соберется как мусор, если Вы убъете на него ссылки. Так что да, создавать строку на основе литерала прямо в коде - нерационально, если только Вы не хотите, чтобы ссылка была не на объект, находящийся в пуле. Но копирующий конструктор сам по себе - бывает весьма полезен. Добавлено @ 13:35
Да. Да. Нет. "test" помещается в пул - ибо есть строковый литерал. s - по значению в пуле будет, ибо создана на основе литерала. Т.е. вызов s.intern() не приведет к помещению новых данных в пул, а вернет ссылку на "test". А вот ссылка на s - не будет совпадать со сcылкой на "test", т.е. результатом (s =="test") будет false. |
| Автор: Evgeni68 29.9.2010, 20:23 | ||
Спасибо, очень полезно. О копирующем конструкторе не задумывался |