HashSet vs ArrayList содержит производительность
при обработке больших объемов данных я часто делаю следующее:
HashSet<String> set = new HashSet<String> ();
//Adding elements to the set
ArrayList<String> list = new ArrayList<String> (set);
что-то вроде "сброса" содержимого набора в списке. Я обычно делаю это, так как элементы, которые я добавляю, часто содержат дубликаты, которые я хочу удалить, и это кажется простым способом их удаления.
имея в виду только эту цель (избегая дубликатов), я мог бы также написать:
ArrayList<String> list = new ArrayList<String> ();
// Processing here
if (! list.contains(element)) list.add(element);
//More processing here
и "сброс" установить в список. Однако, Я бы сделал небольшую проверку перед вставкой каждого элемента (что, я предполагаю, делает и HashSet)
является ли какая-либо из двух возможностей более эффективной?
5 ответов
набор даст гораздо лучшую производительность (O(n)
vs O(n^2)
для списка), и это нормально, потому что установить принадлежность (contains
операции) является цель набора.
содержится в HashSet
is O(1)
по сравнению с O(n)
для списка, поэтому вы никогда не должны использовать список, если вам часто приходится работать contains
.
на ArrayList
использует массив для хранения данных. The ArrayList.contains
будет иметь сложность O(n). Поэтому по существу поиск в массиве снова и снова будет иметь O(n^2)
сложности.
пока HashSet
использует механизм хэширования для хранения элементов в соответствующих ведрах. Операция HashSet
будет быстрее для длинного списка значений. Он достигнет элемента в O(1)
.
Если вам не нужен список, я бы просто использовал набор, и это естественная коллекция для использования, если порядок не имеет значения, и вы хотите игнорировать дубликаты.
вы можете сделать как вам нужен список без дубликатов.
private Set<String> set = new HashSet<>();
private List<String> list = new ArrayList<>();
public void add(String str) {
if (set.add(str))
list.add(str);
}
таким образом, список будет содержать только уникальные значения, исходный порядок вставки сохраняется и операция O(1).
Я сделал тест, поэтому, пожалуйста, проверьте результат:
для тех же строковых элементов в HashSet, TreeSet, ArrayList и LinkedList, вот результаты для
- 50.000 идентификаторы UUID
- искомый предмет : e608c7d5-c861-4603-9134-8c636a05a42b (индекс 25.000)
- поиска HashSet.содержит(пункт) ? TRUE 0 ms
- treeSet.содержит(пункт) ? TRUE 0 ms
- класса ArrayList.содержит(пункт) ? Правда 2 мс
- linkedList.содержит(пункт) ? TRUE 3 ms
- 5.000.000 идентификаторы UUID
- искомый предмет: 61fb2592-3186-4256-a084-6c96f9322a86 (индекс 25.000)
- поиска HashSet.содержит(пункт) ? TRUE 0 ms
- treeSet.содержит(пункт) ? TRUE 0 ms
- класса ArrayList.содержит(пункт) ? TRUE 1 ms
- linkedList.содержит(пункт) ? Правда 2 мс
- 5.000.000 идентификаторы UUID
- искомый элемент: db568900-c874-46ba-9b44-0e1916420120 (индекс 2.500.000)
- поиска HashSet.содержит(пункт) ? TRUE 0 ms
- treeSet.содержит(пункт) ? TRUE 0 ms
- класса ArrayList.содержит(пункт) ? Правда 33 мс
- linkedList.содержит(пункт) ? Правда 65 мс
основываясь на приведенных выше результатах, нет большой разницы в использовании списка массивов против набора. Возможно, вы можете попробовать изменить этот код и заменить строка с объект и увидеть различия затем...
public static void main(String[] args) {
Set<String> hashSet = new HashSet<>();
Set<String> treeSet = new TreeSet<>();
List<String> arrayList = new ArrayList<>();
List<String> linkedList = new LinkedList<>();
List<String> base = new ArrayList<>();
for(int i = 0; i<5000000; i++){
if(i%100000==0) System.out.print(".");
base.add(UUID.randomUUID().toString());
}
System.out.println("\nBase size : " + base.size());
String item = base.get(25000);
System.out.println("SEARCHED ITEM : " + item);
hashSet.addAll(base);
treeSet.addAll(base);
arrayList.addAll(base);
linkedList.addAll(base);
long ms = System.currentTimeMillis();
System.out.println("hashSet.contains(item) ? " + (hashSet.contains(item)? "TRUE " : "FALSE") + (System.currentTimeMillis()-ms) + " ms");
System.out.println("treeSet.contains(item) ? " + (treeSet.contains(item)? "TRUE " : "FALSE") + (System.currentTimeMillis()-ms) + " ms");
System.out.println("arrayList.contains(item) ? " + (arrayList.contains(item)? "TRUE " : "FALSE") + (System.currentTimeMillis()-ms) + " ms");
System.out.println("linkedList.contains(item) ? " + (linkedList.contains(item)? "TRUE " : "FALSE") + (System.currentTimeMillis()-ms) + " ms");
}
вы можете добавить элементы в сам список. Затем, чтобы дедуп -
HashSet<String> hs = new HashSet<>(); // new hashset
hs.addAll(list); // add all list elements to hashset (this is the dedup, since addAll works as a union, thus removing all duplicates)
list.clear(); // clear the list
list.addAll(hs); // add all hashset elements to the list
Если вам просто нужен набор с dedup, вы также можете использовать addAll () на другом наборе, так что он будет иметь только уникальные значения.