Java编程手册-Collection框架(下)

时间:2022-02-05 19:20:47

建议先看Java编程手册-Collection框架(上)

5.  Set<E>接口与实现

Set<E>接口表示一个数学的集合,它不允许元素的重复,只能包含一个null元素。

Java编程手册-Collection框架(下)


Set<E>接口声明了下面抽象方法。

boolean add(E o)           // add the specified element if it is not already presentboolean remove(Object o)   // remove the specified element if it is present
boolean contains(Object o) // return true if it contains o

// Set operations
boolean addAll(Collection<? extends E> c) // Set union
boolean retainAll(Collection<?> c) // Set intersection

Set<E>接口的实现类包括:

  • HashSet<E>:在一个哈希表中存储元素(哈希值通过hashcode()产生),HashSet是Set的全面实现。
  • LinkedHashSet<E>:存储元素在一个哈希链表中,因此它有更高的插入删除效率。
  • TreeSet<E>: 它也实现了子接口NavigableSet 和 SortedSet,存储元素在一个红黑树的数据结构中,它的搜索、添加、删除效率很高,时间复杂度为O(log(n)
5.1  HashSet<E>的例子
下面写了一个Book类并且写了一个Book的集合。
public class Book {   private int id;   private String title;    // Constructor   public Book(int id, String title) {      this.id = id;      this.title = title;   }    @Override   public String toString() {      return id + ": " + title;   }    // Two book are equal if they have the same id   @Override   public boolean equals(Object o) {      if (!(o instanceof Book)) {         return false;      }      return this.id == ((Book)o).id;   }    // Consistent with equals(). Two objects which are equal have the same hash code.   @Override   public int hashCode() {      return id;   }}

我们需要提供equals()方法,这样实现Set的集合可以验证元素的相等性和重复性,例如,我们选用id来区分不同的对象元素,这样我们实现的equals()方法就是如果两个对象的id相同,那么就返回true。另外,我们也需要重新hashCode()方法来保持它也equals()的一致性。关于equals()和hashCode()的关联可以参考文章: Effective Java——对所有对象通用的方法
import java.util.HashSet;import java.util.Set;public class TestHashSet {   public static void main(String[] args) {      Book book1 = new Book(1, "Java for Dummies");      Book book1Dup = new Book(1, "Java for the Dummies"); // same id as above      Book book2 = new Book(2, "Java for more Dummies");      Book book3 = new Book(3, "more Java for more Dummies");       Set<Book> set1 = new HashSet<Book>();      set1.add(book1);      set1.add(book1Dup); // duplicate id, not added      set1.add(book1);    // added twice, not added      set1.add(book3);      set1.add(null);     // Set can contain a null      set1.add(null);     // but no duplicate      set1.add(book2);      System.out.println(set1); // [null, 1: Java for Dummies,                                //  2: Java for more Dummies, 3: more Java for more Dummies]       set1.remove(book1);      set1.remove(book3);      System.out.println(set1); // [null, 2: Java for more Dummies]       Set<Book> set2 = new HashSet<Book>();      set2.add(book3);      System.out.println(set2); // [3: more Java for more Dummies]      set2.addAll(set1);        // "union" with set1      System.out.println(set2); // [null, 2: Java for more Dummies, 3: more Java for more Dummies]       set2.remove(null);      System.out.println(set2); // [2: Java for more Dummies, 3: more Java for more Dummies]      set2.retainAll(set1);     // "intersection" with set1      System.out.println(set2); // [2: Java for more Dummies]   }}
一个Set不能存放重复的元素,检查元素的重复性就是通过重写的equal()方法来检查的。另外一个Set中只能存放一个null元素。addAll()是否是将两个Set联合,retainAll()是取两个Set的交集。

需要注意的是Set中元素的排序是任意的,并不是插入的顺序。

5.2  LinkedHashSet<E>的例子
不像HashSet, LinkedHashSet<E>构建的是一个基于哈希表的链表,主要是为了更好的插入和删除效率,另外,它里面元素的顺序维持的就是插入的顺序(例如:add()方法)。
import java.util.LinkedHashSet;import java.util.Set;public class TestLinkedHashSet {   public static void main(String[] args) {      Book book1 = new Book(1, "Java for Dummies");      Book book1Dup = new Book(1, "Java for the Dummies"); // same id as above      Book book2 = new Book(2, "Java for more Dummies");      Book book3 = new Book(3, "more Java for more Dummies");       Set<Book> set = new LinkedHashSet<Book>();      set.add(book1);      set.add(book1Dup); // duplicate id, not added      set.add(book1); // added twice, not added      set.add(book3);      set.add(null);  // Set can contain a null      set.add(null);  // but no duplicate      set.add(book2);      System.out.println(set);  // [1: Java for Dummies, 3: more Java for more Dummies,                                //  null, 2: Java for more Dummies]   }}

可以看到,Set的输出的顺序跟add()添加的顺序是一致的。

5.3  NavigableSet<E> & SortedSet<E>接口
SortedSet<E>在元素add()的过程中会进行排序,排序策略使用的是Comparable的实现或者提供的Comparator对象。
NavigableSet<E>是Set的子接口,它提供了一些额外的方法。
Iterator<E> descendingIterator()  // Returns an iterator over the elements in this set,                                  // in descending order.Iterator<E> iterator()   // Returns an iterator over the elements in this set, in ascending order. // Per-element operationE floor(E e)    // Returns the greatest element in this set less than or equal to the given element,                 // or null if there is no such element.E ceiling(E e)  // Returns the least element in this set greater than or equal to the given element,                 // or null if there is no such element.E lower(E e)    // Returns the greatest element in this set strictly less than the given element,                 // or null if there is no such element.E higher(E e)   // Returns the least element in this set strictly greater than the given element,                 // or null if there is no such element. // Subset operationSortedSet<E> headSet(E toElement) // Returns a view of the portion of this set                                   // whose elements are strictly less than toElement.SortedSet<E> tailSet(E fromElement) // Returns a view of the portion of this set                                    // whose elements are greater than or equal to fromElement.SortedSet<E> subSet(E fromElement, E toElement)                        // Returns a view of the portion of this set                       // whose elements range from fromElement, inclusive, to toElement, exclusive.
5.4  TreeSet<E>例子
TreeSet<E>是NavigableSet<E> 和 SortedSet<E>的实现.
例子——实现Comparable的TreeSet

public class AddressBookEntry implements Comparable<AddressBookEntry> {   private String name, address, phone;    public AddressBookEntry(String name) {      this.name = name;   }    @Override   public String toString() {      return name;   }    @Override   public int compareTo(AddressBookEntry another) {      return this.name.compareToIgnoreCase(another.name);   }    @Override   public boolean equals(Object o) {      if (!(o instanceof AddressBookEntry)) {         return false;      }      return this.name.equalsIgnoreCase(((AddressBookEntry)o).name);   }    @Override   public int hashCode() {      return name.length();   }}
AddressBookEntry实现了Comparable接口,为了在TreeSet中进行使用,它重写了 compareTo() 方法去比较name,它同时也重新了equals()和hashCode()方法,主要为了保持跟compareTo() 的一致性。
import java.util.TreeSet; public class TestTreeSetComparable {   public static void main(String[] args) {      AddressBookEntry addr1 = new AddressBookEntry("peter");      AddressBookEntry addr2 = new AddressBookEntry("PAUL");      AddressBookEntry addr3 = new AddressBookEntry("Patrick");       TreeSet<AddressBookEntry> set = new TreeSet<AddressBookEntry>();      set.add(addr1);      set.add(addr2);      set.add(addr3);      System.out.println(set); // [Patrick, PAUL, peter]       System.out.println(set.floor(addr2));   // PAUL      System.out.println(set.lower(addr2));   // Patrick      System.out.println(set.headSet(addr2)); // [Patrick]      System.out.println(set.tailSet(addr2)); // [PAUL, peter]   }}
可以看到 AddressBookEntry对象在add()操作过程中进行了排序,使用的就是Comparable实现的方法。

例子——实现ComparatorTreeSet
下面我们来重新上面的例子,这次使用的是Comparator对象来实现对象的比较。
public class PhoneBookEntry {   public String name, address, phone;    public PhoneBookEntry(String name) {      this.name = name;   }    @Override   public String toString() {      return name;   }}

上面的 PhoneBookEntry并没有实现Comparator,我们这里是单独定义一个Comparator实例,使用这个实例来创建TreeSet。
import java.util.Set;import java.util.TreeSet;import java.util.Comparator; public class TestTreeSetComparator {   public static class PhoneBookComparator implements Comparator<PhoneBookEntry> {      @Override      public int compare(PhoneBookEntry p1, PhoneBookEntry p2) {         return p2.name.compareToIgnoreCase(p1.name);  // descending name      }   }    public static void main(String[] args) {      PhoneBookEntry addr1 = new PhoneBookEntry("peter");      PhoneBookEntry addr2 = new PhoneBookEntry("PAUL");      PhoneBookEntry addr3 = new PhoneBookEntry("Patrick");       Comparator<PhoneBookEntry> comp = new PhoneBookComparator();      TreeSet<PhoneBookEntry> set = new TreeSet<PhoneBookEntry>(comp);      set.add(addr1);      set.add(addr2);      set.add(addr3);      System.out.println(set);    // [peter, PAUL, Patrick]       Set<PhoneBookEntry> newSet = set.descendingSet();  // Reverse the order      System.out.println(newSet); // [Patrick, PAUL, peter]   }}
上面我们创建了一个带有 BookComparator实例的TreeSet,这样上面Set中对象的比较使用的就是BookComparator的compare方法,在上面例子中,调用TreeSet的descendingSet()方法来是集合倒序。

6.  Queue<E>接口与实现
Queue中的元素是以一种特定的顺序进行添加和删除,例如典型的先入先出。deque是一个双向队列,它的元素的添加和删除可以在两端进行。
Java编程手册-Collection框架(下)
Collection<E>操作之外,Queue<E> 也额外添加了插入、获取、查看的操作方法,每个方法都提供了两种形式:一个是如果操作失败会抛出异常,另外一个如果操作是否会返回一个值(false或者null,据具体操作而定)。
// Insertion at the end of the queueboolean add(E e)   // throws IllegalStateException if no space is currently availableboolean offer(E e) // returns true if the element was added to this queue, else false // Extract element at the head of the queueE remove()         // throws NoSuchElementException if this queue is emptyE poll()           // returns the head of this queue, or null if this queue is empty // Inspection (retrieve the element at the head, but does not remove)E element()        // throws NoSuchElementException if this queue is emptyE peek()           // returns the head of this queue, or null if this queue is empty

Deque<E>也提供了额外的方法来操作队列的两端
// Insertionvoid addFirst(E e)void addLast(E e)boolean offerFirst(E e)boolean offerLast(E e) // Retrieve and RemoveE removeFirst()E removeLast()E pollFirst()E pollLast() // Retrieve but does not removeE getFirst()E getLast()E peekFirst()E peekLast()
一个 Deque可以看做是一个FIFO对象(通过add(e)remove()element()offer(e)poll()peek()方法),也可以看做是一个LIFO队列(通过push(e)pop()peek()方法)。
Queue<E> 和 Deque<E>的实现类如下:
  • PriorityQueue<E>: 这个队列可以使用一个指定的顺序去进行排序,而不是FIFO的顺序。
  • ArrayDeque<E>: 使用queue或者deque来实现的动态数组,类似于ArrayList<E>。
  • LinkedList<E>: 它在实现List<E>接口的基础上,也实现了Queue<E> 和 Deque<E>接口,它是双向链表结构。

7.  Map<K,V>接口与实现
Map集合对应的是一个键值对,每个key对应一个value,键值不允许重复,value是允许重复的,它跟线性数组有些相同,不同的是它使用key来进行索引访问对应的value,所以map使用使用任意的key。
Java编程手册-Collection框架(下)
 Map<K,V>接口声明了下面抽象方法。
V get(Object key)      // Returns the value of the specified keyV put(K key, V value)  // Associate the specified value with the specified keyboolean containsKey(Object key)  // Is this map has specified key?boolean containsValue(Object value)// ViewsSet<K> keySet()         // Returns a set view of the keysCollection<V> values()  // Returns a collection view of the valuesSet entrySet()          // Returns a set view of the key-value
Map<K,V>接口的实现类包括:
  • HashMap<K,V>:HashMap实现了Map<K,V>,并且是Map全面的实现,但是HashMap的方法不是同步的,也就是线程不安全。
  • TreeMap<K,V>:实现了SortedMap<K,V>接口的红黑树。
  • LinkedHashMap<K,V>:带有链表的哈希表,方便插入和删除
  • Hashtable<K,V>:它是对Map<K,V>方法的同步实现,也就是它是线程安全的,另外它不允许key和value为null。
例子:
HashMap<String, String> aMap = new HashMap<String, String>();aMap.put("1", "Monday");aMap.put("2", "Tuesday");aMap.put("3", "Wednesday");String str1 = aMap.get("1");  // No need downcastSystem.out.println(str1);String str2 = aMap.get("2");System.out.println(str2);String str3 = aMap.get("3");System.out.println(str3);Set<String> keys = aMap.keySet();for (String str : keys) {   System.out.print(str);   System.out.print(":");   System.out.println(aMap.get(str));}

例子——HashMap
// Counts the frequency of each of the words in a file given in the command-line,// and saves in a map of {word, freq}.import java.util.Map;import java.util.HashMap;import java.util.Scanner;import java.io.File; public class WordCount {   public static void main(String[] args) throws Exception {      Scanner in = new Scanner(new File(args[0]));       Map<String, Integer> map = new HashMap<String, Integer>();      while (in.hasNext()) {         String word = in.next();         int freq = (map.get(word) == null) ? 1 : map.get(word) + 1;   // type-safe         map.put(word, freq);      // autobox int to Integer and upcast, type-check      }      System.out.println(map);   }}

8. 算法
Collection框架提供了两个工具类:java.util.Arrays 和 java.util.Collections,它们提供了一些基本的算法,例如插入和查找等等。
另外需要注意的是Collections是工具类,Collection是集合框架的接口。

8.1  java.util.Arrays工具类
java.util.Arrays包含了很多的静态方法来对数组进行排序或者查找等等操作。
Array是Java中的引用类型,它可以包含基本数据类型,也可以包含对象数据类型,Java中的数组可以包含9种数据类型,byteshortintlongfloatdoublecharboolean和对象类型。

Sorting - Arrays.sort()
对除boolean类型之外的基本数据类型以及对象类型的数据进行排序,例如int[]数组:
// Sort the given array into ascending orderpublic static void sort(int[] a)// Sort between fromIndex (inclusive) and toTodex (exclusive)public static void sort(int[] a, int fromIndex, int toIndex)
同理, sort()可用于byte[]short[]long[]float[]double[]char[] (除 boolean[]外) and Object[]中。对应Object[],对象必须实现Comparable接口的compareTo()方法。
public static void sort(Object[] a)public static void sort(Object[] a, int fromIndex, int toIndex)
对于泛型对象的排序,通过提供 Comparator对象来实现排序。
public static <T> void sort(T[] a, Comparator<? super T> c)public static <T> void sort(T[] a, int fromIndex, int toIndex, Comparator<? super T> c)
假设你想对一个 Integer数组进行排序,也就是T为Integer,你可以实现一个Comparator<Integer>或者Comparator<Number>或者Comparator<Object>实例来提供给sort方法,Object and Number都是Integer的超类。

Searching - Arrays.binarySearch()
同理,也有对于所有基本数据类型或者对象类型查找的方法,在执行binarySearch() 执行对数组需要进行排序。
public static int binarySearch(int[] a, int key)public static int binarySearch(int[] a, int fromIndex, int toIndex, int key)// Similar methods for byte[], short[], long[], float[], double[] and char[] // Searching objects, which implements Comparablepublic static int binarySearch(Object[] a, Object key)public static int binarySearch(Object[] a, int fromIndex, int toIndex, Object key)// Searching generic objects, based on the given Comparatorpublic static <T> int binarySearch(T[] a, T key, Comparator<? super T> c)public static <T> int binarySearch(T[] a, T key, int fromIndex, int toIndex, Comparator<? super T> c)

Equality Comparison - Arrays.equals()
public static boolean equals(int[] a1, int[] a2)// Similar methods for byte[], short[], long[], float[], double[], char[], boolean[] and Object[]

Copying - Arrays.copyOf() 和 Arrays.copyOfRange()
public static int[] copyOf(int[] original, int newLength)  // Copies the given array, truncating or padding with zeros (if necessary) so the copy has the specified lengthpublic static int[] copyOfRange(int[] original, int from, int to)  // padded with 0 if to is beyond the length // Similar methods for byte[], short[], long[], float[], double[], char[] and boolean[]public static <T> T[] copyOf(T[] original, int newLength)public static <T> T[] copyOfRange(T[] original, int from, int to)public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType)public static <T,U> T[] copyOfRange(U[] original, int from, int to, Class<? extends T[]> newType)

Filling - Arrays.fill()
public static void fill(int[] a, int value)public static void fill(int[] a, int fromIndex, int toIndex, int value)// Similar methods for byte[], short[], long[], float[], double[], char[] and boolean[] and Object[]

Description - Arrays.toString()
// Returns a string representation of the contents of the specified array.public static String toString(int[] a)// Similar methods for byte[], short[], long[], float[], double[], char[] and boolean[] and Object[]

转换为List - Arrays.asList()
// Returns a fixed-size list backed by the specified array.// Change to the list write-thru to the array.public static <T> List<T> asList(T[] a)

8.2  java.util.Collections工具类
java.util.Arrays一样,java.util.Collections也提供了静态的方法来对集合进行操作。、

Sorting - Collections.sort()
// Sorts the specified list into ascending order. The objects shall implement Comparable. public static <T extends Comparable<? super T>> void sort(List<T> list)// Sorts the specified list according to the order induced by the specified comparator.public static <T> void sort(List<T> list, Comparator<? super T> c)
需要注意的是 Collections.sort()只能用在List上面,不能使用在SetQueue 和 Map上,SortedSet (TreeSet) 和 SortedMap(TreeMap)可以自动排序。

Searching - Collections.binarySearch()
在使用binarySearch()之前,List必须进行排序。
public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key)public static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c)

最大和最小 - Collections.max() & Collections.min()
// Returns the maximum/minimum element of the given collection, according to the natural ordering of its elements.public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> c)public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> c) // Returns the maximum/minimum element of the given collection, according to the order induced by the specified comparator.public static <T> T max(Collection<? extends T> c, Comparator<? super T> comp)public static <T> T min(Collection<? extends T> c, Comparator<? super T> comp)

Synchronized Collection, List, Set & Map
很多的Collection的实现类都不是同步的,例如ArrayListHashSet 和  HashMap ,也就是它们不是线程安全的,除了Vector 和 HashTable是同步的,如果我们不想使用Vector 和 HashTable,我们可以通过静态的 Collections.synchronizedXxx() 创建同步的CollectionListSetSortedSetMap 和 SortedMap。
// Returns a synchronized (thread-safe) collection backed by the specified collection.public static <T> Collection<T> synchronizedCollection(Collection<T> c)// Otherspublic static <T> List<T> synchronizedList(List<T> list)public static <T> Set<T> synchronizedSet(Set<T> set)public static <T> SortedSet<T> synchronizedSortedSet(SortedSet<T> set)public static <K,V> Map<K,V> synchronizedMap(Map<K,V> map)public static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> map)
对于上面方法返回的对象,在遍历的时候,必须包含在 synchronize块中。
List lst = Collections.synchronizedList(new ArrayList());   ......synchronized(lst) {  // must be enclosed in a synchronized block   Iterator iter = lst.iterator();    while (iter.hasNext())     iter.next();     ......}

原文链接: Java Programming Tutorial The Collection Framework

Java编程手册-Collection框架(下)

欢迎关注我的公众号:DroidMind

精品内容,独家发布