Использовать 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, особенно если вызывающим абонентам вашего класса просто нужно перебирать коллекцию.