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

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

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:this

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集合中,則添加操做失敗。es5

eg:spa

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實現類中性能最好的,但它只能保存同一個枚舉類的枚舉值做爲集合元素。

相關文章
相關標籤/搜索