Реализация IdentityLinkedHashSet путем переноса LinkedHashSet - PullRequest
0 голосов
/ 18 января 2019

В стандартной библиотеке Java значение LinkedHashSet совпадает с HashSet, но с предсказуемым порядком итерации (порядок вставки). Мне нужен IdentityHashSet (который использует идентификатор объекта или ссылочное равенство вместо равенства объектов) с предсказуемым порядком итерации (порядок вставки).

Хотя в стандартной библиотеке Java нет IdentityHashSet, вы можете легко создать ее, используя доступную IdentityHashMap, с помощью следующего оператора:

Set<T> identitySet = java.util.Collections.newSetFromMap(new IdentityHashMap<>());

Это почти та же самая реализация, которая используется в Guava's Sets.newIdentityHashSet(); но это имеет неопределенное, обычно хаотическое расположение ключей HashMapHashSet элементов).

Мои поиски реализации IdentityLinkedHashSet не дали результатов, поэтому я решил реализовать ее самостоятельно. Один полезный результат, который я нашел, был этот ответ , который предлагает использовать класс-оболочку для идентификации в композиции с LinkedHashSet.

Я пытался реализовать эту идею. Ниже приведен ключевой фрагмент моей реализации. Полный доступ к Gist можно получить здесь .

public class IdentityLinkedHashSet<E> implements Set<E> {

    private LinkedHashSet<IdentityWrapper> set;

    /* ... constructors ... */

    @Override
    public boolean add(E e) {
        return set.add(new IdentityWrapper(e));
    }

    @Override
    public boolean contains(Object obj) {
        return set.contains(new IdentityWrapper((E) obj));
    }

    /* ... rest of Set methods ... */

    private class IdentityWrapper {

        public final E ELEM;

        IdentityWrapper(E elem) {
            this.ELEM = elem;
        }

        @Override
        public boolean equals(Object obj) {
            return obj != null && ELEM == obj;
        }

        @Override
        public int hashCode() {
            return System.identityHashCode(ELEM);
        }
    }
}

Затем я написал несколько юнит-тестов для проверки моей реализации. К сожалению, некоторые из утверждений не удаются! Вот мои тесты:

String str1 = new String("test-1");
String str2 = new String("test-2");
String str3 = new String("test-2");

Set<String> identitySet = new IdentityLinkedHashSet<>();

assertTrue(idSet.add(str1));
assertFalse(idSet.add(str1));       //  <-- fails!
assertTrue(idSet.contains(str1));   //  <-- fails!
//
assertTrue(idSet.add(str2));
assertFalse(idSet.add(str2));       //  <-- fails!
assertTrue(idSet.contains(str2));   //  <-- fails!
assertFalse(idSet.contains(str3));
//
assertTrue(idSet.add(str3));
assertFalse(idSet.add(str3));       //  <-- fails!
assertTrue(idSet.contains(str3));   //  <-- fails!
assertEquals(3, idSet.size());      //  <-- fails!

Что я сделал не так в этой реализации?

1 Ответ

0 голосов
/ 18 января 2019

При вызове метода IdentityWrapper.equals(). LinkedHashSet не пропустит ELEM, а скорее IdentityWrapper объект.

Вы должны ввести дополнительную проверку (instanceOf), а затем развернуть переданный объект для сравнения элементов:

public boolean equals(Object obj) {
    return (obj instanceof IdentityLinkedHashSet<?>.IdentityWrapper) && 
        ELEM == ((IdentityLinkedHashSet<?>.IdentityWrapper) obj).ELEM;
}

Некоторые заметки:

  1. instanceof проверит наличие нуля, поэтому вы можете полностью удалить эту проверку.
  2. Вы должны ссылаться на IdentityWrapper следующим образом: IdentityLinkedHashSet<?>.IdentityWrapper, потому что это не класс static. Как отмечено в комментариях. Его можно сделать статическим, а тип ELEM можно изменить с E на Object. Который вы бы оставили также с более приятным equals методом:

    private static class IdentityWrapper {
    
        public final Object ELEM;
    
        IdentityWrapper(Object elem) {
            this.ELEM = elem;
        }
    
        @Override
        public boolean equals(Object obj) {
            return (obj instanceof IdentityWrapper) && ELEM == ((IdentityWrapper) obj).ELEM;
        }
    
        @Override
        public int hashCode() {
            return System.identityHashCode(ELEM);
        }
    }
    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...