При том, что присваивание переменной null ничего явно не укажет сборщику мусора. А вот отсутствие ссылок — укажет, что объект ушел из области видимости. Попробуйте донести свою мысль еще раз. Я всего лишь хотел донести простую мысль, что переприсваивание локальной переменной значения это плохой стиль, всегда есть более прямые способы донести свою мысль до кода.
Я бы посоветовал вообще вот такой вариант, с явным указанием области видимости. Тут как минимум всегда видно, что именно за объект лежит в e.
{
Elem e = new HeavyElem();
}
{
Elem e = new HeavyElem();
}
Впрочем подобный код все равно намекает, что скорее всего тут необходим рефакторинг, скажем экстракт метода.
1. По контракту хешкод обязан сохранять свое значение при условии, что информация используемая в equals не была обновлена. С-но, если же эта информация была обновлена, то хэш код изменится, и это не нарушает контракт хэш кода. Посмотрите javadoc к методу hashCode.
method must consistently return the same integer, provided no information used in equals comparisons on the object is modified.
2. HashSet построен на основе HashMap, который в свою очередь построен на основе массива.
/**
* The table, resized as necessary. Length MUST Always be a power of two.
*/
transient Entry[] table;
3. HashCodeUtil или любой другой аналог, не поможет вам кэшировать хэш в мутабл объекте. Потому что этот кэш придется сбрасывать на любое изменение объекта. И вообще рекомендовать Apache Commons это неразумно, он застрял на уровне семантики явы 1.4 и его развитие неудовлетворительное. Есть отличный форк от него под названием Guava, в которой есть аналогичные метода, но уже с семантикой 6й явы, например, Objects.hashCode(...).
4. Вы написали, что хэш будет считаться по канонам явы один раз на объект. Это очевидно неправда.
HashMap:
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
В самом начале метода put у HashMap у ключа тупо вызывается hashCode, на каждый put/add/contains итп будет вызван этот метод.
Это не так. Попробуйте добавить объект в HashSet 2 раза и проверьте, сколько раз будет вызван метод hashCode. Ответ — 2 раза. Более того, если после добавления элемента в HashSet вы поменяете его состояние и этим измените hashCode, то получите шикарный баг. Итерируясь по коллекции вы сможете найти объект, но тем не менее contains вернет false (как раз таки объект вы не действительно найдете в HashSet) и добавить его можно будет еще раз, уже в другое место массива. Я собственно это в свое время и словил, ибо не понимал что HashMultimap слово Hash от HashSet, а не как я думал от HashMap. ArrayMultimap решил ту проблему.
HashSet[Object] q = new HashSet[Object]();
Object o = new Object() {
@Override
public int hashCode() {
System.err.println(«hashCode»);
return super.hashCode();
}
};
q.add(o);
q.add(o);
Тем не менее, кэшировать хэшкод это очень правильная идея в плане перформанса, но поддержка этого кэширования в коде слишком трудоемко для сложных случаев, где от кэша собственно и будет эффект, ибо кэш надо будет сбрасывать на любую мутацию объекта, в итоге не встречал, чтобы ктото хэш кэшировал на практике.
Про сериализацию. Я бы не советовал, без крайней нужды использовать стандартную ява сериализацию. Protobuf как правило намного более удобное решение. В принципе, вообще сильная связность объекта с его сериализацией, его компаратором или с его реализацией методов hashCode/equals как правило это плохая идея. Впрочем, по последнему пункту красивых решения я не встречал, поэтому все упирается во wrapper-декоратор объекты, которые просто перегружают методы hashCode/equals.
На самом деле это все большая проблема, потому что написать адекватный контейнер класс, который содержит 2 банальных стринг поля и по сути обычный pojo bean занимает у меня уже до 3х экранов кода, что конечно ужас. И, что печально, код везде повторяется, а вот без рефлекшена его вынести куда-то не выходит, а рефлекшен слабое решение с позиции перформанса, а вот для таких контейнеров как раз перформанс важен, ибо они обычно пролетают всю систему и несколько потоков меняют при этом.
Для сложных объектов скорее всего будет сложный хеш. Причем, он еще в подавляющем большинстве случаев еще и не кэшируется и будет честно считаться каждый раз при работе с хешсетом.
У нас на проекте принято использовать для логгера
private Logger logger = Logger.getLogger(getClass());
опять же, несмотря на некоторую неэффективность данного подхода, зато очень удобно копипастить и нет глупых ошибок при этом.
И я бы наоборот советовал, везде где это возможно, инициализировать поля, там же где они объявлены. Код все еще чистый, и на одну строчку короче. Для того и предзначена эта возможность, чтобы не засорять этим конструктор.
Проблема с простыми объектами, если они не просто создались, а пожили некоторое небольшое время и после этого освободились. Тут они вполне могут случайно пережить 2-3 минор коллекции и угодить в олд ген. Если приложение серверное (как у меня) и живет длительное время и не может позволить себе задержки от мажор коллекций, это большая проблема со временем. Хотя Оракл активно и пропагандирует, что создание pojo операция копеечная, это все еще бывает заметной проблемой.
Кстати, стоит провести поиск по сорцам JDK как находятся тысяча использовании new Integer(0) и тому подобных. Например в классе Locale, MessageFormat. В сорцах спринга тоже встречается. Это банально чище выглядит в коде, нагляднее. Так что практика довольно распространенная, насколько она вредна сложно судить без тестов.
В целом согласен. Только в случае с баффером-билдером так и непонятно, в чем преимущество последнего, кроме синхронизации, которая вообще незаметна при однопоточном использовании и дает надежность против легкого удара по перформансу в многопоточном использовании.
Хотелось бы увидеть доказательства, что после JIT действительно будет хоть какойто выигрыш? Скажем, я лично не уверен, что new String("...") и «ююю» вообще компилируется в разные вещи. Я конечно тут гляну на досуге, но без веских доказательств мне сложно поверить, что будет разница после JIT во всех этих преобразованиях в Integer, String итп.
Скажем «Преобразование чисел» предложение оно вообще непонятно как может быть скомпилировано иначе чем в valueOf. Если там будет отличие в байт коде мне это будет удивительно. Сам активно предпочитаю + "", изза лаконичности и лучшей читабельности.
Collections.emptyMap и тому подобные методы (Arrays.asList скажем) это очень опасная штука ибо неявно immutable. Причем код понять это иначе чем получив эксепшен не может. Что впрочем вообще общая проблема паттерна null object, относительно безопасно отдавать его только как Iterable<...>.
Все отсылки к идеологии ООП, тоже слабый аргумент. Если конечно это не академическое программирование. Я не вижу практической пользы набор констант оформлять как класс и уж бы точно не стал бы советовать такую практику новичку. И воспринимать ООП излишне серьезно я бы тоже крайне не советовал, это довольно вредное заблуждение. Программирование должно быть прагматично, и исходить где лучше применить наследование, делегирование или композицию лучше не из формальных правил ООП, а из соображений удобства. Скажем, в некоторых случаях наследования стоит избегать где только это возможно изза непредсказуемых поведений потомком при изменении родителя.
Про отличия стринг билдера и буфера, это вообще очень странный совет, синхронизация дальше BIAS не надуется и на производительности скажется не более, чем сверка threadId с монитором. Сомневаюсь, что это сильный аргумент. Хотя, конечно билдер использовать более правильно, но на перформанс кивать тут довольно глупо, если это не мегаострое место в программе.
Боятся instanceof тоже не стоит, где-то была работа где изучались разные виды multiple dispatch. Все эти двойные коллбеки аля визитор, instanceof и прочие полиморфизмы. instanceof по перформансу был одним из самых быстрых. Опять же проблема тут не в перформансе, а скорее в поддерживаемости кода. Тормознутость рефлекшена, кстати опять же сильно преувеличена, при умелом подходе оно бегает довольно шустро.
Я не очень понял, а если я хочу позвонить Петру Петровичу, что мне делать то? Судя по видео, это еще хуже сделано, чем на СГС2, где вообще предлагается пойти в контакты, начать вводить петр петрович и в 2 тапа ему звонить. HTC way когда ты в номеронабирателе цифрами набираешь не цифры, а имя или фамилию также присутствует в СГС2, но не работает с русским, а посему бесполезна. В Sense Это сделано разительно удобнее, ибо набираешь ты большими телефонными кнопками и по одному тапу звонишь.
Тут получается нужно тап на контакты, тап на поиск и вводить этими мелкими экранными кнопками ФИО, что зачастую неудобно на ходу.
Также, судя по всему, так и не поправили вроде очевиднейшую вещь, особенно для гугла с его поисковой специализацией. Если общесистемным поиском пробовать искать контакты, это можно сделать только по имени и фамилии, но скажем не по комментариям или другим полям. Скажем если у меня контакт имеет имя Михаил Иванов, но в комментах у него написано, что он автомеханик. Мне довольно сложно было его найти. В это сторону в 4ке что либо поменяли?
{
Elem e = new HeavyElem();
}
{
Elem e = new HeavyElem();
}
Впрочем подобный код все равно намекает, что скорее всего тут необходим рефакторинг, скажем экстракт метода.
method must consistently return the same integer, provided no information used in equals comparisons on the object is modified.
2. HashSet построен на основе HashMap, который в свою очередь построен на основе массива.
/**
* The table, resized as necessary. Length MUST Always be a power of two.
*/
transient Entry[] table;
3. HashCodeUtil или любой другой аналог, не поможет вам кэшировать хэш в мутабл объекте. Потому что этот кэш придется сбрасывать на любое изменение объекта. И вообще рекомендовать Apache Commons это неразумно, он застрял на уровне семантики явы 1.4 и его развитие неудовлетворительное. Есть отличный форк от него под названием Guava, в которой есть аналогичные метода, но уже с семантикой 6й явы, например, Objects.hashCode(...).
4. Вы написали, что хэш будет считаться по канонам явы один раз на объект. Это очевидно неправда.
HashMap:
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
В самом начале метода put у HashMap у ключа тупо вызывается hashCode, на каждый put/add/contains итп будет вызван этот метод.
HashSet[Object] q = new HashSet[Object]();
Object o = new Object() {
@Override
public int hashCode() {
System.err.println(«hashCode»);
return super.hashCode();
}
};
q.add(o);
q.add(o);
Тем не менее, кэшировать хэшкод это очень правильная идея в плане перформанса, но поддержка этого кэширования в коде слишком трудоемко для сложных случаев, где от кэша собственно и будет эффект, ибо кэш надо будет сбрасывать на любую мутацию объекта, в итоге не встречал, чтобы ктото хэш кэшировал на практике.
На самом деле это все большая проблема, потому что написать адекватный контейнер класс, который содержит 2 банальных стринг поля и по сути обычный pojo bean занимает у меня уже до 3х экранов кода, что конечно ужас. И, что печально, код везде повторяется, а вот без рефлекшена его вынести куда-то не выходит, а рефлекшен слабое решение с позиции перформанса, а вот для таких контейнеров как раз перформанс важен, ибо они обычно пролетают всю систему и несколько потоков меняют при этом.
private Logger logger = Logger.getLogger(getClass());
опять же, несмотря на некоторую неэффективность данного подхода, зато очень удобно копипастить и нет глупых ошибок при этом.
И я бы наоборот советовал, везде где это возможно, инициализировать поля, там же где они объявлены. Код все еще чистый, и на одну строчку короче. Для того и предзначена эта возможность, чтобы не засорять этим конструктор.
Скажем «Преобразование чисел» предложение оно вообще непонятно как может быть скомпилировано иначе чем в valueOf. Если там будет отличие в байт коде мне это будет удивительно. Сам активно предпочитаю + "", изза лаконичности и лучшей читабельности.
Collections.emptyMap и тому подобные методы (Arrays.asList скажем) это очень опасная штука ибо неявно immutable. Причем код понять это иначе чем получив эксепшен не может. Что впрочем вообще общая проблема паттерна null object, относительно безопасно отдавать его только как Iterable<...>.
Все отсылки к идеологии ООП, тоже слабый аргумент. Если конечно это не академическое программирование. Я не вижу практической пользы набор констант оформлять как класс и уж бы точно не стал бы советовать такую практику новичку. И воспринимать ООП излишне серьезно я бы тоже крайне не советовал, это довольно вредное заблуждение. Программирование должно быть прагматично, и исходить где лучше применить наследование, делегирование или композицию лучше не из формальных правил ООП, а из соображений удобства. Скажем, в некоторых случаях наследования стоит избегать где только это возможно изза непредсказуемых поведений потомком при изменении родителя.
Про отличия стринг билдера и буфера, это вообще очень странный совет, синхронизация дальше BIAS не надуется и на производительности скажется не более, чем сверка threadId с монитором. Сомневаюсь, что это сильный аргумент. Хотя, конечно билдер использовать более правильно, но на перформанс кивать тут довольно глупо, если это не мегаострое место в программе.
Боятся instanceof тоже не стоит, где-то была работа где изучались разные виды multiple dispatch. Все эти двойные коллбеки аля визитор, instanceof и прочие полиморфизмы. instanceof по перформансу был одним из самых быстрых. Опять же проблема тут не в перформансе, а скорее в поддерживаемости кода. Тормознутость рефлекшена, кстати опять же сильно преувеличена, при умелом подходе оно бегает довольно шустро.
15.05 = 2.15 * 7
Тут получается нужно тап на контакты, тап на поиск и вводить этими мелкими экранными кнопками ФИО, что зачастую неудобно на ходу.
Также, судя по всему, так и не поправили вроде очевиднейшую вещь, особенно для гугла с его поисковой специализацией. Если общесистемным поиском пробовать искать контакты, это можно сделать только по имени и фамилии, но скажем не по комментариям или другим полям. Скажем если у меня контакт имеет имя Михаил Иванов, но в комментах у него написано, что он автомеханик. Мне довольно сложно было его найти. В это сторону в 4ке что либо поменяли?