Использовать HashSet через ArrayList для передачи намерений?

Представьте, что мне нужно создать коллекцию элементов, где порядок может или не имеет значения. Фактически все, что я планирую делать, это использовать итератор. Я замечаю, что большинство моих коллег используют ArrayList vs LinkedHashSet/HashSet. Мой вопрос: если я знаю, что эти элементы должны быть уникальными, я должен использовать набор или список? Эффективно это не имеет особого значения, но не позволяет более эффективно передать, что элементы уникальны?

Я считаю, что это интересный вопрос для крупных корпоративных приложений по нескольким причинам: 1) Если вы не можете гарантировать качество кода в целом, использование Set может быть опасным. Зачем? Потому что equals() и hashcode могут быть неправильно переопределены, и, таким образом, использование Set может вызвать некоторые неприятные проблемы. 2) Использование списка более устойчиво к будущим изменениям. Если дубликаты по любой причине становятся возможными, не нужно беспокоиться.

По существу это сводится к следующему: если я знаю, что я должен ожидать уникальные элементы, должен ли я одобрить Set over List во всех случаях?

Изменить: я полагаю, я также спрашиваю: должен ли Set использоваться обеспечить, что дубликаты не добавлены, или же он может также использоваться для единственной цели иллюстрации что нет дубликатов для простоты понимания?

Ответ 1

1) является полностью фиктивным. Не работайте с ошибками, исправляйте их. Поэтому используйте Set, если порядок не имеет значения, или SortedSet, если дело имеет значение. Если элементы не должны быть уникальными (и вы должны определить это сейчас, и это обычно не должно меняться), не стесняйтесь использовать List.

Ответ 2

Если вам нужно подумать об уникальных элементах, используйте Set. Но если вы не доверяете своим пользователям правильно внедрять equals/hashCode, я предлагаю вам документировать, что если что-то не так с итерацией, проверьте свой equals/hashCode! Но это действительно зависит от варианта использования модели данных.

Ответ 3

Рассмотрим читаемость кода.

Если вы ожидаете и хотите уникальный набор, используйте структуру данных "SET", в долгосрочной перспективе все будет намного яснее. И, таким образом, это также будет способствовать улучшению кодирования.

Ответ 4

Кто-то сказал, что HashSet предлагает постоянную производительность во время добавления, удаления, добавления и размера.

Фактический оператор в JavaDocs: "Этот класс предлагает постоянную производительность времени для основных операций (добавлять, удалять, содержать и размер), , предполагая, что хеш-функция правильно распределяет элементы среди ковшей."

Это означает, что вы можете получить медленное время добавления при добавлении чего-либо в набор, если он получил плохо реализованный метод hashCode.

Следующий код демонстрирует, что может произойти в зависимости от вашей реализации hashCode.

public void testHashSetAddition() {
    for(int mod=10; mod <= 100; mod=mod+10 ) {
        Set s = new HashSet();
        long start = new Date().getTime();
        for(int i=0; i<100000; i++) {
            s.add(new Foo(i % mod));
        }
        long end = new Date().getTime();
        System.out.println("Mod: " + mod + " - " + (end - start) + "ms");
    }
}

class Foo {
    private int hc;
    public Foo(int i) {
        this.hc = i;
    }
    public int hashCode() {
        return hc;
    }
}

Результаты синхронизации:

Mod: 10 - 22683ms
Mod: 20 - 14200ms
Mod: 30 - 10486ms
Mod: 40 - 8562ms
Mod: 50 - 7761ms
Mod: 60 - 6740ms
Mod: 70 - 5778ms
Mod: 80 - 5268ms
Mod: 90 - 4716ms
Mod: 100 - 3966ms

Затем, выполняя точно такой же тест для ArrayList:

public void testAddingToArrayList() {
    for(int mod=100; mod >= 10; mod=mod-10 ) {
        List l = new ArrayList();
        long start = new Date().getTime();
        for(int i=0; i<100000; i++) {
            l.add(new Foo(i % mod));
        }
        long end = new Date().getTime();
        System.out.println("Mod: " + mod + " - " + (end - start) + "ms");
    }
}

дает:

Mod: 100 - 50ms
Mod: 90 - 30ms
Mod: 80 - 40ms
Mod: 70 - 30ms
Mod: 60 - 30ms
Mod: 50 - 40ms
Mod: 40 - 20ms
Mod: 30 - 30ms
Mod: 20 - 30ms
Mod: 10 - 30ms

Ответ 5

    import java.util.*;

    public class Test {
        public void testHashSetAddition() {
            for(int mod=10; mod <= 100; mod=mod+10 ) {
                Set s = new HashSet();
                long start = new Date().getTime();
                for(int i=0; i<100000; i++) {
                    s.add(new Foo(i % mod));
                }
                System.out.println(s.size());
                long end = new Date().getTime();
                System.out.println("Mod: " + mod + " - " + (end - start) + "ms");
            }
        }
        public void testAddingToArrayList() {
            for(int mod=100; mod >= 10; mod=mod-10 ) {
                List l = new ArrayList();
                long start = new Date().getTime();
                for(int i=0; i<100000; i++) {
                    l.add(new Foo(i % mod));
                }
                System.out.println(l.size());
                long end = new Date().getTime();
                System.out.println("Mod: " + mod + " - " + (end - start) + "ms");
            }
        }

        public static void main(String...a){
            new Test().testHashSetAddition();
            new Test().testAddingToArrayList();
        }
        class Foo {
            private int hc;
            public Foo(int i) {
                this.hc = i;
            }
            public int hashCode() {
                return hc;
            }
            public int getHc(){
                return hc;
            }
            public boolean equals(Object o){
                if(!(o instanceof Foo)) return false;
                Foo fo = (Foo)o;
                return fo.getHc() == this.hc;
            }
        }

    }
/*
10
Mod: 10 - 31ms
20
Mod: 20 - 16ms
30
Mod: 30 - 15ms
40
Mod: 40 - 16ms
50
Mod: 50 - 0ms
60
Mod: 60 - 16ms
70
Mod: 70 - 0ms
80
Mod: 80 - 15ms
90
Mod: 90 - 0ms
100
Mod: 100 - 0ms
100000
Mod: 100 - 32ms
100000
Mod: 90 - 31ms
100000
Mod: 80 - 31ms
100000
Mod: 70 - 31ms
100000
Mod: 60 - 32ms
100000
Mod: 50 - 15ms
100000
Mod: 40 - 31ms
100000
Mod: 30 - 32ms
100000
Mod: 20 - 15ms
100000
Mod: 10 - 32ms
*/

Ответ 6

Установите, если это предпочтительнее, так как это обеспечит уникальность и покажет вам, где вы ошибаетесь.

У вас могут быть некоторые проблемы, когда методы неправильно переоцениваются, но правильный выбор - не молиться и не называть их. Обнаруживайте ошибки и исправляйте их!

Изменить: И да, если яснее, когда вы видите Set, нужны уникальные значения и еще лучше: применяются уникальные значения. Никогда не предполагайте/не доверяйте использованию своего кода;)

Ответ 7

Я не думаю, что любой выбор должен быть рассмотрен, чтобы передать намерение - ваш метод должен быть объявлен, чтобы вернуть просто Collection с соответствующим общим параметром, как для гибкости, так и, как вы сказали, потребители этого должен быть в состоянии просто перебирать его, не беспокоясь о том, какой он тип. Это дает дополнительное преимущество в том, что если требования меняются позже или получается, что по какой-либо причине ваш первоначальный выбор был неправильным, вам нужно изменить код только в одном месте (вызов начального конструктора).

Предполагается, что намерение должно быть указано в документации метода, в котором должно быть указано, будет ли итератор коллекции возвращать элементы в любом конкретном порядке и будут ли отображаться повторяющиеся элементы.

И я также согласен с вышеуказанными сообщениями, в которых говорится, что ваши рассуждения вокруг пункта 1) выключены - если есть классы с неправильными реализациями equals и/или hashcode, которые вы хотите поместить в набор, вы исправляете их, а затем используйте Set!

Ответ 8

@Andrzej Doyle - я не думаю, что когда вы добавляете элемент в набор, то выполняется дублирование сравнения. Set внутри использует hashMap, и поэтому любой дублирующий ключ будет переопределен и hnce не будет проверять конкретную проверку

Ответ 9

@Andrzej Doyle - я не думаю, что когда вы добавляете элемент в набор, то выполняется дублирование сравнения. Set внутри использует hashMap, и поэтому любой дублирующий ключ будет переопределен и hnce не будет проверять конкретную проверку

Ответ 10

Использование реализации Set над реализацией List может ухудшить производительность. При вставке элемента в Set вам нужно проверить, что он не является дубликатом. Если вы планируете использовать итератор, используйте простейшую возможную реализацию (ArrayList).

Я не думаю, что это хорошая идея использовать набор для передачи информации. Если вы добавляете элементы самостоятельно, и вы можете гарантировать, что дубликатов не будет добавлено, бессмысленно использовать набор. Используйте собственное имя для передачи информации о коллекции. Кроме того, это хорошая идея, чтобы разоблачить его через интерфейс Collection, особенно если вызывающим абонентам вашего класса просто нужно перебирать коллекцию.