java collection详解_java 7 collection 详解(一)
一、綜述
java集合框架定義了幾個接口,這些接口決定了collection類的基本特性。不同的是,具體類僅僅是提供了標準接口的不同實現,如圖,
java集合框架接口圖
從圖可知,java集合類的主要是由兩個接口派生而出——Collection和Map,Collection和Map是集合框架的根接口。其介紹如下:Collection — 位于集合框架的頂層,一個Collection代表一組Object,即Collection的元素(Elements)。有的Collection(子類)允許有相同的元素出現而有的就不行,有的Collection(子類)支持排序而有的則不行。Java SDK不提供直接繼承自Collection的類,Java SDK提供的類都是繼承自Collection的“子接口”如List、Set和Queue。
Set — 擴展了Collection的集合,集合中的元素不可以重復,即任意的兩個元素e1和e2都有e1.equals(e2) == false。訪問集合中的元素只能根據元素本身來訪問。
List — 擴展了Collection的集合,集合中的元素可以重復,訪問集合中的元素可以根據元素的索引來訪問。
Queue — 提供了隊列的實現,除了基本的Collection操作外,隊列還提供其他的插入、提取和檢查操作。
Map — 以Key-value對形式保存元素,訪問時只能根據每項元素的key來訪問其value。key必須唯一,value的值可以重復。
尚未列出的兩個接口是Set和Map的排序版本:SortedSet — 擴展Set,集合元素按升序排列
SortedMap — 擴展Map,以便關鍵字按升序排列
另外,除了上面提到的接口,java集合使用Comparator、Iterator和ListIterator等接口,這些接口將會陸續講解,簡單來說,Comparator接口定義了兩個對象的比較方法,即Iterator和ListIterator接口集合里的對象。同時,為了提供集合最大的靈活性,每個集合接口里的方法都是可修改的 —— 一個給定的implemention不一定支持所有的操作(方法)。當調用了不支持的方法時,將引發一個UnsupportedOperationException異常。
二、Collection接口
Collection是集合框架的基礎,它聲明了所有集合都將擁有的核心方法。一個Collection代表一組Object,即Collection的元素(Elements)。所有實現Collection接口的類都必須提供兩個標準的構造函數:無參數的構造函數用于創建一個空的Collection,有一個 Collection參數的構造函數用于創建一個新的Collection,這個新的Collection與傳入的Collection有相同的元素。后 一個構造函數允許用戶復制一個Collection。如下是Collection接口的代碼實現:
public interface Collection extends Iterable {
// 基本方法
int size();
boolean isEmpty();
boolean contains(Object element);
boolean add(E element);//可選
boolean remove(Object element);//可選
Iterator iterator();
// 批量操作
boolean containsAll(Collection> c);
boolean addAll(Collection extends E> c); //可選
boolean removeAll(Collection> c);//可選
boolean retainAll(Collection> c);//可選
void clear();//可選
// 數組操作
Object[] toArray();
T[] toArray(T[] a);
}
附:“可選”代表了其子類(或子接口)可以有選擇的去實現,即可以不提供此操作(方法).
Collection接口定義了操作一組objects的基本方法,如:集合里有多少個objects(size,isEmpty),集合是否包含某個object(contain),從集合里增加或刪除一個object(add,remove),返回集合的迭代(iterator)
遍歷Collection
遍歷Collection,有兩種方法(foreach循環,Iterator接口)。
如下演示了使用foreach循環來輸出集合里的元素:
for (Object o : collection){
System.out.println(o);
}
Iterator接口如下:
public interface Iterator {
boolean hasNext();//如果仍有元素可以迭代,則返回 true。
E next(); //返回迭代的下一個元素。
void remove(); //可選操作,移除當前迭代的object
}
在需要進行如下情況時,應使用Iterator接口迭代collection,而不選foreach方法:
1.移除當前object。foreach方法隱藏了迭代器,故而不能使用remove()方法,同時也不能使用foreach過濾collection集合
2.多個collection的迭代
如下演示如何使用Iterator過濾任意collection—在遍歷集合時,移除指定object:
static void filter(Collection> c) {
for (Iterator> it = c.iterator(); it.hasNext(); )
if (!cond(it.next()))
it.remove();
}
Collection的批量操作
批量操作操縱的是整一個集合,同時批量操作的效率的較低。containsAll — 如果此 collection 包含指定 collection 中的所有元素,則返回 true。
addAll — ?將指定collection中的所有元素添加到此collection
removeAll — 移除此 collection 中那些也包含在指定 collection 中的所有元素
retainAll — 僅保留此 collection 中那些也包含在指定 collection 的元素,換句話說,移除此 collection 中未包含在指定 collection 中的所有元素。
clear — 移除此 collection 中的所有元素
以上批量操作的方法,如果成功修改此collection,都會返回true。如下:從集合c里刪除所有指定的元素e(一個或多個),c.removeAll(Collections.singleton(e));更具體一點的就是,你可以刪除集合c里所有的null元素c.removeAll(Collections.singleton(null));附:Collections.singleton是一個靜態工廠方法,返回一個只包含指定對象的不可變的set
Collection的數組操作
Collection的toArray()方法會返回在一個數組,這個數組包含了collection集合里的元素,數組長度取決于collection集合元素的個數。如下,c為一個Collection,
Object[] a = c.toArray();//返回的是一個object數組
如果已知集合里元素的類型,假設所有元素都為String類型(Collection),如下操作可以返回一個String數組:
String[] a = c.toArray(new String[0]);
三、Set接口
一個不包含重復元素的 collection。更確切地講,set 不包含滿足 e1.equals(e2) 的元素對 e1 和 e2,并且最多包含一個 null 元素。因此,Set構造函數里的Collection參數不能包含相同的object。當兩個Set對象所包含的元素都相同,則認為這兩個Set等同的。
請注意:必須小心操作可變對象(Mutable Object)。如果一個Set中的可變元素改變了自身狀態導致Object.equals(Object)==true將導致Set的行為不確定。
某些 set 實現對其所包含的元素有所限制。例如,某些實現禁止包含 null 元素,而某些則對其元素的類型所有限制。試圖添加不合格的元素會拋出未經檢查的異常,通常是 NullPointerException 或 ClassCastException。試圖查詢不合格的元素是否存在可能會拋出異常,也可能簡單地返回 false.
Set接口的代碼如下:
public interface Set extends Collection {
// 基本方法
int size();
boolean isEmpty();
boolean contains(Object element);
boolean add(E element); // 可選
boolean remove(Object element);// 可選
Iterator iterator();
// 批量操作
boolean containsAll(Collection> c);
boolean addAll(Collection extends E> c);// 可選
boolean removeAll(Collection> c); // 可選
boolean retainAll(Collection> c);// 可選
void clear();// 可選
// 數組操作
Object[] toArray();
T[] toArray(T[] a);
}
java提供了三種Set的常用實現:HashSet,TreeSet和LinkedHashSet,HashSet — 以哈希表的形式存儲集合元素,它不保證 set 的迭代順序;特別是它不保證該順序恒久不變。可以包含 null 元素。
TreeSet — 以紅黑樹的形式存儲集合元素,使用元素的自然順序對元素進行排序。整體性能比HashSet低
LinkedHashSet — 具有可預知迭代順序的 Set 接口的哈希表和鏈接列表實現,按照元素的插入順序進行排序。可以包含null元素。
在這里,給出一個簡單而是用的Set用法:從一個可能含有重復元素里的集合c,創建一個消除重復元素的集合noDups:(在下面的FindDups有具體的演示)Collection noDups = new HashSet(c);
如果在刪除了重復元素的基礎上,還要保留原來元素的順序的,可以選擇使用LinkedHashSet,Collection noDups = new LinkedHashSet(c); ? ?這個LinkedHashSet的用法,也可以用如下的泛型方法來代替:
public static Set removeDups(Collection c) {
return new LinkedHashSet(c);
}Set的基本操作
size()返回集合元素的個數,isEmpty()判斷集合是否為空;add()方法往集合添加一個object,如果Set集合里尚未包含這個元素,則可順利添加,否則返回false;類似的,remove方法可以刪除Set集合里存在的一個元素,如果不存在則返回false。iterator()返回集合的迭代。
Demo:FindDups利用Set的基本方法,輸出String數組里重復的元素,public class FindDups {
public static void main(String[] args) {
String[] str = {"d","b","c","a","d"};
Set s = new HashSet();
for (String a : str)
if (!s.add(a))
System.out.println("重復的元素: " + a);
System.out.println(s.size() + " 個不同的元素: " + s);
}
}
其輸出如下:
在FindDups里使用HashSet— 一個沒有排序功能的Set,如果要實現元素排序的輸出,可以選擇使用TreeSet作為Set的實現。
Set s = new TreeSet();
其輸出如下:
Set批量操作
collection里的批量操作很是適用于Set,具體批量操作可參考collection的批量操作。在這里演示一下如何求兩個Set集合的并集、交集和差集:
// 并集
Set union = new HashSet(s1);
union.addAll(s2);
// 交集
Set intersection = new HashSet(s1);
intersection.retainAll(s2);
// 差集
Set difference = new HashSet(s1);
difference.removeAll(s2);
在此,可以回顧一下FindDups,假設你需要找出collection里有哪些元素只出現了一次,哪些元素出現了多次,應該如何實現呢???可以設計兩個Set,一個包含了只出現一次的元素,另外一個包含了哪些重復出現的元素,具體的實現如下:
public class FindDups2 {
public static void main(String[] args) {
String[] str = {"d","b","c","a","d"};
Set uniques = new HashSet();
Set dups = new HashSet();
for (String a : str){
if (!uniques.add(a))
dups.add(a);
}
// 移除所有的重復出現的元素
uniques.removeAll(dups);
System.out.println("只出現一次的元素: " + uniques);
System.out.println("重復出現的元素: " + dups);
}
}
其輸出如下:
Set的數組操作
Set的數組操作和collection的數組一致,并沒有特別的用法,詳情可看Collection的數組操作。
四、List接口
List是一個有序的collection(有時也叫做“序列”),可以包含重復的元素。除了從Collection接口繼承過來的方法,List也提供了如下操作(方法)。索引訪問 — 根據元素的索引來操縱元素
搜索— 搜索指定元素并返回其索引
Iteration — 繼承了Iterator ,充分利用了List的索引優勢
截取 — 截取List任意范圍的元素
List接口代碼如下:
public interface List extends Collection {
// 索引訪問
E get(int index);
E set(int index, E element);//可選
boolean add(E element); //可選
void add(int index, E element);//可選
E remove(int index);//可選
boolean addAll(int index, Collection extends E> c);//可選
// 搜索
int indexOf(Object o);
int lastIndexOf(Object o);
// 迭代器
ListIterator listIterator();
ListIterator listIterator(int index);
// 截取List
List subList(int from, int to);
}
javat提供了兩個List的常用實現:ArrayList和LinkedList。在搜索頻繁的情況下,選擇使用ArrayList;插入刪除頻繁的情況下,選擇使用ListArray。值得提一下的的是,從java2開始,Vector向量改進為可以實現List。
Collection操作
List從Collection那繼承過來的方法,它的使用方式和Collection是一致的,如果不太熟悉Collection操作,可以看一下Collection接口。remove()方法會刪除List集合里的第一個元素,add()和addAll()方法從List的尾部依次添加元素,所以下面的代碼會連接兩個List:
list1.addAll(list2);
如果需要新建一個List3來連接List1和List2,可以這樣做:
List list3 = new ArrayList(list1);
list3.addAll(list2);
和Set接口一樣,如果兩個List所包含的元素一致,則認為這兩個List是等同的。
List的索引訪問與搜索
List的索引訪問與搜索和數組的操作基本相似,如下演示了交換List里的兩個元素的位置
public static void swap(List a, int i, int j) {
E temp = a.get(i);
a.set(i, a.get(j));
a.set(j, temp);
}
利用這個元素位置交換的性質,我們可以模擬撲克牌的洗牌操作,如下
public static void shuffle(List> list, Random rnd) {
for (int i = list.size(); i > 1; i--)
swap(list, i - 1, rnd.nextInt(i));
}
利用隨機數的性質,使洗牌這一操作對每一位玩家來說都是公平的。而這個洗牌shuffle的方法,其實包含在Collections類里,如下代碼演示了隨機輸出集合里各個元素(發牌):
public class Shuffle {
public static void main(String[] args) {
String[] str = {"a","b","c","d","e","f"};//可以選擇湊夠52張牌
List list = new ArrayList();
for (String a : str){
list.add(a);
}
Collections.shuffle(list, new Random());
System.out.println(list);
}
}
附:事實上,還可以采取一種更為快捷簡單的方法,使用Arrays提供的靜態工廠方法asList:
public class Shuffle2 {
public static void main(String[] args) {
String[] str = {"a","b","c","d","e","f"};//可以選擇湊夠52張牌
List list = Arrays.asList(str);
Collections.shuffle(list);
System.out.println(list);
}
}
迭代器Iterators
List提供了一個功能更為豐富的迭代器ListIterator,ListIterator,允許程序員按任一方向遍歷列表、迭代期間修改列表,并獲得迭代器在列表中的當前位置。ListIterator 沒有當前元素;它的光標位置 始終位于調用 previous() 所返回的元素和調用 next() 所返回的元素之間。長度為 n 的列表的迭代器有 n+1 個可能的指針位置(如圖,長度為4的列表的迭代器有5個可能的指針位置)。
ListIterator接口的代碼如下:
public interface ListIterator extends Iterator {
boolean hasNext();
E next();
boolean hasPrevious();
E previous();
int nextIndex();//返回對 next 的下一個元素的索引。
int previousIndex();//返回對 previous 的上一個元素的索引
void remove(); //可選
void set(E e); //可選
void add(E e); //可選
}
如下代碼演示了如何反向迭代列表元素:
for (ListIterator it = list.listIterator(list.size()); it.hasPrevious(); ) {
Type t = it.previous();
...
}
hasNext, next, 和remove方法的用法和原來的Iterator的用法一致,nextIndex和previousIndex通常用于記錄某個搜索到的元素的位置或者用于創建一個元素位置一致的新ListIterator。如果指針位置在第一個元素之前調用previousIndex()方法,會返回-1;類似的,如果指針位置在最后一個元素之后調用nextIndex()方法,會返回List.size。更為詳細的講解, 可以參考一下List.indexOf方法的實現:
public int indexOf(E e) {
for (ListIterator it = listIterator(); it.hasNext(); )
if (e == null ? it.next() == null : e.equals(it.next()))
return it.previousIndex();
// 元素不存在
return -1;
}
ListIterator接口提供了兩種更新列表的方法—set和add。set(E e)用指定元素替換 next 或 previous 返回的最后一個元素。如下代碼演示了如何用另一個值替換列表里得指定值:
public static void replace(List list, E val, E newVal) {
for (ListIterator it = list.listIterator(); it.hasNext(); )
if (val == null ? it.next() == null : val.equals(it.next()))
it.set(newVal);
}
這個Demo里只需要比較val和it.next是否相同就行了,同時需要判斷null,防止出現NullPointerException。add(E e)會在指針位置的前面插入一個元素。如下Demo演示了用一組數據替換List里的指定的值:
public static void replace(List list, E val, List extends E> newVals) {
for (ListIterator it = list.listIterator(); it.hasNext();) {
if (val == null ? it.next() == null : val.equals(it.next())) {
it.remove();
for (E e : newVals)
it.add(e);
}
}
}
List的截取操作
List的截取操作,subList(int fromIndex, int toIndex),截取原List第fromIndex位到第toIndex位的元素(新List包含第fromIndex位,不包含toIndex位,用集合表示為[fromIndex,toIndex]。注意的是,返回的新List還是原List的一部風,在新List所做的改變,同時也會改變原List。
任和需要List對象的操作都可以看成是對其子List的一系列操作。舉個例子,如下代碼可刪除List指定范圍里的元素:
list.subList(fromIndex, toIndex).clear();
類似地,可以這樣搜索List表里的指定元素:int i = list.subList(fromIndex, toIndex).indexOf(object);
int j = list.subList(fromIndex, toIndex).lastIndexOf(object);注:如上搜索指定元素的方法,返回的字表的索引值,而不是原List的索引值。
任意一個操作List的多肽算法,如replace和shuffle方法,都是與字表打交道的。這里給出一個使用subList的多肽方法,使用這個方法來處理經典發撲克牌的問題。首先,先假設只給一個人發牌(hand),撲克牌堆用deck表示,那么發n張牌就可以看做從deck表的尾部截取n個元素的子List。代碼如下:
public static List dealHand(List deck, int n) {
int deckSize = deck.size();//撲克牌的(剩余)張數
List handView = deck.subList(deckSize - n, deckSize);
List hand = new ArrayList(handView);//發給這個人n張牌
handView.clear();//已發的牌,需要從deck里刪除
return hand;
}
注意到了,我們是從deck(List)的尾端截取的。對多數List的實現來說,如ArrayList,從尾端移除元素比從頭部移除元素的效率好。接著,我們來看一下如何給n個人發牌(這里假設有52張牌)。
public class Deal {
public static void main(String[] args) {
// 52張撲克牌
String[] suit = new String[] { "黑桃", "紅心", "梅花", "方塊" };
String[] rank = new String[] { "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K" };
// 初始化deck列表
List deck = new ArrayList();
for (int i = 0; i < suit.length; i++) {
for (int j = 0; j < rank.length; j++) {
deck.add(suit[i] + rank[j]);
}
}
dealing(deck, 4, 13); // 4個玩家,每個玩家13張牌
}
//發牌,共有numHands玩家,每個玩家有cardsPerPerson張牌
public static void dealing(List deck, int numHands, int cardsPerPerson) {
// 洗牌
Collections.shuffle(deck);
if (numHands * cardsPerPerson > deck.size()) {
System.out.println("玩家太多,撲克牌不足");
return;
}
for (int i = 0; i < numHands; i++) {
System.out.println(dealHand(deck, cardsPerPerson));
}
}
public static List dealHand(List deck, int n) {
int deckSize = deck.size();
List handView = deck.subList(deckSize - n, deckSize);
List hand = new ArrayList(handView);
handView.clear();
return hand;
}
}
其輸出如下:(因為是隨機發牌的,所以每次運行的結果都會不一樣)
List 算法
類Collections提供的多數多肽方法都適用于List,在這里給出這些算法概覽(以后會詳談):sort — 使用合并排序算法排序List,默認為升序排列。
shuffle — 使用隨機源對指定列表進行置換。(洗牌).
reverse — 反轉指定列表中元素的順序。
rotate — 根據指定的距離輪換指定列表中的元素。
swap — 交換指定位置上的元素
replaceAll — 使用一個值替換列表中出現的所有某一指定值。
fill — 使用指定元素替換列表中的所有元素。
copy — 將所有元素從一個列表復制到另一個列表。
binarySearch — 使用二分搜索法搜索指定列表,以獲得指定對象。
indexOfSubList — 返回指定列表中第一次出現指定目標列表的起始位置;如果沒有出現這樣的目標,則返回 -1。
lastIndexOfSubList — 返回指定源列表中最后一次出現指定目標列表的起始位置;如果沒有出現這樣的目標,則返回 -1。
最后附上兩張經典的collection圖,希望可以給你帶來一定的 幫助,感謝你的瀏覽。
總結
以上是生活随笔為你收集整理的java collection详解_java 7 collection 详解(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑同步器(电脑同步器工作原理图解)
- 下一篇: 门边设置构造柱(门边构造柱要求多大柱子)