HashingStrategy - это концепция, которую вы ищете. Это интерфейс стратегии, который позволяет вам определять пользовательские реализации equals и hashcode.
public interface HashingStrategy<E>
{
int computeHashCode(E object);
boolean equals(E object1, E object2);
}
Вы не можете использовать HashingStrategy
со встроенным HashSet
или HashMap
. GS Collections включает в себя java.util.Set с именем UnifiedSetWithHashingStrategy
и java.util.Map с именем UnifiedMapWithHashingStrategy
.
Давайте рассмотрим пример.
public class Data
{
private final int id;
public Data(int id)
{
this.id = id;
}
public int getId()
{
return id;
}
// No equals or hashcode
}
Вот как вы можете настроить UnifiedSetWithHashingStrategy
и использовать его.
java.util.Set<Data> set =
new UnifiedSetWithHashingStrategy<>(HashingStrategies.fromFunction(Data::getId));
Assert.assertTrue(set.add(new Data(1)));
// contains returns true even without hashcode and equals
Assert.assertTrue(set.contains(new Data(1)));
// Second call to add() doesn't do anything and returns false
Assert.assertFalse(set.add(new Data(1)));
Почему бы просто не использовать Map
? UnifiedSetWithHashingStrategy
использует половину памяти UnifiedMap
и одну четверть памяти HashMap
. А иногда у вас нет удобного ключа и вам нужно создать синтетический ключ, например, кортеж. Это может тратить больше памяти.
Как мы выполняем поиск? Помните, что наборы имеют contains()
, но не get()
. UnifiedSetWithHashingStrategy
реализует Pool
в дополнение к Set
, поэтому он также реализует форму get()
.
Вот простой подход к обработке строк без учета регистра.
UnifiedSetWithHashingStrategy<String> set =
new UnifiedSetWithHashingStrategy<>(HashingStrategies.fromFunction(String::toLowerCase));
set.add("ABC");
Assert.assertTrue(set.contains("ABC"));
Assert.assertTrue(set.contains("abc"));
Assert.assertFalse(set.contains("def"));
Assert.assertEquals("ABC", set.get("aBc"));
Это демонстрирует API, но не подходит для производства. Проблема в том, что HashingStrategy постоянно делегирует String.toLowerCase()
, что создает кучу мусорных строк. Вот как вы можете создать эффективную стратегию хеширования для строк без учета регистра.
public static final HashingStrategy<String> CASE_INSENSITIVE =
new HashingStrategy<String>()
{
@Override
public int computeHashCode(String string)
{
int hashCode = 0;
for (int i = 0; i < string.length(); i++)
{
hashCode = 31 * hashCode + Character.toLowerCase(string.charAt(i));
}
return hashCode;
}
@Override
public boolean equals(String string1, String string2)
{
return string1.equalsIgnoreCase(string2);
}
};
Примечание: Я разработчик коллекций GS.