Java集合(五):Set集
在上一講中介紹了散列映射表HashMap和樹映射表TreeMap,知道了HashMap的底層實現機制。這一講將介紹Set接口和實現類:HashSet和TreeSet。由于HashSet的實現是基于HashMap的,TreeSet的實現是基于TreeMap的,所以這里不做過多底層的討論,畢竟這部分已經在Java集合(四):Map映射中討論過了。
1 散列表與Set接口
鏈表和數組可以按照人們的意愿排列元素的順序。但是,如果想要查看某個指定的元素,但卻忘了它的位置,就需要訪問所有的元素,直到找到為止。如果集合中的元素很多,將會消耗很長時間。如果不在意元素的順序,可以有幾種能夠快速查找元素的數據結構。但是缺點是不能控制元素的順序。它們將按照有利于其操作目的的原則組織數據。
有一種常見的數據結構,就是散列表(hash table)。散列表可以根據每個對象計算一個整數,稱為散列碼(hash code)。不同的對象產生不同的散列碼。
在Java中,散列表用鏈表數組實現。每個列表稱為桶,這個已經在Map映射中介紹了。
散列表的特點就是:元素沒有順序,元素不能重復。
Set接口的定義如下:
public interface java.util.Set<E> extends java.util.Collection<E> {public abstract int size();public abstract boolean isEmpty();public abstract boolean contains(java.lang.Object);public abstract java.util.Iterator<E> iterator();public abstract java.lang.Object[] toArray();public abstract <T> T[] toArray(T[]);public abstract boolean add(E);public abstract boolean remove(java.lang.Object);public abstract boolean containsAll(java.util.Collection<?>);public abstract boolean addAll(java.util.Collection<? extends E>);public abstract boolean retainAll(java.util.Collection<?>);public abstract boolean removeAll(java.util.Collection<?>);public abstract void clear();public abstract boolean equals(java.lang.Object);public abstract int hashCode();public java.util.Spliterator<E> spliterator(); }這些方法的含義也很簡單。2 HashSet類
Java集合類庫中提供了HashSet類實現了Set接口。這個類的底層是使用HashMap實現的:
private transient HashMap<E,Object> map;// Dummy value to associate with an Object in the backing Map private static final Object PRESENT = new Object();我們知道,HashMap是一個鍵值對,而HashSet存儲的不是鍵值對,因此為了使用HashMap存儲HashSet的元素,就需要構造一個鍵值對。可以使用需要存儲在HashSet中的元素作為鍵,而上面的PRESENT作為值,就構成了一個鍵值對,這樣就可以存在HashMap中了。
也就是說,HashSet與HashMap的原理一樣,不同的是HashSet的值都一樣,都是PRESENT。
由于在前一節中已經詳細介紹了HashMap的原理,這里不再敘述了。只說一下HashSet的使用。
下面的代碼從System.in中讀取單詞,然后將它們添加到HashSet中,再打印出所有的單詞。由于HashSet中不存儲相同的元素,所以打印出來的單詞是不重復的。運行這個程序時使用下面的命令行:
java SetTest < alice.txt這樣就把alice.txt作為輸入,程序就會讀取所有的單詞。代碼如下:
import java.util.*;public class SetTest {public static void main(String[] args){Set<String> words = new HashSet<>(); // HashSet implements Setlong totalTime = 0;Scanner in = new Scanner(System.in);while (in.hasNext()){String word = in.next();long callTime = System.currentTimeMillis();words.add(word);callTime = System.currentTimeMillis() - callTime;totalTime += callTime;}Iterator<String> iter = words.iterator();for (int i = 1; i <= 20 && iter.hasNext(); i++)System.out.println(iter.next());System.out.println(". . .");System.out.println(words.size() + " distinct words. " + totalTime + " milliseconds.");} }結果如下:
可以看見,一共有5392個不同的單詞。
3 TreeSet類
TreeSet和HashSet類似,不過,它比HashSet有所改進。TreeSet是一個有序集合,可以以任意順序將元素插入到集合中。在對集合進行遍歷時,每個值將自動按照排序后的順序呈現。例如,假設插入三個字符串,然后訪問添加的所有元素:
SortedSet<String> sorter=new TreeSet<>(); sorter.add("B"); sorter.add("A"); sorter.add("C"); for(String s:sorter)System.out.println(s);結果是:A B C
TreeSet的底層是使用TreeMap實現的,是一個紅黑樹。每次添加一個元素到樹中時,都被放置在正確的排序位置上。因此,迭代器總是以排好序的順序訪問每個元素。
將一個元素添加到樹中要比添加到一個散列表中要慢,但是,與將元素添加到數組中或鏈表中要快。如果樹中一共有n個元素,將元素插入到正確位置的時間為logn。
與TreeMap一樣,構造一個TreeSet也需要一個比較器,可以使用默認的比較器,也可以使用自己的比較器。使用自己的比較器時,需要給TreeSet的構造器傳遞一個Comparator對象。
下面的程序創建了兩個Item對象的樹集。第一個按照部件編號排序,這是Item對象的默認順序。第二個通過使用一個定制的比較器來按照描述信息排序:
import java.util.*;public class TreeSetTest { public static void main(String[] args){ SortedSet<Item> parts = new TreeSet<>();parts.add(new Item("Toaster", 1234));parts.add(new Item("Widget", 4562));parts.add(new Item("Modem", 9912));System.out.println(parts);SortedSet<Item> sortByDescription = new TreeSet<>(newComparator<Item>(){ public int compare(Item a, Item b){ String descrA = a.getDescription();String descrB = b.getDescription();return descrA.compareTo(descrB);}});sortByDescription.addAll(parts);System.out.println(sortByDescription);} }import java.util.*;/*** An item with a description and a part number.*/ public class Item implements Comparable<Item> {private String description;private int partNumber;/*** Constructs an item.* * @param aDescription* the item's description* @param aPartNumber* the item's part number*/public Item(String aDescription, int aPartNumber){description = aDescription;partNumber = aPartNumber;}/*** Gets the description of this item.* * @return the description*/public String getDescription(){return description;}public String toString(){return "[\n\tdescripion=" + description + ",\n\tpartNumber=" + partNumber +"\n]\n";}public boolean equals(Object otherObject){if (this == otherObject) return true;if (otherObject == null) return false;if (getClass() != otherObject.getClass()) return false;Item other = (Item) otherObject;return Objects.equals(description, other.description) && partNumber == other.partNumber;}public int hashCode(){return Objects.hash(description, partNumber);}public int compareTo(Item other){return Integer.compare(partNumber, other.partNumber);} }
結果如下:
總結
以上是生活随笔為你收集整理的Java集合(五):Set集的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java集合(四):Map映射
- 下一篇: Java集合(六):专用集合和遗留类