Showing posts with label java. Show all posts
Showing posts with label java. Show all posts

Wednesday, July 2, 2014

A little tip about equals() and hashCode()


Don't forget to check if equals() and hashCode() are overriden in any class (unless you use String or class that you're very familiar with) you use in collection!

And a little story about that...

Several days ago I tried to fix a memory leak somewhere in LruCache. LruCache provides a way to cache some objects while they're not exceeding a particular amount of memory. After that the oldest object in the cache is not being hold any more and available for garbage collector.

LruCache isn't a collection itself, but it should contain a map inside (and it does, have a look at the source code here) for key to value mapping (where the value is a cached object you need), so any class you use as key for LruCache should contain overridden equals() and hashCode().

But, unlucky for me, I used ResolveInfo as the key (and application icon as a value). Although it looks like a pretty simple data structure, it doesn't contain equals() and hashCode() overridden, I don't see the reason why - you can look here if you don't believe me.

So instead of caching it simply stored my icons and sometimes garbage collector couldn't do anything before OutOfMemoryError was throwed.

In my case, I replaced ResolveInfo to my own class that contains two fields: resolveInfo.activityInfo.packageName and resolveInfo.activityInfo.name and ultimately override equals() and hashCode(). Pair class is useful for such purposes too (and I actually copied equals() and hashCode() implementation from there) but it's always better to use more meaningful names.

Have a nice day!

P.S. Be careful to update to ADT 23.0.0 - it ruined my Eclipse and I needed to download the whole ADT Bundle for clean install. See here for details.

Monday, April 2, 2012

synchronized(Boolean)


Читал чужой код, вижу блоки синхронизации по какой-то переменной, и вдруг понимаю, что тип этой переменной - Boolean. То есть смысл переменной в том, чтобы хранить какой-то флажок, а объектом переменная сделана для того, чтобы иметь возможность выполнить по ней синхронизацию. И тут мы проходим мимо здоровенных граблей, который могут нас больно ударить по самому больному месту. Дело в том, что существует всего два объекта Boolean - Boolean.FALSE и Boolean.TRUE. В двух абсолютно не связанных друг с другом кусках кода мы можем иметь по переменной типа Boolean. Эти переменные будут иметь абсолютно разный смысл, но в один момент они могут ссылаться на один объект. И когда мы войдем в блок синхронизации в одном месте программы, другой блок уже не сможет выполниться. И наоборот. Хотя логической связи между ними не будет вообще.

Формально, конечно, Boolean не синглетон. Конструктор у него public, и экземпляров может быть сколько угодно, то есть вместо Boolean.valueOf() мы можем использовать new Boolean(). Но это выглядит как еще больший хак - любой программист заменит конструктор на статический метод Boolean.valueOf(), как рекомендует официальная документация, и незаслуженно получит в лоб. Я не могу придумать ситуацию, где мог бы понадобиться конструктор Boolean и синхронизация по нему.

Как вывод - НИКОГДА не используйте синхронизацию для объектов типа Boolean и, обобщая, для оберток всех примитивных типов (поскольку некоторые объекты могут кэшироваться внутри класса).