Java集合基礎

真是好久沒來寫東西了,看的東西學的東西也不少,但是說沒時間也不至於,雖然最近996,只是自己懶癌犯了罷。前天學東西睡覺前突然又跳出個想法:技術學得再好又能怎麼樣呢,只要沒達到頂峯,貌似在目前的情況下會的東西再多再好,都沒有什麼用,理解業務,安排計劃完成基礎編碼就行了,想多了反而會出問題,導致開發變慢,呵呵了。但我還是不能放棄啊……


這兩天把集合框架系統的看了一遍,也是簡單的看看,以前一直都沒有系統學過集合框架,只是在用HashMap和ArrayList,偶爾使用HashSet,這兩天終於下定決心好好學學了,雖然每天也是要搞到一兩點。看完還是覺得挺好的。兩天時間把筆記簡單的整理了下,說是整理,其實就是把書上的內容敲到電腦上罷了。在這裏粘貼一份吧。


筆記:


爲了保存數量不確定的數據,以及保存具有映射關係的數據(也被稱爲關聯數組)。Java提供集合類,集合類主要負責保存、盛裝其他數據,因此集合類也被稱爲容器類。所有集合類都位於java.util包下。後來爲了處理多線程情況下的併發安全問題,java5在java.util.concurrent包下提供了一些多線程支持的集合類。

Java集合類是一種工具類(特別有用)。Java集合大致可分爲Set、List、Queue、Map四種體系。
Set代表無序、不可重複的集合;
List代表有序、可重複的集合;
Map代表具有映射關係的集合;
Queue代表一種隊列集合實現(Java5新增的)


Java的集合主要是由兩個接口派生而出:Collection和Map。(查閱java.util包)
Collection集合體系:

Map集合體系:

最常用的實現類:HashMap、ArrayList、HashSet及次之的LinkedList、TreeSet、ArrayDeque、TreeMap

Collection接口
看API文檔
public interface Collection<E> extends Iterable<E>

所有的方法:無非是增刪改查。
方法摘要
boolean add(E e)
該方法用於向集合中添加一個元素, 如果此 collection 由於調用而發生更改,則返回 true,很多情況下會拋出異常,詳看文檔
boolean addAll(Collection<? extends E> c)
將集合c中的所有元素都添加到此 collection 中。
void clear()
移除此集合中的所有元素,集合長度將變爲0。
boolean contains(Object o)
如果此 collection 包含指定的元素,則返回 true。
boolean containsAll(Collection<?> c)
如果此集合包含集合c中的所有元素,則返回 true。
boolean equals(Object o)
比較此 collection 與指定對象是否相等。
int hashCode()
返回此 collection 的哈希碼值。
boolean isEmpty()
如果此 collection 不包含元素,則返回 true。
Iterator<E> iterator()
返回在此集合上的迭代器。 關於元素返回的順序沒有任何保證(除非此 collection 是某個能提供保證順序的類實例),此方法來自於Iterable<E> 接口。
boolean remove(Object o)
從此集合中移除指定元素o,如果此集合中有多個元素o,則只會刪除第一個。
boolean removeAll(Collection<?> c)
從此集合中刪除集合c中的所有元素(差集), 如果此 collection 由於調用而發生更改,則返回 true
boolean retainAll(Collection<?> c)
僅保留此集合中那些也包含在指定集合c中的元素(交集)。
int size()
返回此 collection 中的元素數。
Object[] toArray()
把集合轉換成一個數組。
<T> T[] toArray(T[] a)
返回包含此 collection 中所有元素的數組;返回數組的運行時類型與指定數組的運行時類型相同。

示例代碼:
import java.util.*;

public class CollectionTest
{
public static void main(String[] args)
{
Collection c = new ArrayList();
// 添加元素
c.add("孫悟空");
// 雖然集合裏不能放基本類型的值,但Java支持自動裝箱
c.add(6);
System.out.println("c集合的元素個數爲:" + c.size()); // 輸出2
// 刪除指定元素
c.remove(6);
System.out.println("c集合的元素個數爲:" + c.size()); // 輸出1
// 判斷是否包含指定字符串
System.out.println("c集合的是否包含\"孫悟空\"字符串:"
+ c.contains("孫悟空")); // 輸出true
c.add("輕量級Java EE企業應用實戰");
System.out.println("c集合的元素:" + c);
Collection books = new HashSet();
books.add("輕量級Java EE企業應用實戰");
books.add("瘋狂Java講義");
System.out.println("c集合是否完全包含books集合?"
+ c.containsAll(books)); // 輸出false
// 用c集合減去books集合裏的元素
c.removeAll(books);
System.out.println("c集合的元素:" + c);
// 刪除c集合裏所有元素
c.clear();
System.out.println("c集合的元素:" + c);
// 控制books集合裏只剩下c集合裏也包含的元素
books.retainAll(c);
System.out.println("books集合的元素:" + books);
}
}

集合的遍歷
方法一、原來只能通過iterator()方法獲取集合的迭代器後再進行遍歷的,現在Java8給Iterable接口新增了一個forEach(Consumer<? super T> action)方法,而Collection又是實現了Iterable接口的,該方法所需的參數是一個函數式接口(只有一個抽象方法),因此可以用Lambda表達式。當調用Iterable的forEach(Consumer action)遍歷集合時,程序會依次將集合元素傳遞給Consumer的accept(T t)方法。

示例代碼:

import java.util.*;

public class CollectionEach
{
public static void main(String[] args)
{
// 創建一個集合
Collection books = new HashSet();
books.add("輕量級Java EE企業應用實戰");
books.add("瘋狂Java講義");
books.add("瘋狂Android講義");
// 調用forEach()方法遍歷集合
books.forEach(obj -> System.out.println("迭代集合元素:" + obj));
}
}

遍歷方法二、使用傳統的Iterator。
Iterator接口隱藏了各種Collection實現類的底層細節,向外提供了遍歷Collection元素的統一編程接口。
Iterator接口也是Java集合框架的成員,但它與Collection系列、Map系列的集合不一樣:Collection系列集合、Map系列集合主要用於盛裝其他對象,而Iterator則主要用於遍歷(即迭代訪問)Collection集合中的元素,Iterator對象也被稱爲迭代器。
public interface Iterator<E>
方法摘要
boolean hasNext()
如果仍有元素可以迭代,則返回 true。
E next()
返回迭代的下一個元素。
void remove()
從迭代器指向的集合中移除迭代器返回的最後一個元素(上一次next返回的元素)。
default void forEachRemaining(Consumer<? super E> action)
Performs the given action for each remaining element until all elements have been processed or the action throws an exception.
這是Java8新增的方法,可以用Lambda表達式來遍歷。

示例代碼:

import java.util.*;

public class IteratorTest
{
public static void main(String[] args)
{
// 創建集合、添加元素的代碼與前一個程序相同
Collection books = new HashSet();
books.add("輕量級Java EE企業應用實戰");
books.add("瘋狂Java講義");
books.add("瘋狂Android講義");
// 獲取books集合對應的迭代器
Iterator it = books.iterator();
while(it.hasNext())
{
// it.next()方法返回的數據類型是Object類型,因此需要強制類型轉換
String book = (String)it.next();
System.out.println(book);
if (book.equals("瘋狂Java講義"))
{
// 從集合中刪除上一次next方法返回的元素
it.remove();
// 使用Iterator迭代過程中,不可修改集合元素,下面代碼引發異常
books.remove(book); //此行代碼將引發ConcurrentModificationException異常
}
// 對book變量賦值,不會改變集合元素本身
book = "測試字符串"; //①
}
System.out.println(books);
}
}

使用forEachRemaining(Consumer<? super E> action)遍歷集合:

import java.util.*;

public class IteratorEach
{
public static void main(String[] args)
{
// 創建集合、添加元素的代碼與前一個程序相同
Collection books = new HashSet();
books.add("輕量級Java EE企業應用實戰");
books.add("瘋狂Java講義");
books.add("瘋狂Android講義");
// 獲取books集合對應的迭代器
Iterator it = books.iterator();
// 使用Lambda表達式(目標類型是Comsumer)來遍歷集合元素
it.forEachRemaining(obj -> System.out.println("迭代集合元素:" + obj));
}
}


遍歷方法三:使用Java5新增的foreach遍歷也挺簡單的
示例代碼:

import java.util.*;

public class ForeachTest
{
public static void main(String[] args)
{
// 創建集合、添加元素的代碼與前一個程序相同
Collection books = new HashSet();
books.add(new String("輕量級Java EE企業應用實戰"));
books.add(new String("瘋狂Java講義"));
books.add(new String("瘋狂Android講義"));
for (Object obj : books)
{
// 此處的book變量也不是集合元素本身
String book = (String)obj;
System.out.println(book);
if (book.equals("瘋狂Android講義"))
{
// 下面代碼會引發ConcurrentModificationException異常
books.remove(book); //①
}
}
System.out.println(books);
}
}

注意:1、和使用迭代器一樣,循環中的迭代變量也不是集合元素本身,只是以此把集合元素的賦給迭代變量,對此變量進行修改不會影響集合本身。2、循環時不能改變集合,否則將引發ConcurrentModificationException異常


Java8爲Collection集合新增了一個removeIf(Predicate filter)方法,該方法將會批量刪除符合filter條件的所有元素。該方法的參數是一個Predicate對象,Predicate是一個函數式接口,可使用Lambda表達式。

示例代碼:

import java.util.*;
import java.util.function.*;

public class PredicateTest
{
public static void main(String[] args)
{
// 創建一個集合
Collection books = new HashSet();
books.add(new String("輕量級Java EE企業應用實戰"));
books.add(new String("瘋狂Java講義"));
books.add(new String("瘋狂iOS講義"));
books.add(new String("瘋狂Ajax講義"));
books.add(new String("瘋狂Android講義"));
// 使用Lambda表達式(目標類型是Predicate)過濾集合
books.removeIf(ele -> ((String)ele).length() < 10);
System.out.println(books);
}
}

使用Predicate可以簡化集合的運算:將原來需要多次循環才能完成的事變成使用一個方法即可,
示例代碼:(三個統計需求)

import java.util.*;
import java.util.function.*;

public class PredicateTest2
{
public static void main(String[] args)
{
// 創建books集合、爲books集合添加元素的代碼與前一個程序相同。
Collection books = new HashSet();
books.add(new String("輕量級Java EE企業應用實戰"));
books.add(new String("瘋狂Java講義"));
books.add(new String("瘋狂iOS講義"));
books.add(new String("瘋狂Ajax講義"));
books.add(new String("瘋狂Android講義"));
// 統計書名包含“瘋狂”子串的圖書數量
System.out.println(calAll(books , ele->((String)ele).contains("瘋狂")));
// 統計書名包含“Java”子串的圖書數量
System.out.println(calAll(books , ele->((String)ele).contains("Java")));
// 統計書名字符串長度大於10的圖書數量
System.out.println(calAll(books , ele->((String)ele).length() > 10));
}
public static int calAll(Collection books , Predicate p)
{
int total = 0;
for (Object obj : books)
{
// 使用Predicate的test()方法判斷該對象是否滿足Predicate指定的條件
if (p.test(obj))
{
total ++;
}
}
return total;
}
}


Java 8新增了Stream、IntStream、LongStream、DoubleStream等流式API來操作集合,這些API代表多個支持串行和並行聚集操作的元素。Stream是一個通用的流接口。
Stream等流式API貌似沒有構造函數,但是Java8爲每個API提供了對應的Builder,如Stream.Builder、IntStream.Builder,只能通過這些Builder來創建對應的流。
獨立使用Stream的步驟如下:
(1)使用Stream或XxxStream的builder()類方法創建該Stream對應Builder。
(2)重複調用Builder的add()方法向該流中添加多個元素。
(3)調用Builder的build()方法獲取對應的Stream。
(4)調用Stream的聚集方法。

示例代碼:

import java.util.stream.*;

public class IntStreamTest
{
public static void main(String[] args)
{
IntStream is = IntStream.builder()
.add(20)
.add(13)
.add(-2)
.add(18)
.build();
// 下面調用聚集方法的代碼每次只能執行一個
System.out.println("is所有元素的最大值:" + is.max().getAsInt());
System.out.println("is所有元素的最小值:" + is.min().getAsInt());
System.out.println("is所有元素的總和:" + is.sum());
System.out.println("is所有元素的總數:" + is.count());
System.out.println("is所有元素的平均值:" + is.average());
System.out.println("is所有元素的平方是否都大於20:"
+ is.allMatch(ele -> ele * ele > 20));
System.out.println("is是否包含任何元素的平方大於20:"
+ is.anyMatch(ele -> ele * ele > 20));
// 將is映射成一個新Stream,新Stream的每個元素是原Stream元素的2倍+1
IntStream newIs = is.map(ele -> ele * 2 + 1);
// 使用方法引用的方式來遍歷集合元素
newIs.forEach(System.out::println); // 輸出41 27 -3 37
}
}

Stream提供了大量的方法進行聚合操作,這些方法既可以是“中間的”(intermediate)操作,也可以是“末端的”(terminal)操作。
中間方法:中間操作允許流保持打開狀態,並允許直接調用後續方法。中間方法的返回值是另外一個流。如map()方法。
末端方法:末端方法是對流的最終操作。如sum()、count()。

Stream常用的中間方法:
Stream<T> filter(Predicate<? super T> predicate)
Returns a stream consisting of the elements of this stream that match the given predicate.返回符合條件的元素。
<R> Stream<R> map(Function<? super T,? extends R> mapper)
Returns a stream consisting of the results of applying the given function to the elements of this stream.
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper)Returns a DoubleStream consisting of the results of applying the given function to the elements of this stream.
IntStream mapToInt(ToIntFunction<? super T> mapper)Returns an IntStream consisting of the results of applying the given function to the elements of this stream.
LongStream mapToLong(ToLongFunction<? super T> mapper)Returns a LongStream consisting of the results of applying the given function to the elements of this stream.
Stream peek(Consumer action)
依次對每個元素執行一些操作,該方法返回的流與原始流包含相同的元素,該方法主要用於調試。
Stream distinct()
Returns a stream consisting of the distinct elements (according to Object.equals(Object)) of this stream
Stream sorted()
Returns a stream consisting of the elements of this stream, sorted according to natural order.
Stream limit(long maxSize)
該方法用於保證對該流的後續操作中最大允許訪問的元素個數。


Stream常用的末端方法:
void forEach(Consumer<? super T> action)
遍歷流中的所有元素,對每個流執行action操作
Object[] toArray()
將流中的所有元素轉化爲一個數組
Optional<T> reduce(BinaryOperator<T> accumulator)Performs a reduction on the elements of this stream, using an associative accumulation function, and returns an Optional describing the reduced value, if any.
T reduce(T identity, BinaryOperator<T> accumulator)Performs a reduction on the elements of this stream, using the provided identity value and anassociative accumulation function, and returns the reduced value.
<U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)Performs a reduction on the elements of this stream, using the provided identity, accumulation and combining functions.
Optional<T> min(Comparator<? super T> comparator)
返回流中所有元素的最小值
Optional<T> max(Comparator<? super T> comparator)
返回流中所有元素的最大值
long count()
返回流中所有元素的數量
boolean allMatch(Predicate<? super T> predicate)  
判斷流中是否每個元素都符合Predicate條件。
boolean anyMatch(Predicate<? super T> predicate)  
判斷流中是否至少存在一個元素符合Predicate條件。
boolean noneMatch(Predicate<? super T> predicate)  
判斷流中是否所有元素都不符合Predicate條件。
Optional<T> findFirst()
返回流中的第一個元素
Optional<T> findAny() 
 返回流中的任意一個元素

Collection接口提供了一個stream()默認方法,該方法可返回該集合對應的流,接下來即可通過流API來操作集合元素。由於Stream可以對集合元素進行整體的聚集操作,因此Stream極大了豐富了集合的功能。
示例代碼:(改進了上一個使用Predicate來統計數量的程序PredicateTest2)

import java.util.*;
import java.util.function.*;

public class CollectionStream
{
public static void main(String[] args)
{
// 創建books集合、爲books集合添加元素的代碼與8.2.5小節的程序相同。
Collection books = new HashSet();
books.add(new String("輕量級Java EE企業應用實戰"));
books.add(new String("瘋狂Java講義"));
books.add(new String("瘋狂iOS講義"));
books.add(new String("瘋狂Ajax講義"));
books.add(new String("瘋狂Android講義"));
// 統計書名包含“瘋狂”子串的圖書數量
System.out.println(books.stream()
.filter(ele->((String)ele).contains("瘋狂"))
.count()); // 輸出4
// 統計書名包含“Java”子串的圖書數量
System.out.println(books.stream()
.filter(ele->((String)ele).contains("Java") )
.count()); // 輸出2
// 統計書名字符串長度大於10的圖書數量
System.out.println(books.stream()
.filter(ele->((String)ele).length() > 10)
.count()); // 輸出2
// 先調用Collection對象的stream()方法將集合轉換爲Stream,
// 再調用Stream的mapToInt()方法獲取原有的Stream對應的IntStream
books.stream().mapToInt(ele -> ((String)ele).length())
// 調用forEach()方法遍歷IntStream中每個元素
.forEach(System.out::println);// 輸出8 11 16 7 8
}
}


-----------------------------------------------------------
Set集合:無序、不可重複。
HashSet:
1、集合元素可以是null;
2、不是同步的,如果多個線程同時修改同一個HashSet,則必須通過代碼來保證其同步。
3、按hash算法來存儲元素,具有很好的存取和查找性能。

當向HashSet中存入元素時,HashSet會調用該對象的hashCode()方法來獲取該對象的hashCode值,然後決定該對象在HashSet中的位置。
HashSet判斷兩個元素相等的標準是兩個對象的equal()方法比較相等,且hashCode()方法返回值也相等。

示例代碼:

import java.util.*;

// 類A的equals方法總是返回true,但沒有重寫其hashCode()方法
class A
{
public boolean equals(Object obj)
{
return true;
}
}
// 類B的hashCode()方法總是返回1,但沒有重寫其equals()方法
class B
{
public int hashCode()
{
return 1;
}
}
// 類C的hashCode()方法總是返回2,且重寫其equals()方法總是返回true
class C
{
public int hashCode()
{
return 2;
}
public boolean equals(Object obj)
{
return true;
}
}
public class HashSetTest
{
public static void main(String[] args)
{
HashSet books = new HashSet();
// 分別向books集合中添加兩個A對象,兩個B對象,兩個C對象
books.add(new A());
books.add(new A());
books.add(new B());
books.add(new B());
books.add(new C());
books.add(new C());
System.out.println(books);
}
}

將可變對象添加到了HashSet中之後,儘量不要去修改該元素參與equal()和hashCode()計算的實例變量,否則將會導致HashSet無法正確操作這些集合元素。
示例代碼:

import java.util.*;

class R
{
int count;
public R(int count)
{
this.count = count;
}
public String toString()
{
return "R[count:" + count + "]";
}
public boolean equals(Object obj)
{
if(this == obj)
return true;
if (obj != null && obj.getClass() == R.class)
{
R r = (R)obj;
return this.count == r.count;
}
return false;
}
public int hashCode()
{
return this.count;
}
}
public class HashSetTest2
{
public static void main(String[] args)
{
HashSet hs = new HashSet();
hs.add(new R(5));
hs.add(new R(-3));
hs.add(new R(9));
hs.add(new R(-2));
// 打印HashSet集合,集合元素沒有重複
System.out.println(hs);
// 取出第一個元素
Iterator it = hs.iterator();
R first = (R)it.next();
// 爲第一個元素的count實例變量賦值
first.count = -3; // ①
// 再次輸出HashSet集合,集合元素有重複元素
System.out.println(hs);
// 刪除count爲-3的R對象
hs.remove(new R(-3)); // ②
// 可以看到被刪除了一個R元素
System.out.println(hs);
System.out.println("hs是否包含count爲-3的R對象?"
+ hs.contains(new R(-3))); // 輸出false
System.out.println("hs是否包含count爲-2的R對象?"
+ hs.contains(new R(-2))); // 輸出false
}
}


LinkedHashSet:HashSet的子類,看名字就知道。LinkedHashSet集合也是根據元素的hashCode值來決定元素的位置的,但他同時使用了鏈表維護元素的次序,因此其性能略低於HashSet。
示例代碼:

import java.util.*;

public class LinkedHashSetTest
{
public static void main(String[] args)
{
LinkedHashSet books = new LinkedHashSet();
books.add("瘋狂Java講義");
books.add("輕量級Java EE企業應用實戰");
System.out.println(books);
// 刪除 瘋狂Java講義
books.remove("瘋狂Java講義");
// 重新添加 瘋狂Java講義
books.add("瘋狂Java講義");
System.out.println(books);
}
}

TreeSet:SortedSet接口的實現。功能如其名:元素處於排序狀態。
TreeSet採用紅黑樹的數據結構來存儲集合元素,TreeSet支持兩種排序方法:自然排序和定製排序。
TreeSet提供的額外方法:
Comparator<? super E> comparator()
返回對此 set 中的元素進行排序的比較器;如果此 set 使用其元素的自然順序,則返回 null。
E first() 
          返回此 set 中當前第一個(最低)元素。
E last() 
          返回此 set 中當前最後一個(最高)元素。
E lower(E e) 
          返回此 set 中嚴格小於給定元素(參考元素不需要是TreeSet集合中的元素)的最大元素;如果不存在這樣的元素,則返回 null。
E higher(E e) 
          返回此 set 中嚴格大於給定元素 (參考元素不需要是TreeSet集合中的元素)  的最小元素;如果不存在這樣的元素,則返回 null。
 SortedSet<E> headSet(E toElement) 
          返回此 set 的子集,由小於 toElement的元素組成。
 SortedSet<E> tailSet(E fromElement)c           
返回此 set 的子集,由大於或等於fromElement的元素組成。
 SortedSet<E> subSet(E fromElement, E toElement) 
          返回此 set 的部分視圖,其元素從 fromElement(包括)到 toElement(不包括)。
示例代碼:

import java.util.*;

public class TreeSetTest
{
public static void main(String[] args)
{
TreeSet nums = new TreeSet();
// 向TreeSet中添加四個Integer對象
nums.add(5);
nums.add(2);
nums.add(10);
nums.add(-9);
// 輸出集合元素,看到集合元素已經處於排序狀態
System.out.println(nums);
// 輸出集合裏的第一個元素
System.out.println(nums.first()); // 輸出-9
// 輸出集合裏的最後一個元素
System.out.println(nums.last()); // 輸出10
// 返回小於4的子集,不包含4
System.out.println(nums.headSet(4)); // 輸出[-9, 2]
// 返回大於5的子集,如果Set中包含5,子集中還包含5
System.out.println(nums.tailSet(5)); // 輸出 [5, 10]
// 返回大於等於-3,小於4的子集。
System.out.println(nums.subSet(-3 , 4)); // 輸出[2]
}
}

自然排序:TreeSet會調用集合元素的compareTo(Object obj)方法來比較元素之間大小關係,然後將集合元素按升序排列,這種方式就是自然排列。
定製排序:TreeSet藉助於Comparator接口的幫助。該接口裏包含一個的int compare(T o1, T o2)方法,該方法用於比較o1和o2的大小。

compareTo是Comparable接口的方法,obj1.compare(obj2),如果返回0則說明兩個對象相等,如果返回正整數則說明obj1大於obj2,如果返回負整數,則obj2大於obj1。
java的一些常用類已實現了Comparable接口。
如果要把一個對象添加到TreeSet中,則該對象必須實現Comparable接口,且得是同一個類型的對象。否則報錯ClassCastException。

不要修改放入TreeSet中的元素。

定製排序:在構造TreeSet時就指定比較器。public TreeSet(Comparator<? super E> comparator)
Comparator是一個函數式接口,可使用Lambda表達式。
示例代碼:

import java.util.*;

class M
{
int age;
public M(int age)
{
this.age = age;
}
public String toString()
{
return "M[age:" + age + "]";
}
}
public class TreeSetTest4
{
public static void main(String[] args)
{
// 此處Lambda表達式的目標類型是Comparator
TreeSet ts = new TreeSet((o1 , o2) ->
{
M m1 = (M)o1;
M m2 = (M)o2;
// 根據M對象的age屬性來決定大小,age越大,M對象反而越小
return m1.age > m2.age ? -1
: m1.age < m2.age ? 1 : 0;
});
ts.add(new M(5));
ts.add(new M(-3));
ts.add(new M(9));
System.out.println(ts);
}
}

EnumSet
EnumSet是一個專爲枚舉類設計的集合類,EnumSet中所有元素都必須是指定枚舉類型的枚舉值,該枚舉類型在創建EnumSet時顯式或隱式地指定。EnumSet的集合元素也是有序的,EnumSet以枚舉值在Enum類的定義順序來決定集合元素的順序。
EnumSet在內部以位向量的形式存儲,這種存儲形式非常緊湊、高效,因此EnumSet對象佔用內存很小,而且運行效率很好。尤其是當進行批量操作(如調用containsAll 和 retainAll方法)時,如其參數也是EnumSet集合,則該批量操作的執行速度也非常快。
EnumSet集合不允許加入null元素。如果試圖插入null元素,EnumSet將拋出 NullPointerException異常。如果僅僅只是試圖測試是否出現null元素、或刪除null元素都不會拋出異常,只是刪除操作將返回false,因爲沒有任何null元素被刪除。

EnumSet沒有暴露任何構造器來創建該類的實例,只能通過它提供的類方法來創建EnumSet實例,常用方法有:
EnumSet allOf(Class<E> elementType) 
          創建一個包含指定枚舉類型裏所有枚舉值的EnumSet集合
EnumSet complementOf(EnumSet<E> s) 
          創建一個其元素類型與指定EnumSet相同的EnumSet,新EnumSet 包含原EnumSet集合所不包含的枚舉值
EnumSet copyOf(Collection<E> c) 
          創建一個從指定 collection 初始化的枚舉 set。
EnumSet copyOf(EnumSet<E> s) 
          創建一個其元素類型與指定EnumSet具有相同的枚舉值的EnumSet
EnumSet noneOf(Class<E> elementType) 
          創建一個具有指定元素類型的空枚舉 set。
EnumSet of(E first, E... rest) 
          創建一個最初包含指定元素的枚舉 set。
EnumSet range(E from, E to) 
          創建一個最初包含由兩個指定端點所定義範圍內的所有元素的枚舉 set。

示例代碼:

import java.util.*;

enum Season
{
SPRING,SUMMER,FALL,WINTER
}
public class EnumSetTest
{
public static void main(String[] args)
{
// 創建一個EnumSet集合,集合元素就是Season枚舉類的全部枚舉值
EnumSet es1 = EnumSet.allOf(Season.class);
System.out.println(es1); // 輸出[SPRING,SUMMER,FALL,WINTER]
// 創建一個EnumSet空集合,指定其集合元素是Season類的枚舉值。
EnumSet es2 = EnumSet.noneOf(Season.class);
System.out.println(es2); // 輸出[]
// 手動添加兩個元素
es2.add(Season.WINTER);
es2.add(Season.SPRING);
System.out.println(es2); // 輸出[SPRING,WINTER]
// 以指定枚舉值創建EnumSet集合
EnumSet es3 = EnumSet.of(Season.SUMMER , Season.WINTER);
System.out.println(es3); // 輸出[SUMMER,WINTER]
EnumSet es4 = EnumSet.range(Season.SUMMER , Season.WINTER);
System.out.println(es4); // 輸出[SUMMER,FALL,WINTER]
// 新創建的EnumSet集合的元素和es4集合的元素有相同類型,
// es5的集合元素 + es4集合元素 = Season枚舉類的全部枚舉值
EnumSet es5 = EnumSet.complementOf(es4);
System.out.println(es5); // 輸出[SPRING]
}
}



import java.util.*;

public class EnumSetTest2
{
public static void main(String[] args)
{
Collection c = new HashSet();
c.clear();
c.add(Season.FALL);
c.add(Season.SPRING);
// 複製Collection集合中所有元素來創建EnumSet集合
EnumSet enumSet = EnumSet.copyOf(c); // ①
System.out.println(enumSet); // 輸出[SPRING,FALL]
c.add("瘋狂Java講義");
c.add("輕量級Java EE企業應用實戰");
// 下面代碼出現異常:因爲c集合裏的元素不是全部都爲枚舉值
enumSet = EnumSet.copyOf(c); // ②
}
}

----------------------------------------------------------------------------
List:有序、可重複
List中添加了一些通過索引操作List的方法。
void add(int index, E element)
在列表的指定位置index插入指定元素。
boolean addAll(int index, Collection<? extends E> c) 
          將指定 collection 中的所有元素都插入到列表中的指定位置。
 E get(int index) 
          返回列表中指定位置的元素。
int indexOf(Object o) 
          返回此列表中第一次出現的指定元素的索引;如果此列表不包含該元素,則返回 -1。
int lastIndexOf(Object o) 
          返回此列表中最後出現的指定元素的索引;如果列表不包含此元素,則返回 -1。
boolean remove(Object o) 
          從此列表中移除第一次出現的指定元素(如果存在)。
 E set(int index, E element) 
          用指定元素替換列表中指定位置的元素。
 List<E> subList(int fromIndex, int toIndex) 
          返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之間的部分視圖。
void sort(Comparator<? super E> c)
根據Comparator對List集合進行排序(Java8新增的方法)
void replaceAll(UnaryOperator<E> operator)
根據operator指定的計算規則重新設置list集合的所有元素(Java8)
 ListIterator<E> listIterator() 
          返回此列表元素的列表迭代器(按適當順序)。
 ListIterator<E> listIterator(int index) 
          返回列表中元素的列表迭代器(按適當順序),從列表的指定位置開始。
示例代碼:

import java.util.*;

public class ListTest
{
public static void main(String[] args)
{
List books = new ArrayList();
// 向books集合中添加三個元素
books.add(new String("輕量級Java EE企業應用實戰"));
books.add(new String("瘋狂Java講義"));
books.add(new String("瘋狂Android講義"));
System.out.println(books);
// 將新字符串對象插入在第二個位置
books.add(1 , new String("瘋狂Ajax講義"));
for (int i = 0 ; i < books.size() ; i++ )
{
System.out.println(books.get(i));
}
// 刪除第三個元素
books.remove(2);
System.out.println(books);
// 判斷指定元素在List集合中位置:輸出1,表明位於第二位
System.out.println(books.indexOf(new String("瘋狂Ajax講義"))); //①
//將第二個元素替換成新的字符串對象
books.set(1, new String("瘋狂Java講義"));
System.out.println(books);
//將books集合的第二個元素(包括)
//到第三個元素(不包括)截取成子集合
System.out.println(books.subList(1 , 2));
}
}

List判斷兩個對象相等的是equal()方法
示例代碼:

import java.util.*;

class A
{
public boolean equals(Object obj)
{
return true;
}
}
public class ListTest2
{
public static void main(String[] args)
{
List books = new ArrayList();
books.add(new String("輕量級Java EE企業應用實戰"));
books.add(new String("瘋狂Java講義"));
books.add(new String("瘋狂Android講義"));
System.out.println(books);
// 刪除集合中A對象,將導致第一個元素被刪除
books.remove(new A()); // ①
System.out.println(books);
// 刪除集合中A對象,再次刪除集合中第一個元素
books.remove(new A()); // ②
System.out.println(books);
}
}

Java8新增的方法:

import java.util.*;

public class ListTest3
{
public static void main(String[] args)
{
List books = new ArrayList();
// 向books集合中添加4個元素
books.add(new String("輕量級Java EE企業應用實戰"));
books.add(new String("瘋狂Java講義"));
books.add(new String("瘋狂Android講義"));
books.add(new String("瘋狂iOS講義"));
// 使用目標類型爲Comparator的Lambda表達式對List集合排序
books.sort((o1, o2)->((String)o1).length() - ((String)o2).length());
System.out.println(books);
// 使用目標類型爲UnaryOperator的Lambda表達式來替換集合中所有元素
// 該Lambda表達式控制使用每個字符串的長度作爲新的集合元素
books.replaceAll(ele->((String)ele).length());
System.out.println(books); // 輸出[7, 8, 11, 16]
}
}

List額外的方法listIterator()返回的ListIterator接口對象,提供了專門操作List的方法,它不僅可以向後迭代,它還可以向前迭代:
boolean hasPrevious():返回該迭代器關聯的集合是否還有上一個元素。
Object previous():返回該迭代器的上一個元素。
void add():在指定位置插入一個元素
示例代碼:

import java.util.*;

public class ListIteratorTest
{
public static void main(String[] args)
{
String[] books = {
"瘋狂Java講義", "瘋狂iOS講義",
"輕量級Java EE企業應用實戰"
};
List bookList = new ArrayList();
for (int i = 0; i < books.length ; i++ )
{
bookList.add(books[i]);
}
ListIterator lit = bookList.listIterator();
while (lit.hasNext())
{
System.out.println(lit.next());
lit.add("-------分隔符-------");
}
System.out.println("=======下面開始反向迭代=======");
while(lit.hasPrevious())
{
System.out.println(lit.previous());
}
}
}


ArrayList和Vector實現類
都是基於數組實現的List類,都是封裝了一個動態的、允許再分配的Object[]數組,使用initialCapacity參數來設置該數組的長度,當添加的元素超出了該數組的長度時,它們的initialCapacity會自動增加。如果開始就知道ArrayList或Vector集合需要保存的元素個數,則可以在創建他們時就指定initialCapacity大小,如果不指定默認爲10。
Vector是一個古老的集合類(從JDK1.0開始就有了),那時候還沒有提供系統的集合框架,從JDK1.2以後才提供系統的集合框架,於是將Vector改爲了實現List接口,導致了Vector裏面有一些功能重複的方法。
ArrayList是線程不安全的,Vector是線程安全的。所以Vector的性能比ArrayList低。即使要保證線程安全,也不推薦使用Vector,而是推薦使用Collections工具類來將ArrayList集合變成線程安全的。
Vector有一個Stack子類,它用於模擬“棧”這種數據結構。同樣,Stack也是線程安全、性能較差的,不推薦使用,而是用ArrayDeque代替。

固定長度的List
Arrays.ArrayList
Arrays工具類裏提供了asList(Object... a)方法,該方法可以把一個數組、或指定個數的對象轉換成一個List集合,這個List集合既不是ArrayList實現類的實例,也不是Vector實現類的實例,而是Arrays的內部類ArrayList的實例。Arrays.ArrayList是一個固定長度的List集合,程序只能遍歷訪問該集合裏的元素,不可增加、刪除該集合裏的元素

示例代碼:

import java.util.*;

public class FixedSizeList
{
public static void main(String[] args)
{
List fixedList = Arrays.asList("瘋狂Java講義"
, "輕量級Java EE企業應用實戰");
// 獲取fixedList的實現類,將輸出Arrays$ArrayList
System.out.println(fixedList.getClass());
// 使用方法引用遍歷集合元素
fixedList.forEach(System.out::println);
// 試圖增加、刪除元素都會引發UnsupportedOperationException異常
fixedList.add("瘋狂Android講義");
fixedList.remove("瘋狂Java講義");
}
}

--------------------------------------------------------------------
Queue:隊列集合
Queue是模擬隊列的數據結構,隊列是先進先出(FIFO)的容器。
Queue的方法:
方法摘要
boolean add(E e)
將指定元素插入此隊列的尾部。 在成功時返回 true,如果當前沒有可用的空間,則拋出IllegalStateException。
 E element() 
          獲取隊列頭部的元素,但是不移除此元素。
boolean offer(E e) 
          將指定的元素插入此隊列,當使用有容量限制的隊列時,此方法通常要優於add(E),後者可能無法插入元素,而只是拋出一個異常。
 E peek() 
          獲取但不移除此隊列的頭;如果此隊列爲空,則返回 null。
 E poll() 
          獲取並移除此隊列的頭,如果此隊列爲空,則返回 null。
 E remove() 
          獲取並移除此隊列的頭。

PriorityQueue:Queue的實現,將添加的元素進行了排序。
示例代碼:

import java.util.*;

public class PriorityQueueTest
{
public static void main(String[] args)
{
PriorityQueue pq = new PriorityQueue();
// 下面代碼依次向pq中加入四個元素
pq.offer(6);
pq.offer(-3);
pq.offer(20);
pq.offer(18);
// 輸出pq隊列,並不是按元素的加入順序排列
System.out.println(pq); // 輸出[-3, 6, 20, 18]
// 訪問隊列第一個元素,其實就是隊列中最小的元素:-3
System.out.println(pq.poll());
}
}

PriorityQueue的元素有兩種排序方式:
1、自然排序:元素必須實現Comparable接口,且得是同一類的實例。
2、定製排序:創建PriorityQueue時傳入一個Comparator對象,該對象負責對隊列中所有的元素進行排序。
兩種排序的用法與TreeSet一樣,參考TreeSet即可。

Deque接口與ArrayDeque實現類
Deuqe接口代表一個雙端隊列:可以從隊列兩端來操作隊列。
新增的操作首尾元素的方法:
方法摘要
void addFirst(E e)
將指定元素插入此雙端隊列的開頭(如果可以直接這樣做而不違反容量限制)。
void addLast(E e)
將指定元素插入此雙端隊列的末尾(如果可以直接這樣做而不違反容量限制)。
Iterator<E> descendingIterator()
返回以逆向順序在此雙端隊列的元素上進行迭代的迭代器。
E getFirst()
獲取,但不移除此雙端隊列的第一個元素。
E getLast()
獲取,但不移除此雙端隊列的最後一個元素。
Iterator<E> iterator()
返回以恰當順序在此雙端隊列的元素上進行迭代的迭代器。
boolean offerFirst(E e)
在不違反容量限制的情況下,將指定的元素插入此雙端隊列的開頭。
boolean offerLast(E e)
在不違反容量限制的情況下,將指定的元素插入此雙端隊列的末尾。
E peekFirst()
獲取,但不移除此雙端隊列的第一個元素;如果此雙端隊列爲空,則返回 null。
E peekLast()
獲取,但不移除此雙端隊列的最後一個元素;如果此雙端隊列爲空,則返回 null。
E pollFirst()
獲取並移除此雙端隊列的第一個元素;如果此雙端隊列爲空,則返回 null。
E pollLast()
獲取並移除此雙端隊列的最後一個元素;如果此雙端隊列爲空,則返回 null。
E pop()  棧方法
從此雙端隊列所表示的堆棧中彈出一個棧頂的元素。相當於removeFirst()方法
void push(E e)    棧方法  
將一個元素推入此雙端隊列所表示的棧頂(此雙端隊列的頭部),相當於addFirst()
E remove()
獲取並移除此雙端隊列所表示的隊列的頭部(換句話說,此雙端隊列的第一個元素)。
boolean remove(Object o)
從此雙端隊列中移除第一次出現的指定元素。
E removeFirst()
獲取並移除此雙端隊列第一個元素。
boolean removeFirstOccurrence(Object o)
從此雙端隊列移除第一次出現的指定元素。
E removeLast()
獲取並移除此雙端隊列的最後一個元素。
boolean removeLastOccurrence(Object o)
從此雙端隊列移除最後一次出現的指定元素。

示例代碼:隊列的使用

import java.util.*;

public class ArrayDequeQueue
{
public static void main(String[] args)
{
ArrayDeque queue = new ArrayDeque();
// 依次將三個元素加入隊列
queue.offer("瘋狂Java講義");
queue.offer("輕量級Java EE企業應用實戰");
queue.offer("瘋狂Android講義");
// 輸出:[瘋狂Java講義, 輕量級Java EE企業應用實戰, 瘋狂Android講義]
System.out.println(queue);
// 訪問隊列頭部的元素,但並不將其poll出隊列"棧",輸出:瘋狂Java講義
System.out.println(queue.peek());
// 依然輸出:[瘋狂Java講義, 輕量級Java EE企業應用實戰, 瘋狂Android講義]
System.out.println(queue);
// poll出第一個元素,輸出:瘋狂Java講義
System.out.println(queue.poll());
// 輸出:[輕量級Java EE企業應用實戰, 瘋狂Android講義]
System.out.println(queue);
}
}

示例代碼:棧的使用
import java.util.*;

public class ArrayDequeStack
{
public static void main(String[] args)
{
ArrayDeque stack = new ArrayDeque();
// 依次將三個元素push入"棧"
stack.push("瘋狂Java講義");
stack.push("輕量級Java EE企業應用實戰");
stack.push("瘋狂Android講義");
// 輸出:[瘋狂Android講義, 輕量級Java EE企業應用實戰, 瘋狂Java講義]
System.out.println(stack);
// 訪問第一個元素,但並不將其pop出"棧",輸出:瘋狂Android講義
System.out.println(stack.peek());
// 依然輸出:[瘋狂Android講義, 瘋狂Java講義, 輕量級Java EE企業應用實戰]
System.out.println(stack);
// pop出第一個元素,輸出:瘋狂Android講義
System.out.println(stack.pop());
// 輸出:[輕量級Java EE企業應用實戰, 瘋狂Java講義]
System.out.println(stack);
}
}

LinkedList實現類:可作爲List集合、雙端隊列、棧來使用。
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, Serializable

示例代碼:

import java.util.*;

public class LinkedListTest
{
public static void main(String[] args)
{
LinkedList books = new LinkedList();
// 將字符串元素加入隊列的尾部
books.offer("瘋狂Java講義");
// 將一個字符串元素加入棧的頂部
books.push("輕量級Java EE企業應用實戰");
// 將字符串元素添加到隊列的頭部(相當於棧的頂部)
books.offerFirst("瘋狂Android講義");
// 以List的方式(按索引訪問的方式)來遍歷集合元素
for (int i = 0; i < books.size() ; i++ )
{
System.out.println("遍歷中:" + books.get(i));
}
// 訪問、並不刪除棧頂的元素
System.out.println(books.peekFirst());
// 訪問、並不刪除隊列的最後一個元素
System.out.println(books.peekLast());
// 將棧頂的元素彈出“棧”
System.out.println(books.pop());
// 下面輸出將看到隊列中第一個元素被刪除
System.out.println(books);
// 訪問、並刪除隊列的最後一個元素
System.out.println(books.pollLast());
// 下面輸出:[輕量級Java EE企業應用實戰]
System.out.println(books);
}
}

==================================================

Map:


Map用於保存具有映射關係的數據,因此Map集合中保存着兩組數據:一組數據保存着Map中的key,另一組保存着value。key不允許重複(重複是通過equal方法比較的)。

如果把Map中的所有key放在一起來看,它們就組成了一個Set集合,而實際上Map也確實提供了一個方法keySet()來獲取所有key組成的Set集合。
Map中的key集與Set集合裏元素的存儲形式一樣,從其子類的名字上即可看出:
Set:HashSet、LinkedHashSet、SortedSet、TreeSet、EnumSet
Map:HashMap、LinkedHashMap、SortedMap、TreeMap、EnumSet
Java先是實現了Map,後來從中抽取出來了Set集合。而Map中的value集就像是List集合,只不過索引不是整數值,而是key值。

Map集合提供的方法:
public interface Map<K,V>
方法摘要
void clear()
刪除該map所有的key-value對。
boolean containsKey(Object key)
如果此map中包含指定key的映射關係,則返回 true。
boolean containsValue(Object value)
如果此map包含一個或多個指定的value,則返回 true。
Set<Map.Entry<K,V>> entrySet()
返回此map中包含的映射關係所組成的Set集合,每個集合元素都是Map.Entry對象。
V get(Object key)
返回指定鍵所映射的值;如果此映射不包含該鍵的映射關係,則返回 null。
boolean isEmpty()
如果此映射未包含鍵-值映射關係,則返回 true。
Set<K> keySet()
返回此映射中包含的鍵的 Set 視圖。
V put(K key, V value)
將指定的值與此映射中的指定鍵關聯(可選操作)。
void putAll(Map<? extends K,? extends V> m)
從指定映射中將所有映射關係複製到此映射中(可選操作)。
V remove(Object key)
如果存在一個鍵的映射關係,則將其從此映射中移除(可選操作)。
int size()
返回此映射中的鍵-值映射關係數。
Collection<V> values()
返回此映射中包含的值的 Collection 視圖。

Map接口中包含一個內部類Entry,該類封裝了一個kay-value對,Entry包括三個方法:
K getKey()
返回與此項對應的鍵。
V getValue()
返回與此項對應的值。
V setValue(V value)
用指定的值替換與此項對應的值。

Map的基本功能示例代碼:

import java.util.*;

public class MapTest
{
public static void main(String[] args)
{
Map map = new HashMap();
// 成對放入多個key-value對
map.put("瘋狂Java講義" , 109);
map.put("瘋狂iOS講義" , 10);
map.put("瘋狂Ajax講義" , 79);
// 多次放入的key-value對中value可以重複
map.put("輕量級Java EE企業應用實戰" , 99);
// 放入重複的key時,新的value會覆蓋原有的value
// 如果新的value覆蓋了原有的value,該方法返回被覆蓋的value
System.out.println(map.put("瘋狂iOS講義" , 99)); // 輸出10
System.out.println(map); // 輸出的Map集合包含4個key-value對
// 判斷是否包含指定key
System.out.println("是否包含值爲 瘋狂iOS講義 key:"
+ map.containsKey("瘋狂iOS講義")); // 輸出true
// 判斷是否包含指定value
System.out.println("是否包含值爲 99 value:"
+ map.containsValue(99)); // 輸出true
// 獲取Map集合的所有key組成的集合,通過遍歷key來實現遍歷所有key-value對
for (Object key : map.keySet() )
{
// map.get(key)方法獲取指定key對應的value
System.out.println(key + "-->" + map.get(key));
}
map.remove("瘋狂Ajax講義"); // 根據key來刪除key-value對。
System.out.println(map); // 輸出結果不再包含 瘋狂Ajax講義=79 的key-value對
}
}


Java8爲map新增的方法:
boolean remove(Object key, Object value)

V compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction):該方法使用remappingFunction根據原key-value對計算一個新的value,如果新的value不爲null,則使用新value覆蓋原來的value;如果新value爲null,但是原value不爲null,則刪除原key-value對;如果新value、原value同時爲null,則該方法不做任何改變,直接返回null。

V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction):如果傳給map的key對應的value爲null,則使用mappingFunction根據key計算一個新的結果,如果計算的新結果不爲null,則使用新結果覆蓋原來的value;如果原map不包括該key,則添加一對key-value。

V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction):如果傳給map的key對應的value不爲null,則使用remappingFunction根據原key、value計算一個新的結果,如果計算的新結果不爲null,則使用新結果覆蓋原來的value;如果計算的結果爲null,則刪除原來的key-value對。

void forEach(BiConsumer<? super K,? super V> action):遍歷方法

V getOrDefault(Object key, V defaultValue):根據key獲取value,如果該key不存在,則返回defaultValue。

default V merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction):
If the specified key is not already associated with a value or is associated with null, associates it with the given non-null value.

V putIfAbsent(K key, V value)
If the specified key is not already associated with a value (or is mapped to null) associates it with the given value and returns null, else returns the current value.

V replace(K key, V value)
Replaces the entry for the specified key only if it is currently mapped to some value.

V replace(K key, V oldValue, V newValue)
Replaces the entry for the specified key only if currently mapped to the specified value.

void replaceAll(BiFunction<? super K,? super V,? extends V> function)
Replaces each entry's value with the result of invoking the given function on that entry until all entries have been processed or the function throws an exception.

示例代碼:

import java.util.*;

public class MapTest2
{
public static void main(String[] args)
{
Map map = new HashMap();
// 成對放入多個key-value對
map.put("瘋狂Java講義" , 109);
map.put("瘋狂iOS講義" , 99);
map.put("瘋狂Ajax講義" , 79);
// 嘗試替換key爲"瘋狂XML講義"的value,由於原Map中沒有對應的key,
// 因此對Map沒有改變,不會添加新的key-value對
map.replace("瘋狂XML講義" , 66);
System.out.println(map);
// 使用原value與參數計算出來的結果覆蓋原有的value
map.merge("瘋狂iOS講義" , 10 ,
(oldVal , param) -> (Integer)oldVal + (Integer)param);
System.out.println(map); // "瘋狂iOS講義"的value增大了10
// 當key爲"Java"對應的value爲null(或不存在時),使用計算的結果作爲新value
map.computeIfAbsent("Java" , (key)->((String)key).length());
System.out.println(map); // map中添加了 Java=4 這組key-value對
// 當key爲"Java"對應的value存在時,使用計算的結果作爲新value
map.computeIfPresent("Java",
(key , value) -> (Integer)value * (Integer)value);
System.out.println(map); // map中 Java=4 變成 Java=16
}
}


HashMap和Hashtable的關係類似於ArrayList和Vector的關係,Hashtable也是一個古老的類(從JDK1.0開始)。Hashtable的名字都沒有遵循規範,單詞首字母大寫。
Hashtable:線程安全,key、value都不能爲null;(不推薦使用)
HashMap:線程不安全,key、value可以爲null,性能較Hashtable好;
key對象必須實現equal()和hashCode()方法,與HashSet一樣使用這兩個方法判斷key是否相等。操作map時不要修改其元素。

LinkedHashMap類似LinkedHashSet,使用鏈表來維護key集的次序(與插入順序一致)。

使用Properties讀寫屬性文件
Properties是Hashtable類的子類,用來處理屬性文件,可以把Map和屬性文件關聯起來。可以讀寫屬性文件。Properties相當於一個key、value都是String的Map。
public class Properties extends Hashtable<Object,Object>

方法摘要
String getProperty(String key)
用指定的鍵在此屬性列表中搜索屬性。
String getProperty(String key, String defaultValue)
用指定的鍵在屬性列表中搜索屬性。
void list(PrintStream out)
將屬性列表輸出到指定的輸出流。
void list(PrintWriter out)
將屬性列表輸出到指定的輸出流。
void load(InputStream inStream)
從輸入流中讀取屬性列表(鍵和元素對)。
void load(Reader reader)
按簡單的面向行的格式從輸入字符流中讀取屬性列表(鍵和元素對)。
void loadFromXML(InputStream in)
將指定輸入流中由 XML 文檔所表示的所有屬性加載到此屬性表中。
Enumeration<?> propertyNames()
返回屬性列表中所有鍵的枚舉,如果在主屬性列表中未找到同名的鍵,則包括默認屬性列表中不同的鍵。
Object setProperty(String key, String value)
調用 Hashtable 的方法 put。
void store(OutputStream out, String comments)
以適合使用 load(InputStream) 方法加載到 Properties 表中的格式,將此 Properties 表中的屬性列表(鍵和元素對)寫入輸出流。
void store(Writer writer, String comments)
以適合使用 load(Reader) 方法的格式,將此 Properties 表中的屬性列表(鍵和元素對)寫入輸出字符。
void storeToXML(OutputStream os, String comment)
發出一個表示此表中包含的所有屬性的 XML 文檔。
void storeToXML(OutputStream os, String comment, String encoding)
使用指定的編碼發出一個表示此表中包含的所有屬性的 XML 文檔。
Set<String> stringPropertyNames()
返回此屬性列表中的鍵集,其中該鍵及其對應值是字符串,如果在主屬性列表中未找到同名的鍵,則還包括默認屬性列表中不同的鍵。

示例代碼:

import java.util.*;
import java.io.*;

public class PropertiesTest
{
public static void main(String[] args)
throws Exception
{
Properties props = new Properties();
// 向Properties中增加屬性
props.setProperty("username" , "yeeku");
props.setProperty("password" , "123456");
// 將Properties中的key-value對保存到a.ini文件中
props.store(new FileOutputStream("a.ini")
, "comment line"); //①
// 新建一個Properties對象
Properties props2 = new Properties();
// 向Properties中增加屬性
props2.setProperty("gender" , "male");
// 將a.ini文件中的key-value對追加到props2中
props2.load(new FileInputStream("a.ini") ); //②
System.out.println(props2);
}
}


SortedMap接口和TreeMap實現類
類比SortedSet和TreeSet,使用紅黑樹結構存儲,對key進行了排序。
兩種排序方式:自然排序和定製排序。


方法摘要
Map.Entry<K,V> ceilingEntry(K key)
返回一個鍵-值映射關係,它與大於等於給定鍵的最小鍵關聯;如果不存在這樣的鍵,則返回 null。
K ceilingKey(K key)
返回大於等於給定鍵的最小鍵;如果不存在這樣的鍵,則返回 null。
void clear()
從此映射中移除所有映射關係。
Object clone()
返回此 TreeMap 實例的淺表副本。
Comparator<? superK> comparator()
返回對此映射中的鍵進行排序的比較器;如果此映射使用鍵的自然順序,則返回 null。
boolean containsKey(Object key)
如果此映射包含指定鍵的映射關係,則返回 true。
boolean containsValue(Object value)
如果此映射爲指定值映射一個或多個鍵,則返回 true。
NavigableSet<K> descendingKeySet()
返回此映射中所包含鍵的逆序 NavigableSet 視圖。
NavigableMap<K,V> descendingMap()
返回此映射中所包含映射關係的逆序視圖。
Set<Map.Entry<K,V>> entrySet()
返回此映射中包含的映射關係的 Set 視圖。
Map.Entry<K,V> firstEntry()
返回一個與此映射中的最小鍵關聯的鍵-值映射關係;如果映射爲空,則返回 null。
K firstKey()
返回此映射中當前第一個(最低)鍵。
Map.Entry<K,V> floorEntry(K key)
返回一個鍵-值映射關係,它與小於等於給定鍵的最大鍵關聯;如果不存在這樣的鍵,則返回 null。
K floorKey(K key)
返回小於等於給定鍵的最大鍵;如果不存在這樣的鍵,則返回 null。
V get(Object key)
返回指定鍵所映射的值,如果對於該鍵而言,此映射不包含任何映射關係,則返回 null。
SortedMap<K,V> headMap(K toKey)
返回此映射的部分視圖,其鍵值嚴格小於 toKey。
NavigableMap<K,V> headMap(K toKey, boolean inclusive)
返回此映射的部分視圖,其鍵小於(或等於,如果 inclusive 爲 true)toKey。
Map.Entry<K,V> higherEntry(K key)
返回一個鍵-值映射關係,它與嚴格大於給定鍵的最小鍵關聯;如果不存在這樣的鍵,則返回 null。
K higherKey(K key)
返回嚴格大於給定鍵的最小鍵;如果不存在這樣的鍵,則返回 null。
Set<K> keySet()
返回此映射包含的鍵的 Set 視圖。
Map.Entry<K,V> lastEntry()
返回與此映射中的最大鍵關聯的鍵-值映射關係;如果映射爲空,則返回 null。
K lastKey()
返回映射中當前最後一個(最高)鍵。
Map.Entry<K,V> lowerEntry(K key)
返回一個鍵-值映射關係,它與嚴格小於給定鍵的最大鍵關聯;如果不存在這樣的鍵,則返回 null。
K lowerKey(K key)
返回嚴格小於給定鍵的最大鍵;如果不存在這樣的鍵,則返回 null。
NavigableSet<K> navigableKeySet()
返回此映射中所包含鍵的 NavigableSet 視圖。
Map.Entry<K,V> pollFirstEntry()
移除並返回與此映射中的最小鍵關聯的鍵-值映射關係;如果映射爲空,則返回 null。
Map.Entry<K,V> pollLastEntry()
移除並返回與此映射中的最大鍵關聯的鍵-值映射關係;如果映射爲空,則返回 null。
V put(K key, V value)
將指定值與此映射中的指定鍵進行關聯。
void putAll(Map<? extends K,? extends V> map)
將指定映射中的所有映射關係複製到此映射中。
V remove(Object key)
如果此 TreeMap 中存在該鍵的映射關係,則將其刪除。
int size()
返回此映射中的鍵-值映射關係數。
NavigableMap<K,V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive)
返回此映射的部分視圖,其鍵的範圍從 fromKey 到 toKey。
SortedMap<K,V> subMap(K fromKey, K toKey)
返回此映射的部分視圖,其鍵值的範圍從 fromKey(包括)到 toKey(不包括)。
SortedMap<K,V> tailMap(K fromKey)
返回此映射的部分視圖,其鍵大於等於 fromKey。
NavigableMap<K,V> tailMap(K fromKey, boolean inclusive)
返回此映射的部分視圖,其鍵大於(或等於,如果 inclusive 爲 true)fromKey。
Collection<V> values()
返回此映射包含的值的 Collection 視圖。

示例代碼:

import java.util.*;

class R implements Comparable
{
int count;
public R(int count)
{
this.count = count;
}
public String toString()
{
return "R[count:" + count + "]";
}
// 根據count來判斷兩個對象是否相等。
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj != null && obj.getClass() == R.class)
{
R r = (R)obj;
return r.count == this.count;
}
return false;
}
// 根據count屬性值來判斷兩個對象的大小。
public int compareTo(Object obj)
{
R r = (R)obj;
return count > r.count ? 1 :
count < r.count ? -1 : 0;
}
}
public class TreeMapTest
{
public static void main(String[] args)
{
TreeMap tm = new TreeMap();
tm.put(new R(3) , "輕量級Java EE企業應用實戰");
tm.put(new R(-5) , "瘋狂Java講義");
tm.put(new R(9) , "瘋狂Android講義");
System.out.println(tm);
// 返回該TreeMap的第一個Entry對象
System.out.println(tm.firstEntry());
// 返回該TreeMap的最後一個key值
System.out.println(tm.lastKey());
// 返回該TreeMap的比new R(2)大的最小key值。
System.out.println(tm.higherKey(new R(2)));
// 返回該TreeMap的比new R(2)小的最大的key-value對。
System.out.println(tm.lowerEntry(new R(2)));
// 返回該TreeMap的子TreeMap
System.out.println(tm.subMap(new R(-1) , new R(4)));
}
}



WeakHashMap
用法與HashMap一樣,區別:HashMap的key保留了對實際對象的強引用,這意味着只要HashMap不被銷燬,該HashMap中所有key引用的對象就不會被回收,HashMap也不會刪除這些key-value對。但是WeakHashMap的key只保留了對實際對象的弱引用,這意味着如果WeakHashMap對象的key所引用的對象若果沒有被其他強引用變量所引用,則這些對象可能會被回收,WeakHashMap也會自動刪除這些key-value對。

示例代碼:

import java.util.*;

public class WeakHashMapTest
{
public static void main(String[] args)
{
WeakHashMap whm = new WeakHashMap();
// 將WeakHashMap中添加三個key-value對,
// 三個key都是匿名字符串對象(沒有其他引用)
whm.put(new String("語文") , new String("良好"));
whm.put(new String("數學") , new String("及格"));
whm.put(new String("英文") , new String("中等"));
//將 WeakHashMap中添加一個key-value對,
// 該key是一個系統緩存的字符串對象。
whm.put("java" , new String("中等")); // ①
// 輸出whm對象,將看到4個key-value對。
System.out.println(whm);
// 通知系統立即進行垃圾回收
System.gc();
System.runFinalization();
// 通常情況下,將只看到一個key-value對。
System.out.println(whm);
}
}


IdentityHashMap
實現與HashMap基本相同,但是比較兩個key時不同,使用的是嚴格相等,即key1==key2纔會認爲兩個key相等。而HashMap比較的是equal和hashCode方法。
示例代碼:

import java.util.*;

public class IdentityHashMapTest
{
public static void main(String[] args)
{
IdentityHashMap ihm = new IdentityHashMap();
// 下面兩行代碼將會向IdentityHashMap對象中添加兩個key-value對
ihm.put(new String("語文") , 89);
ihm.put(new String("語文") , 78);
// 下面兩行代碼只會向IdentityHashMap對象中添加一個key-value對
ihm.put("java" , 93);
ihm.put("java" , 98);
System.out.println(ihm);
}
}


EnumMap
key必須是單個枚舉值。
示例代碼:

import java.util.*;

enum Season
{
SPRING,SUMMER,FALL,WINTER
}
public class EnumMapTest
{
public static void main(String[] args)
{
// 創建EnumMap對象,該EnumMap的所有key都是Season枚舉類的枚舉值
EnumMap enumMap = new EnumMap(Season.class);
enumMap.put(Season.SUMMER , "夏日炎炎");
enumMap.put(Season.SPRING , "春暖花開");
System.out.println(enumMap);
}
}

HashSet和HashMap的性能
因爲HashSet和HashMap、Hashtable都使用hash算法來決定其元素(對HashMap則是key)的存儲,因此HashSet、HashMap的hash表包含如下屬性:
容量(capacity):hash表中桶的數量。
初始化容量(initial capacity):創建hash表時桶的數量。HashMap和HashSet都允許在構造器中指定初始化容量。
尺寸(size):當前散列表中記錄的數量。
負載因子(load factor):負載因子等於“size/capacity”。負載因子爲0,表示空的hash表,0.5表示半滿的散列表,依此類推。輕負載的散列表具有衝突少、適宜插入與查詢的特點(但是使用Iterator迭代元素時會變慢)。
負載極限:0~1的數值,默認是0.75,當hash表中的負載因子達到負載極限時,hash表會自動的成倍的增長容量,rehashing。如果開始就知道要保存的記錄數,則可以使用記錄數除以負載極限得到最大初始容量,那麼以後就不會發生rehashing,提高效率。


============================================
Collections:操作集合的工具類

排序操作:
static  void reverse(List<?> list) 
          反轉指定列表中元素的順序。
static  void shuffle(List<?> list) 
          使用默認隨機源對指定列表進行置換。
static  void sort(List<T> list) 
          根據元素的自然順序 對指定列表按升序進行排序。
static  void sort(List<T> list, Comparator<? super T> c) 
          根據指定比較器產生的順序對指定列表進行排序。
static  void swap(List<?> list, int i, int j) 
          在指定列表的指定位置處交換元素。
static  void rotate(List<?> list, int distance) 
          根據指定的距離輪換指定列表中的元素。

示例代碼:

import java.util.*;

public class SortTest
{
public static void main(String[] args)
{
ArrayList nums = new ArrayList();
nums.add(2);
nums.add(-5);
nums.add(3);
nums.add(0);
System.out.println(nums); // 輸出:[2, -5, 3, 0]
Collections.reverse(nums); // 將List集合元素的次序反轉
System.out.println(nums); // 輸出:[0, 3, -5, 2]
Collections.sort(nums); // 將List集合元素的按自然順序排序
System.out.println(nums); // 輸出:[-5, 0, 2, 3]
Collections.shuffle(nums); // 將List集合元素的按隨機順序排序
System.out.println(nums); // 每次輸出的次序不固定
}
}

查找替換操作:
binarySearch(List<? extends Comparable<? super T>> list, T key) 
      使用二分搜索法搜索指定列表,以獲得指定對象。但是必須保證list已處於有序狀態
max(Collection<? extends T> coll) 
      根據元素的自然順序,返回給定 collection 的最大元素。
max(Collection<? extends T> coll, Comparator<? super T> comp) 
      根據指定比較器產生的順序,返回給定 collection 的最大元素。
min(Collection<? extends T> coll) 
     根據元素的自然順序 返回給定 collection 的最小元素。
min(Collection<? extends T> coll, Comparator<? super T> comp) 
     根據指定比較器產生的順序,返回給定 collection 的最小元素。
fill(List<? super T> list, T obj) 
          使用指定元素替換指定列表中的所有元素。
frequency(Collection<?> c, Object o) 
          返回指定 collection 中等於指定對象的元素數。
indexOfSubList(List<?> source, List<?> target) 
          返回指定源列表中第一次出現指定目標列表的起始位置;如果沒有出現這樣的列表,則返回 -1。
lastIndexOfSubList(List<?> source, List<?> target) 
          返回指定源列表中最後一次出現指定目標列表的起始位置;如果沒有出現這樣的列表,則返回 -1。
replaceAll(List<T> list, T oldVal, T newVal) 
          使用另一個值替換列表中出現的所有某一指定值。
singletonMap(K key, V value)
返回一個不可變的映射,它只將指定鍵映射到指定值。
sort(List<T> list, Comparator<? super T> c)
根據指定比較器產生的順序對指定列表進行排序。

示例代碼:

import java.util.*;

public class SearchTest
{
public static void main(String[] args)
{
ArrayList nums = new ArrayList();
nums.add(2);
nums.add(-5);
nums.add(3);
nums.add(0);
System.out.println(nums); // 輸出:[2, -5, 3, 0]
System.out.println(Collections.max(nums)); // 輸出最大元素,將輸出3
System.out.println(Collections.min(nums)); // 輸出最小元素,將輸出-5
Collections.replaceAll(nums , 0 , 1); // 將nums中的0使用1來代替
System.out.println(nums); // 輸出:[2, -5, 3, 1]
// 判斷-5在List集合中出現的次數,返回1
System.out.println(Collections.frequency(nums , -5));
Collections.sort(nums); // 對nums集合排序
System.out.println(nums); // 輸出:[-5, 1, 2, 3]
//只有排序後的List集合纔可用二分法查詢,輸出3
System.out.println(Collections.binarySearch(nums , 3));
}
}



同步控制:
使用synchronizedXxx()方法將指定集合包裝成線程同步的集合:
示例代碼:

import java.util.*;

public class SynchronizedTest
{
public static void main(String[] args)
{
// 下面程序創建了四個線程安全的集合對象
Collection c = Collections
.synchronizedCollection(new ArrayList());
List list = Collections.synchronizedList(new ArrayList());
Set s = Collections.synchronizedSet(new HashSet());
Map m = Collections.synchronizedMap(new HashMap());
}
}

設置不可變集合:
static <T> List<T> emptyList() 
          返回空的列表(不可變的)。
static <K,V> Map<K,V> emptyMap() 
          返回空的映射(不可變的)。
static <T> Set<T> emptySet() 
          返回空的 set(不可變的)。
static <T> Set<T> singleton(T o) 
          返回一個只包含指定對象的不可變 set。
static <T> List<T> singletonList(T o) 
          返回一個只包含指定對象的不可變列表。
static <K,V> Map<K,V> singletonMap(K key, V value) 
          返回一個不可變的映射,它只將指定鍵映射到指定值。
  unmodifiableXxx() 
          返回指定集合的不可修改視圖。
示例代碼:

import java.util.*;

public class UnmodifiableTest
{
public static void main(String[] args)
{
// 創建一個空的、不可改變的List對象
List unmodifiableList = Collections.emptyList();
// 創建一個只有一個元素,且不可改變的Set對象
Set unmodifiableSet = Collections.singleton("瘋狂Java講義");
// 創建一個普通Map對象
Map scores = new HashMap();
scores.put("語文" , 80);
scores.put("Java" , 82);
// 返回普通Map對象對應的不可變版本
Map unmodifiableMap = Collections.unmodifiableMap(scores);
// 下面任意一行代碼都將引發UnsupportedOperationException異常
unmodifiableList.add("測試元素"); //①
unmodifiableSet.add("測試元素"); //②
unmodifiableMap.put("語文" , 90); //③
}
}

古老的接口:Enumeration
不看也罷,既然不用。

集合框架的優點
提供一組可用的集合接口
提供有效的數據結構和算法
可以方便地擴展或改寫集合
接口的實現都是可交換的
使設計新 API 的工作降到最少
接口和算法的可重用性












發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章