Java筆記:Java集合概述和Set集合


本文主要是Java集合的概述和Set集合

1.Java集合概述

1)數組可以保存多個對象,但數組長度不可變,一旦在初始化數組時指定了數組長度,這個數組長度就是不可變的,如果需要保存數量變化的數據,數組就有點無能為力了;而且數組無法保存
具有映射關系的數據。為了保存數量不確定的數據,以及保存具有映射關系的數據,Java提供了集合類。集合類主要負責保存、盛裝其他數據,因此集合類也被稱為容器類。
2)Java集合類可用於存儲數量不等的多個對象,並可以實現常用的數據結構,如棧、隊列等。還可以用於保存具有映射關系的關聯數組。Java集合大致可以分為Set、List、Map三種體系,
其中Set代表無序、不可重復的集合;List代表有序、重復的集合;Map則代表具有映射關系的集合。Queue體系集合代表一種隊列集合實現。
3)集合類和數組不一樣,數組元素既可以是基本類型的值,也可以是對象(實際上保存的是對象的引用變量);而集合類里只能保存對象(實際上保存的是對象的引用變量)。
4)Java集合類主要由兩個接口派生出:Collection和Map。Set和List接口是Collection接口派生的兩個子接口,他們分別代表了無序集合和有序集合;Queue是Java提供的隊列實現。
Map實現類用於保存具有映射關系的數據。Map保存的每項數據都是key-value對,也就是由key和value兩個值組成。Map里的key是不可重復的,key用於標識集合里的每項數據,如果需要查閱
Map中的數據時,總是根據Map的key來獲取。
5)Collection接口是List、Set和Queue接口的父接口,該接口里定義的方法既可以用於操作Set集合、也可以用於操作List集合和Queue集合。
boolean add(Object o):該方法用於向集合里添加一個元素。
boolean addAll(Collection c):該方法把集合c里的所有元素添加到指定集合里。
void clear():清除集合里的所有元素,將集合長度變為0。
boolean contains(Object o):返回集合里是否包含指定元素。
boolean containsAll(Collection c):返回集合里是否包含集合c里的所有元素。
boolean isEmpty():返回集合是否為空。當集合長度為0時返回true,否則返回false。
Iterator iterator():返回一個Iterator對象,用於遍歷集合里的元素。
boolean remove(Object o):刪除集合中的指定元素o,當集合中包含了一個或多個元素o時,這些元素將被刪除,該方法將返回true。
boolean removeAll(Collection c):將集合中刪除集合c里包含的所有元素(相當於用調用該方法的集合減集合c),如果刪除了一個或一個以上的元素,則該方法返回true。
boolean retainAll(Collection c):將集合中刪除集合c里不包含的元素(相當於把調用該方法的集合變成該集合的集合c的交集),如果該操作改變了調用該方法的集合,則該方法返回true。
int size():該方法返回集合里元素的個數。
Object[] toArray():該方法把集合轉換成一個數組,所有的集合元素變成對應的數組元素。
eg:

package cn.it.lsl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;

public class CollectionTest {
    public static void main(String[] args) {
        Collection c = new ArrayList();
        c.add("小明");
        c.add(6);
        System.out.println("c集合的元素個數為:"+c.size());
        c.remove(6);
        System.out.println("c集合的元素個數為:"+c.size());
        System.out.println("c集合是否包含\"小明\"字符串:"+c.contains("小明"));
        c.add("JavaEE");
        System.out.println("c集合的元素:"+c);
        
        Collection books = new HashSet();
        books.add("JavaEE");
        books.add("Android");
        System.out.println("c集合是否完全包含books集合?"+c.containsAll(books));
        c.removeAll(books);
        System.out.println("c集合的元素:"+c);
        c.clear();
        System.out.println("c集合的元素:"+c);
        //books集合里只剩下c集合里也包含的元素
        books.retainAll(c);
        System.out.println("books集合的元素:"+books);
        
    }
}

6)Iterator接口遍歷集合元素
Iterator接口也是Java集合框架的成員,主要用於遍歷Collection集合中的元素,Iterator對象也被稱為迭代器。
Iterator接口里定義了如下三個方法:
boolean hasNext():如果被迭代的集合元素還沒有被遍歷,則返回true。
Object next():返回集合里的下一個元素。
void remove():刪除集合里上一次next方法返回的元素。

eg:

package cn.it.lsl;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;

public class IteratorTest {
    public static void main(String[] args) {
        Collection books = new HashSet();
        books.add("Java ee");
        books.add("Java");
        books.add("Andrroid");
        //獲取books集合對應的迭代器
        Iterator it = books.iterator();
        while(it.hasNext()){
            //it.next()方法返回的數據類型是Object類型
            String book = (String)it.next();
            System.out.println(book);
            if(book.equals("Java")){
                it.remove();
            }
            book = "測試字符串";
        }
        System.out.println(books);
    }
}

如果要創建Iterator對象,則必須有一個被迭代的集合。

package cn.it.lsl;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;

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("Android")){
                //it.remove();
                books.remove(book);
            }
            //book = "測試字符串";
        }
        //System.out.println(books);
    }
}

//當使用Iterator迭代訪問Collection集合元素時,Colleection集合里的元素不能被改變,只有通過Iterator的remove方法刪除上一次next方法返回集合元素才可以。

eg:

package cn.it.lsl;

import java.util.Collection;
import java.util.HashSet;

public class ForeachTest {
    public static void main(String[] args) {
        Collection books = new HashSet();
        books.add("Java ee");
        books.add("Java");
        books.add("Android");
        for(Object obj : books){
            String book = (String)obj;
            System.out.println(book);
            if(book.equals("Android")){
                //以下代碼會引發異常
                //books.remove(book);
            }
        }
        System.out.println(books);
    }
}

2.Set集合
Set集合與Collection基本上完全一樣,它沒有提供任何額外的方法。實際上Set就是Collection,只是行為略有不同。(Set不允許包含重復元素)。
Set集合不允許包含相同的元素,如果試圖把兩個相同的元素加入同一個Set集合中,則添加操作失敗。

eg:

package cn.it.lsl;

import java.util.HashSet;
import java.util.Set;

public class SetTest {
    public static void main(String[] args) {
        Set books = new HashSet();
        books.add(new String("java"));
        boolean result = books.add(new String("java"));
        System.out.println(result + "-->" + books);
    }
}

1)HashSet類
(1)HashSet是Set接口的實現。HashSet按Hash算法來存儲集合中的元素,具有很好的存取和查找性能。
(2)HashSet不能保證元素的排列順序,順序可能與添加順序不同,順序也有可能發生變化。
(3)當向HashSet集合中存入一個元素時,HashSet會調用該對象的hashCode()方法來得到該對象的hashCode值,然后根據該HashCode值決定該對象在HashSet中的存儲位置。如果有兩個元素
通過equals()方法比較返回true,但它們的hashCode()方法返回值不相等,HashSet將會把它們存儲在不同的位置,依然可以添加成功。即,HashSet集合判斷兩個元素相等的標准是兩個
對象通過equals()方法比較相等,並且兩個對象的hashCode()方法返回值也相等。

eg:

package cn.it.lsl;

import java.util.HashSet;

class A{
    public boolean equals(Object obj){
        return true;
    }
}

class B{
    public int hashCode(){
        return 1;
    }
}

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.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中時,如果需要重寫該對象對應類的equals()方法,則也應該重寫其hashCode()方法。其規則是:如果兩個對象通過equals()方法比較返回true,則兩個對象的hashCode值也應該相同。

重寫hashCode()方法的基本規則:
1)在程序運行過程中,同一個對象多次調用hashCode()方法應該返回相同的值。
2)當兩個對象通過equals()方法比較返回true時,這兩個對象的hashCode()方法應返回相等的值。
3)對象中用作equals()方法比較標准的Field,都應該用來計算hashCode值。

如果向HashSet中添加一個可變對象后,后面程序修改了該可變對象的Field,則可能導致它與集合中的其他元素相同,這就可能導致HashSet中包含兩個相同的對象。

eg:

package cn.it.lsl;

import java.util.HashSet;
import java.util.Iterator;

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;
            if(r.count == this.count){
                return true;
            }
        }
        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));
        System.out.println(hs);
        Iterator it = hs.iterator();
        R first = (R)it.next();
        first.count = -3;
        System.out.println(hs);
        hs.remove(new R(-3));
        System.out.println(hs);
        System.out.println("hs是否包含count為-3的R對象?" + hs.contains(new R(-3)));
        System.out.println("hs是否包含count為5的R對象?" + hs.contains(new R(5)));
    }
}

當向HashSet中添加可變對象時,必須十分小心。如果修改HashSet集合中的對象,有可能導致該對象與集合中的其他對象相等,從而導致HashSet無法准確訪問該對象。

2)LinkedHashSet類
HashSet還有一個子類LinkedHashSet,LinkedHashSet集合也是根據元素的hashCode值來決定元素的存儲位置,但它同時使用鏈表維護元素的次序,這樣使得元素看起來是以插入的順序保存的。也就是說,當遍歷LinkedHashSet集合里的元素時,LinkedHashSet將會按元素的添加順序來訪問集合里的元素。

package cn.it.lsl;

import java.util.LinkedHashSet;

public class LinkedHashSetTest {
    public static void main(String[] args) {
        LinkedHashSet books = new LinkedHashSet();
        books.add("java");
        books.add("Android");
        System.out.println(books);
        books.remove("java");
        books.add("java");
        System.out.println(books);
    }
}

輸出LinkedHashSet集合的元素時,元素的順序總是與添加順序一致。

雖然LinkedHashSet使用了鏈表記錄集合元素的添加順序,但LinkedHashSet依然是HashSet,因此它依然不允許集合元素重復。

3)TreeSet類
TreeSet是SortedSet接口的實現類,可以確保集合元素處於排序狀態。
TreeSet中的幾個方法:
Object first():返回集合中的第一個元素。
Object last():返回集合中的最后一個元素。
Object lower(Object e):返回集合中位於指定元素之前的元素(即小於指定元素的最大元素,參數元素不需要是TreeSet集合里的元素)。
Object higher(Object e):返回集合中位於指定元素之后的元素(即大於指定元素的最小元素,參數元素不需要是TreeSet集合里的元素)。
SortedSet subSet(formElement,toElement):返回次Set的子集合,范圍從formElement(包含)到toElement(不包含)。
SortedSet headSet(toElement):返回此Set的子集,由小於toElement的元素組成。
SortedSet tailSet(fromElement):返回此Set的子集,由大於或等於fromElement的元素組成。

package cn.it.lsl;

import java.util.TreeSet;

public class TreeSetTree {
    public static void main(String[] args) {
        TreeSet nums = new TreeSet();
        nums.add(5);
        nums.add(2);
        nums.add(10);
        nums.add(-9);
        System.out.println(nums);
        System.out.println(nums.first());
        System.out.println(nums.last());
        System.out.println(nums.headSet(4));    //不包含4
        System.out.println(nums.tailSet(5));        //包含5
        System.out.println(nums.subSet(-3, 4));
    }
}

4)EnumSet類
EnumSet是一個專為枚舉類設計的集合類,EnumSet中的所有元素都必須是指定枚舉類型的枚舉值。
EnumSet類沒有暴露任何構造器來創建該類的實例,程序應該通過它提供的static方法來創建EnumSet對象。
static EnumSet allOf(Class elementType):創建一個包含指定枚舉類里所有枚舉值的EnumSet集合。
static EnumSet complementOf(EnumSet s):創建一個其元素類型與指定EnumSet里元素類型相同的EnumSet集合,新EnumSet集合包含原EnumSet集合所不包含的、此枚舉類剩下的枚舉值(
即新EnumSet集合和原EnumSet集合的集合元素加起來就是該枚舉類的所有枚舉值)。
static EnumSet copyOf(Collection c):使用一個普通集合來創建EnumSet集合。
static EnumSet copyOf(EnumSet s):創建一個與指定EnumSet具有相同元素類型、相同集合元素的EnumSet集合。
static EnumSet noneOf(Class elementType):創建一個元素類型為指定枚舉類型的空EnumSet。
static EnumSet of(E first, E...rest):創建一個包含一個或多個枚舉值的EnumSet集合,傳入的多個枚舉值必須屬於同一個枚舉類。
static EnumSet range(E from, E to):創建一個包含從from枚舉值到to枚舉值范圍內所有枚舉值的EnumSet集合。

eg:

package cn.it.lsl;

import java.util.EnumSet;

enum Season{
    SPRING,SUMMER,FAIL,WINTER
}
public class EnumSetTest {
    public static void main(String[] args) {
        EnumSet es1 = EnumSet.allOf(Season.class);
        System.out.println(es1);
        EnumSet es2 = EnumSet.noneOf(Season.class);
        System.out.println(es2);
        es2.add(Season.WINTER);
        es2.add(Season.SPRING);
        System.out.println(es2);
        EnumSet es3 = EnumSet.of(Season.SUMMER , Season.WINTER);
        System.out.println(es3);
        EnumSet es4 = EnumSet.range(Season.SUMMER, Season.WINTER);
        System.out.println(es4);
        EnumSet es5 = EnumSet.complementOf(es4);
        System.out.println(es5);
    }
}

復制另一個EnumSet集合中的所有元素來創建新的EnumSet集合,或者復制另一個Collection集合中的所有元素來創建新的EnumSet集合。當復制Collection集合中的所有元素來創建新的EnumSet集合時,要求Collection集合中的所有元素必須是同一個枚舉類的枚舉值。

package cn.it.lsl;

import java.util.Collection;
import java.util.EnumSet;
import java.util.HashSet;

public class EnumSetTest2 {
    public static void main(String[] args) {
        Collection c = new HashSet();
        c.clear();
        c.add(Season.FAIL);
        c.add(Season.SPRING);
        EnumSet enumSet = EnumSet.copyOf(c);
        System.out.println(enumSet);
//        c.add("java");
//        c.add("Android");
//        enumSet = EnumSet.copyOf(c);
    }
}

當試圖復制一個Collection集合里的元素來創建EnumSet集合時,必須保證Collection集合里的所有元素都是同一個枚舉類的枚舉值。

總結:

HashSet的性能總是比TreeSet好(特別是最常用的添加、查詢元素等操作),因為TreeSet需要額外的紅黑樹算法來維護集合元素的次序。只有當需要一個保持排序的Set時,才應該使用TreeSet,否則都應該使用HashSet。
對於普通的插入、刪除操作,LinkedHashSet比HashSet要略微慢一點,這是由維護鏈表所帶來的額外開銷造成的;不過,因為有了鏈表,遍歷LinkedHashSet會更快。
EnumSet是所有Set實現類中性能最好的,但它只能保存同一個枚舉類的枚舉值作為集合元素。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM