Java: лучший способ сравнить наборы из 2 чисел, чем конкатенация
Вот ситуация: я хочу проверить 2 объекта на уникальность на основе 2 разных идентификаторов. Пример:
// Note I'm using JSON notation to keep things simple; the actual code
// is with Java Objects
// OBJECT A
{
main_id: 0,
id_a: 123,
id_b: 456
}
// OBJECT B
{
main_id: 1,
id_a: 123,
id_b: 456
}
// OBJECT C
{
main_id: 2,
id_a: 123,
id_b: 789
}
В этом примере объекты A и B совпадают, потому что id_a
а также id_b
одинаковы, а объект С отличается.
Чтобы определить это в коде, я планирую преобразовать оба идентификатора в строку и объединить их вместе с символом-разделителем в середине (например, "{id_a},{id_b}"
), затем добавив их в Set<String>
проверить на уникальность.
У меня вопрос, есть ли лучший способ? (Лучше я имею в виду более эффективный и / или менее сложный)
3 ответа
Если вы хотите использовать HashSet
Вы можете переопределить hashCode
а также equals
исключительно смотреть на этих двух членов.
Хэш-код: (31
это просто популярная программа для хэширования в Java)
return 31*id_a + id_b;
Равно: (к которому вам, очевидно, нужно будет добавить instanceof
проверка и преобразование типов)
return id_a == other.id_a && id_b == other.id_b;
Если вы не хотите связывать эти функции с классом, потому что он используется по-другому в другом месте, но вы все еще хотите использовать HashSet
, вы могли бы рассмотреть:
Создание промежуточного класса для хранения в наборе, который будет содержать ваш класс в качестве члена и соответствующим образом реализовывать описанные выше методы.
Используй свой струнный подход
использование
HashSet<Point>
-Point
не идеален для некоординатных целей, так как члены просто называютсяx
а такжеy
, но я считаю полезным иметь такой класс доступным, по крайней мере, для непроизводственного кода.
В качестве альтернативы, если вы хотите использовать TreeSet
, вы могли бы реализовать свой класс Comparable
(наиважнейшая compareTo
) или предоставить Comparator
для TreeSet
, оба из которых будут сравниваться в первую очередь по одному идентификатору, а затем по второму.
Основная идея будет выглядеть примерно так:
if (objectA.id_a != objectB.id_a)
return Integer.compare(objectA.id_a, objectB.id_a);
return Integer.compare(objectA.id_b, objectB.id_b);
Не уверен, что это более эффективно или менее грязно. Вы можете сохранить исходный хеш-код / равно с помощью основного идентификатора (согласно вашему комментарию), а затем создать оболочку, которая имеет хеш-код / равно для составного идентификатора, idb. Может быть, за то, что вам нужно, хотя.
CompositeIdEntity.java
public interface CompositeIdEntity {
long getIdA();
long getIdB();
}
Entity.java
public class Entity implements CompositeIdEntity {
private final long mainId;
private final long idA;
private final long idB;
public Entity(long mainId, long idA, long idB) {
this.mainId = mainId;
this.idA = idA;
this.idB = idB;
}
@Override
public long getIdA() {
return idA;
}
@Override
public long getIdB() {
return idB;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (mainId ^ (mainId >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Entity other = (Entity) obj;
if (mainId != other.mainId)
return false;
return true;
}
@Override
public String toString() {
return "Entity [mainId=" + mainId + ", idA=" + idA + ", idB=" + idB
+ "]";
}
}
CompositeIdWrapper.java
public class CompositeIdWrapper {
private final CompositeIdEntity compositeIdEntity;
public CompositeIdWrapper(CompositeIdEntity compositeIdEntity) {
this.compositeIdEntity = compositeIdEntity;
}
public CompositeIdEntity getCompositeIdEntity() {
return compositeIdEntity;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ (int) (compositeIdEntity.getIdA() ^ (compositeIdEntity
.getIdA() >>> 32));
result = prime * result
+ (int) (compositeIdEntity.getIdB() ^ (compositeIdEntity
.getIdB() >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CompositeIdWrapper other = (CompositeIdWrapper) obj;
if (compositeIdEntity.getIdA() != other.compositeIdEntity.getIdA())
return false;
if (compositeIdEntity.getIdB() != other.compositeIdEntity.getIdB())
return false;
return true;
}
}
Test.java
import java.util.HashSet;
import java.util.Set;
public class Test {
public static void main(String[] args) {
Entity en1 = new Entity(0, 123, 456);
Entity en2 = new Entity(1, 123, 456);
Entity en3 = new Entity(2, 123, 789);
Entity en4 = new Entity(2, 123, 456);
Entity en5 = new Entity(1, 123, 789);
// Set based on main id
Set<Entity> mainIdSet = new HashSet<>();
mainIdSet.add(en1);
mainIdSet.add(en2);
mainIdSet.add(en3);
mainIdSet.add(en4);
mainIdSet.add(en5);
System.out.println("Main id set:");
for (Entity entity : mainIdSet) {
System.out.println(entity);
}
// Set based on ida, idb
Set<CompositeIdWrapper> compositeIdSet = new HashSet<>();
compositeIdSet.add(new CompositeIdWrapper(en1));
compositeIdSet.add(new CompositeIdWrapper(en2));
compositeIdSet.add(new CompositeIdWrapper(en3));
compositeIdSet.add(new CompositeIdWrapper(en4));
compositeIdSet.add(new CompositeIdWrapper(en5));
System.out.println("Composite id set:");
for (CompositeIdWrapper wrapped : compositeIdSet) {
System.out.println(wrapped.getCompositeIdEntity());
}
}
}
Выход
Main id set:
Entity [mainId=1, idA=123, idB=456]
Entity [mainId=2, idA=123, idB=789]
Entity [mainId=0, idA=123, idB=456]
Composite id set:
Entity [mainId=0, idA=123, idB=456]
Entity [mainId=2, idA=123, idB=789]
Смотрите здесь, здесь я переопределяю equals() и hashcode(), чтобы обеспечить уникальность поля "name" объекта Person
public class SetObjectEquals {
Person p1 = new Person("harley");
Person p2 = new Person("harley");
public void method1() {
Set<Person> set = new HashSet<Person>();
set.add(p1);
set.add(p2);
System.out.println(set);
}
public static void main(String[] args) {
SetObjectEquals obj = new SetObjectEquals();
obj.method1();
}
}
class Person {
String name;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
Person(String name) {
this.name = name;
}
}