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;
    }
}
Другие вопросы по тегам