欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

集合框架(Collections Framework)詳解及代碼示例

 更新時(shí)間:2017年11月14日 11:44:34   作者:駿馬金龍  
這篇文章主要介紹了集合框架(Collections Framework)詳解及代碼示例,文章涉及集合數(shù)組的區(qū)別,collection接口,iterator迭代器,list接口及其用法,LinkedHashSet集合等有關(guān)內(nèi)容,具有一定參考價(jià)值,需要的朋友可以了解下。

簡(jiǎn)介

集合和數(shù)組的區(qū)別:

數(shù)組存儲(chǔ)基礎(chǔ)數(shù)據(jù)類(lèi)型,且每一個(gè)數(shù)組都只能存儲(chǔ)一種數(shù)據(jù)類(lèi)型的數(shù)據(jù),空間不可變。

集合存儲(chǔ)對(duì)象,一個(gè)集合中可以存儲(chǔ)多種類(lèi)型的對(duì)象??臻g可變。

嚴(yán)格地說(shuō),集合是存儲(chǔ)對(duì)象的引用,每個(gè)對(duì)象都稱(chēng)為集合的元素。根據(jù)存儲(chǔ)時(shí)數(shù)據(jù)結(jié)構(gòu)的不同,分為幾類(lèi)集合。但對(duì)象不管存儲(chǔ)到什么類(lèi)型的集合中,既然集合能存儲(chǔ)任何類(lèi)型的對(duì)象,這些對(duì)象在存儲(chǔ)時(shí)都必須向上轉(zhuǎn)型為Object類(lèi)型,也就是說(shuō),集合中的元素都是Object類(lèi)型的對(duì)象。

既然是集合,無(wú)論分為幾類(lèi),它都有集合的共性,也就是說(shuō)雖然存儲(chǔ)時(shí)數(shù)據(jù)結(jié)構(gòu)不一樣,但該有的集合方法還是得有。在java中,Collection接口是集合框架的根接口,所有集合的類(lèi)型都實(shí)現(xiàn)了此接口或從其子接口中繼承。

Collection接口

根據(jù)數(shù)據(jù)結(jié)構(gòu)的不同,一些collection允許有重復(fù)的元素,而另一些則不允許。一些collection是有序的,而另一些則是無(wú)序的。

Java SDK不提供直接繼承自Collection的類(lèi),Java SDK提供的類(lèi)都是繼承自Collection的"子接口"如List和Set。也就是說(shuō),無(wú)法直接new一個(gè)collection對(duì)象,而是只能new一個(gè)實(shí)現(xiàn)Collection類(lèi)的子接口的對(duì)象,如new ArrayList();。

所有的Collection類(lèi)都必須至少提供兩個(gè)構(gòu)造方法:無(wú)參數(shù)構(gòu)造方法構(gòu)造一個(gè)空集合;帶Collection參數(shù)的構(gòu)造方法構(gòu)造一個(gè)包含該Collection內(nèi)容的集合。例如,ArrayList就有3個(gè)構(gòu)造方法,其中之二就滿(mǎn)足這兩個(gè)構(gòu)造方法的要求。

Collection是java.util包中的類(lèi),因此要實(shí)現(xiàn)集合的概念,需要先導(dǎo)入該包。

ArrayList繼承自L(fǎng)ist接口,List接口又繼承自Collection接口。ArrayList類(lèi)存儲(chǔ)的集合中,元素有序、可重復(fù)。

import java.util.*;
Collection coll = new ArrayList();

因?yàn)镃ollection接口不允許直接實(shí)現(xiàn),因此需要通過(guò)實(shí)現(xiàn)它的子類(lèi)來(lái)實(shí)現(xiàn)集合的概念,此處創(chuàng)建的是ArrayList對(duì)象,使用了父類(lèi)引用,好處是擴(kuò)展性較好。

Collection有一些集合的通用性操作方法,分為兩類(lèi):一類(lèi)是普通方法;一類(lèi)是帶有All的方法,這類(lèi)方法操作的是集合。

add():向集合的尾部插入元素,返回值類(lèi)型為boolean,插入成功返回true。注意集合只能存儲(chǔ)對(duì)象(實(shí)際上是對(duì)象的引用)。

import java.util.*;
//
public class TestColl {
	public static void main(String[] args) {
		Collection coll = new ArrayList();
		coll.add("abcd");
		//插入字符串對(duì)象
		coll.add(123);
		//插入Int對(duì)象
		coll.add(123);
		coll.add(new Student("Gaoxiaof",23));
		//插入Student對(duì)象
		coll.add(new Student("Gaoxiaof",23));
		//插入另一個(gè)Student對(duì)象
		System.out.println(coll);
		//直接輸出集合中的元素,得到結(jié)果[abcd,123,123,Gaoxiaof 23,Gaoxiaof 23]
	}
}
//
class Student {
	private String name;
	private int age;
	Student(String name,int n) {
		this.name = name;
		this.age = n;
	}
	public String getName() {
		return this.name;
	}
	public int getAge() {
		return this.age;
	}
	public String toString() {
		return this.name + " " + this.age;
	}
}

上面插入的"abcd"和"123"都是經(jīng)過(guò)自動(dòng)裝箱轉(zhuǎn)換為對(duì)象后存儲(chǔ)在集合中的。其中兩個(gè)add(123)是重復(fù)的對(duì)象元素,因?yàn)榕袛嗉现械脑厥欠裰貜?fù)的唯一方法是equals方法是否返回0。Integer已經(jīng)重寫(xiě)過(guò)equals()。而后面的兩個(gè)Student對(duì)象是不同對(duì)象,因?yàn)镾tudent類(lèi)中沒(méi)有重寫(xiě)equals()方法,所以它們是不重復(fù)的元素。

remove():刪除集合中首次出現(xiàn)的元素。確定是否能刪除某個(gè)元素,唯一的方法是通過(guò)equals()方法確定對(duì)象是否相等,相等時(shí)刪除才返回true。

Collection coll = new ArrayList();
coll.add("abcd");
coll.add(new Integer(128));
coll.add(new Student("Gaoxiaofang",23));
System.out.println(coll.remove(new Integer(128))); //true
coll.remove(new Student("Gaoxiaofang",23));     //false,因?yàn)闆](méi)有重寫(xiě)equals()
System.out.println(coll);  //return: [abcd,Gaoxiaofang 23]

clear():清空該集合中的所有元素。
contains(object obj):是否包含某對(duì)象元素。判斷的依據(jù)仍然是equals()方法。

Collection coll = new ArrayList();
coll.add(new Integer(128));
System.out.println(coll.contains(new Integer(128))); //true

isEmpty():集合是否不包含任何元素。
size():返回該集合中元素的個(gè)數(shù)。
equals(Object obj):比較兩個(gè)集合是否完全相等。依據(jù)是集合中的所有元素都能通過(guò)各自的equals得到相等的比較。
addAll(Collection c):將整個(gè)集合c中的元素都添加到該集合中。
containsAll(Collection c):該集合是否包含了c集合中的所有元素,即集合c是否是該集合的子集。
removeAll(Collection c):刪除該集合中那些也包含在c集合中的元素。即刪除該集合和c集合的交集元素。
retainAll(Collection c):和removeAll()相反,僅保留該集合中和集合c交集部分的元素。
iterator(Collection c):返回此集合中的迭代器,注意返回值類(lèi)型為Iterator。迭代器用于遍歷集合。見(jiàn)下文。

Iterator通用迭代器

因?yàn)椴煌?lèi)型的集合存儲(chǔ)數(shù)據(jù)時(shí)數(shù)據(jù)結(jié)構(gòu)不同,想要寫(xiě)一個(gè)通用的遍歷集合的方法是不現(xiàn)實(shí)的。但無(wú)論是哪種類(lèi)型的集合,只有集合自身對(duì)集合中的元素是最了解的,因此在實(shí)現(xiàn)Collection接口時(shí),不同集合類(lèi)都實(shí)現(xiàn)了自己獨(dú)有的遍歷方法,這稱(chēng)為集合的迭代器Iterator。其實(shí)Collection繼承了java.lang.Iterable接口,該接口只提供了一個(gè)方法:iterator(),只要是實(shí)現(xiàn)了這個(gè)接口的類(lèi)就表示具有迭代的能力,也就具有foreach增強(qiáng)遍歷的能力。

迭代器自身是一個(gè)接口,通過(guò)Collection對(duì)象的iterator()方法就可以獲取到對(duì)應(yīng)集合類(lèi)型的迭代器。例如:

Collection coll = new ArrayList();
Iterator it = coll.iterator(); //獲取對(duì)應(yīng)類(lèi)型的集合的迭代器

Iterator接口提供了3個(gè)方法:

hasNext():判斷是否有下一個(gè)元素。
Next():獲取下一個(gè)元素。注意它返回的是Object(暫不考慮泛型)類(lèi)型。
remove():移除迭代器最后返回的一個(gè)元素。此方法為Collection迭代過(guò)程中修改元素的唯一安全的方法。

雖然有不同種類(lèi)型的集合,但迭代器的迭代方法是通用的。例如,要遍歷coll集合中的元素。

import java.util.*;
public class TestColl {
  public static void main(String[] args) {
    Collection coll = new ArrayList();
    coll.add("abcd");
    coll.add(new Integer(129));
    coll.add(new Student("Gaoxiaofang",23));
    Iterator it = coll.iterator();  
     while (it.hasNext()) {       //Iterator遍歷的方法
      System.out.println(it.next()); //return:abcd,129,Gaoxiaofang 23
    } 
  }
}
class Student {
  private String name;
  private int age;
  Student(String name,int n) {
    this.name = name;
    this.age = n;
  }
  public String getName() {
    return this.name;
  }
  public int getAge() {
    return this.age;
  }
  public String toString() {
    return this.name + " " + this.age;
  }
}

但是通常來(lái)說(shuō),上面的遍歷方式雖然正確,但下面的遍歷方式更佳。因?yàn)閕t對(duì)象只用于集合遍歷,遍歷結(jié)束后就應(yīng)該消失,所以將其放入到for循環(huán)的內(nèi)部,由于for循環(huán)的第三個(gè)表達(dá)式缺失,所以不斷地循環(huán)第二個(gè)表達(dá)式即可。

for (Iterator it = coll.iterator();it.hasNext();) {
System.out.println(it.next());
}

通過(guò)Iterator遍歷到的元素是集合中的一個(gè)對(duì)象,對(duì)象也是有屬性的。如何引用這些屬性?只需將遍歷出的元素作為對(duì)象來(lái)使用即可,但由于next()返回的元素都是Object對(duì)象,直接操作這個(gè)元素對(duì)象無(wú)法獲取對(duì)應(yīng)元素中的特有屬性。因此必須先強(qiáng)制對(duì)象類(lèi)型轉(zhuǎn)換。

例如,獲取coll中為Student對(duì)象元素的name屬性,并刪除非Student對(duì)象的元素。

Collection coll = new ArrayList();
coll.add("abcd");
coll.add(new Integer(129));
coll.add(new Student("Gaoxiaofang",23));

for (Iterator it = coll.iterator();it.hasNext();) {
  Object obj = it.next();
  if (obj instanceof Student) {
    Student s = (Student)obj;
    System.out.println(s.getName()); //return: Gaoxiaofang
  } else {
    it.remove();
  }
}
System.out.println(coll);        //return: [Gaoxiaofang 23]

因?yàn)榧现杏行┓荢tudent對(duì)象元素,因此需要判斷it.next()是否滿(mǎn)足instanceof的要求,但不能直接寫(xiě)為下面的代碼:

for (Iterator it = coll.iterator();it.hasNext();) {
  if (it.next() instanceof Student) {
    Student s = (Student)it.next();
    System.out.println(s.getName());
  }
}

因?yàn)槊繄?zhí)行一次it.next(),元素的游標(biāo)指針就向下滑動(dòng)1,在這個(gè)寫(xiě)法中if判斷表達(dá)式中使用了一次it.next(),在if的代碼塊中又調(diào)用了一次it.next()。所以應(yīng)該將it.next()保存到對(duì)象變量中。而it.next()返回的類(lèi)型是Object類(lèi)型,因此定義Object obj = it.next()。

只有remove()方法是Iterator迭代器迭代過(guò)程中修改集合元素且安全的方法。以迭代時(shí)add()為例,當(dāng)開(kāi)始迭代時(shí),迭代器線(xiàn)程獲取到集合中元素的個(gè)數(shù),當(dāng)?shù)^(guò)程中執(zhí)行add()時(shí),它將采用另一個(gè)線(xiàn)程來(lái)執(zhí)行(因?yàn)閍dd()方法不是Iterator接口提供的方法),結(jié)果是元素個(gè)數(shù)就增加了,且導(dǎo)致新增的元素?zé)o法確定是否應(yīng)該作為迭代的一個(gè)元素。這是不安全的行為,因此會(huì)拋出ConcurrentModificationException異常。而remove()方法是迭代器自身的方法,它會(huì)使用迭代器線(xiàn)程來(lái)執(zhí)行,因此它是安全的。

對(duì)于List類(lèi)的集合來(lái)說(shuō),可以使用Iterator的子接口ListIterator來(lái)實(shí)現(xiàn)安全的迭代,該接口提供了不少增刪改查L(zhǎng)ist類(lèi)集合的方法。

List接口

List接口實(shí)現(xiàn)了Collection接口。

List接口的數(shù)據(jù)結(jié)構(gòu)特性是:

1.有序列表,且?guī)饕齣ndex。所謂有序指先后插入的順序,即Index決定順序。而向Set集合中插入數(shù)據(jù)會(huì)被打亂
2.大小可變。
3.數(shù)據(jù)可重復(fù)。
4.因?yàn)橛行蚝痛笮】勺?,使得它除了有Collection的特性,還有根據(jù)index精確增刪改查某個(gè)元素的能力。
5.實(shí)現(xiàn)List接口的兩個(gè)常用類(lèi)為:
(1).ArrayList:數(shù)組結(jié)構(gòu)的有序列表;
1).長(zhǎng)度可變,可變的原因是在減少或添加元素時(shí)部分下標(biāo)整體減一或加一,如果已分配數(shù)組空間不夠,則新創(chuàng)建一個(gè)更大的數(shù)組,并拷貝原數(shù)組的內(nèi)存(直接內(nèi)存拷貝速度極快);
2).查詢(xún)速度快,增刪速度慢。查詢(xún)快是因?yàn)閮?nèi)存空間連續(xù),增刪速度慢是因?yàn)橄聵?biāo)移動(dòng)。
3).除了ArrayList是不同步列表,它幾乎替代了Vector類(lèi)。
(2).LinkedList:鏈表結(jié)構(gòu)的有序列表;
1).不同步;
2).增刪速度快,查詢(xún)速度慢。增刪速度快的原因是只需修改下鏈表中前后兩個(gè)元素的索引指向即可;
3).能夠?qū)崿F(xiàn)堆棧(后進(jìn)先出LIFO,last in first out)、隊(duì)列(queue,通常是FIFO,first in first out)和雙端隊(duì)列(double ends queue)。

ArrayList類(lèi)的方法和List方法基本一致,所以下面介紹了List通用方法和ListIterator就沒(méi)必要介紹ArrayList。但LinkedList比較特殊,所以獨(dú)立介紹。

List接口通用方法

除了因?yàn)槔^承了Collection而具有的通用方法外,對(duì)于List接口也有它自己的通用方法。一般List的這些通用方法針對(duì)的是序列的概念。有了序列和下標(biāo)索引值,可以精確地操控某個(gè)位置的元素,包括增刪改查。

(1).增:add(index,element)
(2).刪:remove(index)、remove(obj)刪除列表中第一個(gè)obj元素
(3).改:set(index,element)
(4).查:get(index)
(5).indexOf(obj):返回列表中第一次出現(xiàn)obj元素的索引值,如不存在則返回-1
(6).lastIndexOf(obj)
(7).subList(start,end):返回列表中從start到end(不包括end邊界)中間的元素組成列表。注意返回的是List類(lèi)型。
(8).listIterator():返回從頭開(kāi)始遍歷的List類(lèi)集合的迭代器ListIterator。
(9).listIterator(index):返回從index位置開(kāi)始遍歷的List結(jié)合迭代器ListIterator。
因?yàn)橛辛薵et()方法,除了Iterator迭代方式,還可以使用get()方法遍歷集合:

List l = new ArrayList();
for (int i=0;i<l.size();i++) {
  System.out.println(l.get(i));
}

但注意,這種方法不安全,因?yàn)閘.size()是即時(shí)改變的,如果增刪了元素,size()也會(huì)隨之改變。

示例:

import java.util.*;
public class TestList {
	public static void main(String[] args) {
		List ls = new ArrayList();
		ls.add(new Student("Malong1",21));
		ls.add(new Student("Malong2",22));
		ls.add(1,new Student("Malong3",23));
		//[Malong1 21,Malong3 23,Malong2 22]
		System.out.println(ls.indexOf(new Student("Malong3",23)));
		// return:1
		ls.set(2,new Student("Gaoxiao1",22));
		//[Malong1 21,Malong3 23,Gaoxiao1 22]
		for (Iterator it = l.iterator();it.hasNext();) {
			//第一種迭代
			Student stu = (Student)it.next();
			if (stu.getAge() == 22) {
				it.remove();
				// the safe way to operate element
				//ls.add(new Student("Malong4",24)); //throw ConcurrentModificationException
			}
		}
		//[Malong1 21,Malong3 23]
		System.out.println(l+"\n---------------");
		for (int i=0;i<ls.size();i++) {
			//第二種迭代
			System.out.println(ls.get(i));
		}
	}
}
class Student {
	private String name;
	private int age;
	Student(String name,int n) {
		this.name = name;
		this.age = n;
	}
	public String getName() {
		return this.name;
	}
	public int getAge() {
		return this.age;
	}
	//override toString()
	public String toString() {
		return this.name + " " + this.age;
	}
	//override equals()
	public Boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (!(obj instanceof Student)) {
			throw new ClassCastException("Class error");
		}
		Student stu = (Student)obj;
		return this.name.equals(stu.name) && this.age == stu.age;
	}
}

上面的代碼中,如果將ls.add(new Student("Malong4",24));的注釋取消,將拋出異常,因?yàn)镮terator迭代器中唯一安全操作元素的方法是Iterator接口提供的remove(),而add()方法是List接口提供的,而非Iterator接口的方法。但對(duì)于List集合類(lèi)來(lái)說(shuō),可以使用ListIterator迭代器,它提供的操作元素的方法更多,因?yàn)槭堑魈峁┑姆椒?,因此它們操作元素時(shí)都是安全的。

List集合的迭代器ListIterator

通過(guò)listIterator()方法可以獲取ListIterator迭代器。該迭代器接口提供了如下幾種方法:

hasNext():是否有下一個(gè)元素
hasPrevious():是否有前一個(gè)元素,用于逆向遍歷
next():獲取下一個(gè)元素
previour():獲取前一個(gè)元素,用于逆向遍歷
add(element):插入元素。注:這是迭代器提供的add(),而非List提供的add()
remove():移除next()或previous()獲取到的元素。注:這是迭代器提供的remove(),而非List提供的remove()
set(element):設(shè)置next()或previour()獲取到的元素。注:這是迭代器提供的set(),而非List提供的set()

例如:前文示例在Iterator迭代過(guò)程中使用List的add()添加元素拋出了異常,此處改用ListIterator迭代并使用ListIterator提供的add()方法添加元素。

List l = new ArrayList();
l.add(new Student("Malong1",21));
l.add(new Student("Malong2",22));
l.add(1,new Student("Malong3",23)); //[Malong1 21,Malong3 23,Malong2 22]
l.set(2,new Student("Gaoxiao1",22));//[Malong1 21,Malong3 23,Gaoxiao1 22]

for (ListIterator li = l.listIterator();li.hasNext();) {
  Student stu = (Student)li.next();
  if (stu.getAge() == 22) {
    //l.add(new Student("Malong4",24));  //throw ConcurrentModificationException
    li.add(new Student("Malong4",24));
  }
}

LinkedList集合

LinkedList類(lèi)的數(shù)據(jù)結(jié)構(gòu)是鏈表類(lèi)的集合。它可以實(shí)現(xiàn)堆棧、隊(duì)列和雙端隊(duì)列的數(shù)據(jù)結(jié)構(gòu)。其實(shí)實(shí)現(xiàn)這些數(shù)據(jù)結(jié)構(gòu)都是通過(guò)LinkedList提供的方法按照不同邏輯實(shí)現(xiàn)的。

提供的其中幾個(gè)方法如下:因?yàn)槭菍?shí)現(xiàn)了List接口,所以除了下面的方法,還有List接口的方法可用。

addFirst(element):向鏈表的首部插入元素
addLast(element):向鏈表的尾部插入元素
getFirst():獲取鏈表的第一個(gè)元素
getLast():獲取鏈表最后一個(gè)元素
removeFirst():移除并返回第一個(gè)元素,注意返回的是元素
removeLast():移除并返回最后一個(gè)元素,注意返回的是元素
LinkedList模擬隊(duì)列數(shù)據(jù)結(jié)構(gòu)

隊(duì)列是先進(jìn)先出FIFO的數(shù)據(jù)結(jié)構(gòu)。封裝的隊(duì)列類(lèi)MyQueue代碼如下:

import java.util.*;
class MyQueue {
	private LinkedList mylist;
	MyQueue() {
		mylist = new LinkedList();
	}
	// add element to queue
	public void add(Object obj) {
		mylist.addFirst(obj);
		//Fisrt In
	}
	//get element from queue
	public Object get() {
		return mylist.removeLast();
		//First Out
	}
	//queue is null?
	public Boolean isNull() {
		return mylist.isEmpty();
	}
	//the size of queue
	public int size() {
		return mylist.size();
	}
	//remove element in queue by index
	public Boolean remove(int index) {
		if(this.size()-1 < index) {
			throw new IndexOutOfBoundsException("index too large!");
		}
		mylist.remove(index);
		return true;
	}
	//remove the first appearance element in queue by Object
	public Boolean remove(Object obj) {
		return mylist.remove(obj);
	}
	public String toString() {
		return mylist.toString();
	}
}

操作該隊(duì)列數(shù)據(jù)結(jié)構(gòu)程序代碼如下:

import java.util.*;

public class FIFO {
  public static void main(String[] args) {
    MyQueue mq = new MyQueue();
    mq.add("Malong1");
    mq.add("Malong2");
    mq.add("Malong3");
    mq.add("Malong4");  //[Malong4,Malong3,Malong2,Malong1]
    System.out.println(mq.size()); //return:4
    mq.remove(2);          //[Malong4,Malong3,Malong1]
    mq.remove("Malong1");      //[Malong4,Malong3]
    System.out.println(mq);

    while (!mq.isNull()) {
      System.out.println(mq.get());
    }
  }
}

Set接口

Set接口也實(shí)現(xiàn)了Collection接口。它既然能單獨(dú)成類(lèi),它和List集合的數(shù)據(jù)結(jié)構(gòu)一定是大有不同的。

Set接口的數(shù)據(jù)結(jié)構(gòu)特性是:

1.Set集合中的元素?zé)o序。這里的無(wú)序是相對(duì)于List而言的,List的有序表示有下標(biāo)Index的順序,而Set無(wú)需是指沒(méi)有index也就沒(méi)有順序。
2.Set集合中的元素不可重復(fù)。
3.因?yàn)闊o(wú)序,因此Set集合中取出元素的方法只有一種:迭代。
4.實(shí)現(xiàn)Set接口的兩個(gè)常見(jiàn)類(lèi)為:
(1).HashSet:hash表數(shù)據(jù)結(jié)構(gòu);
1).不同步;
2).查詢(xún)速度快;
3).判斷元素是否重復(fù)的唯一方法是:先調(diào)用hashcode()判斷對(duì)象是否相同,相同者再調(diào)用equals()方法判斷內(nèi)容是否相同。所以,要將元素存儲(chǔ)到此數(shù)據(jù)結(jié)構(gòu)的集合中,必須重寫(xiě)hashcode()和equals()。
(2).TreeSet:二叉樹(shù)數(shù)據(jù)結(jié)構(gòu);
1).二叉樹(shù)是用來(lái)排序的,因此該集合中的元素是有序的。這個(gè)有序和List的有序概念不同,此處的有序指的是存儲(chǔ)時(shí)對(duì)元素進(jìn)行排序,例如按照字母順序,數(shù)字大小順序等,而非index索引順序。
2).既然要排序,而equals()方法只能判斷是否相等。因此數(shù)據(jù)存儲(chǔ)到TreeSet集合中時(shí)需要能夠判斷大小。
3).有兩種方法用于構(gòu)造有序的TreeSet集合:
a.待存儲(chǔ)對(duì)象的類(lèi)實(shí)現(xiàn)Comparable接口并重寫(xiě)它的compareTo()方法;
b.在構(gòu)造TreeSet集合時(shí)指定一個(gè)比較器comparator。這個(gè)比較器需要實(shí)現(xiàn)Comparator接口并重寫(xiě)compare()方法。
(3).LinkedHashSet:鏈表形式的HashSet,僅在HashSet上添加了鏈表索引。因此此類(lèi)集合有序(Linked)、查詢(xún)速度快(HashSet)。不過(guò)很少使用該集合類(lèi)型。

HashSet集合

HashSet的用法沒(méi)什么可解釋的,方法都繼承自Set再繼承自Collection。需要說(shuō)明的是它的無(wú)序性、不可重復(fù)性、計(jì)算hash值時(shí)的方法以及判斷重復(fù)性時(shí)的方法。

import java.util.*;

public class TestHashSet {
  public static void main(String[] args) {
    Set s = new HashSet();
    s.add("abcd4");
    s.add("abcd1");
    s.add("abcd2");
    s.add("abcd3");
    s.add("abcd1"); //重復(fù)

    for (Iterator it = s.iterator();it.hasNext();) {
      Object obj = it.next();
      System.out.println(obj);
    }
  }
}

得到的結(jié)果是無(wú)序且元素是不可重復(fù)的:

abcd2
abcd3
abcd4
abcd1

這里判斷字符串對(duì)象是否重復(fù)的方法是先調(diào)用String的hashcode()進(jìn)行判斷,如果相同,再調(diào)用String的equals()方法。其中String的hashcode()方法在計(jì)算hash值時(shí),是根據(jù)每個(gè)字符計(jì)算的,相同字符位置處的相同字符運(yùn)算結(jié)果相同。

所以上面幾個(gè)字符串對(duì)象中,前綴"abcd"子串部分的hash運(yùn)算結(jié)果相同,最后一個(gè)字符決定了這些字符串對(duì)象是否相同。插入時(shí)有兩個(gè)"abcd1",所以總共調(diào)用了一次String的equals()方法。

如果是存儲(chǔ)自定義的對(duì)象,如Student對(duì)象,該對(duì)象定義方式如下:

class Student {
  String name;
  int age;
  Student(String name,int n) {
    this.name = name;
    this.age = n;
  }

  //override toString()
  public String toString() {
    return this.name + " " + this.age;
  }

  //override equals()
  public boolean equals(Object obj) {
    if (this == obj) {
      return true;
    }
    if (!(obj instanceof Student)) {
      return false;
    }
    Student stu = (Student)obj;
    return this.name.equals(stu.name) && this.age == age;
  }
}

即使重寫(xiě)了equals(),插入屬性相同的Student對(duì)象到HashSet中時(shí),也會(huì)認(rèn)為不重復(fù)的。

import java.util.*;

public class TestHashSet {
  public static void main(String[] args) {
    Set s = new HashSet();
    s.add(new Student("Malong1",21));
    s.add(new Student("Malong1",21));
    s.add(new Student("Malong1",21));

    for (Iterator it = s.iterator();it.hasNext();) {
      Object obj = it.next();
      System.out.println(obj);
    }
  }
}

結(jié)果:

Malong1 21
Malong1 21
Malong1 21

這是因?yàn)镠astSet集合的底層首先調(diào)用Student的hashcode()方法,而Student沒(méi)有重寫(xiě)該方法,而是繼承自O(shè)bject,所以每個(gè)對(duì)象的hashcode()都不相同而直接插入到集合中。

因此,需要重寫(xiě)Student的hashcode()方法。以下是一種重寫(xiě)方法:

public int hashCode() {
  return this.name.hashCode() + age*31; //31可以是任意數(shù),但不能是1或0。
}

如果不加上"age*31",那么name部分的hash值有可能是相同的,但這很可能不是同一Student對(duì)象,所以應(yīng)該加上age屬性作為計(jì)算hash值的一部分元素。但不能直接加age,因?yàn)檫@樣會(huì)導(dǎo)致"new Student("lisi3",23)"和"new Student("lisi2",24)"的hashcode相同(3+23=2+24),因此需要為age做一些修改,例如乘一個(gè)非0和1的整數(shù)。

在Student中重寫(xiě)hashCode()后,再插入下面這些Student對(duì)象,就能相對(duì)精確地判斷是否為同一個(gè)Student元素。

s.add(new Student("lisi1",21));
s.add(new Student("lisi1",21)); //此處將調(diào)用equals(),且最終判斷為重復(fù)對(duì)象
s.add(new Student("lisi2",24));
s.add(new Student("lisi3",23)); //此處將調(diào)用equals()
s.add(new Student("Gaoxiao1",23));
s.add(new Student("Gaoxiao2",21));
s.add(new Student("Gaoxiao3",22));

結(jié)果:

lisi1 21
Gaoxiao1 23
Gaoxiao3 22
lisi2 24
lisi3 23
Gaoxiao2 21

LinkedHashSet集合

鏈表順序的HashSet集合,相比HashSet,只需多記錄一個(gè)鏈表索引即可,這就使得它保證了存儲(chǔ)順序和插入順序相同。實(shí)現(xiàn)方式除了new對(duì)象時(shí)和HashSet不一樣,其他任何地方都是一樣的。

import java.util.*;

public class TestHashSet {
  public static void main(String[] args) {
    Set s = new LinkedHashSet();

    s.add(new Student("lisi1",21));
    s.add(new Student("lisi1",21));
    s.add(new Student("lisi2",24));
    s.add(new Student("lisi3",23));
    s.add(new Student("Gaoxiao1",23));
    s.add(new Student("Gaoxiao3",21));
    s.add(new Student("Gaoxiao2",22)); 

    for (Iterator it = s.iterator();it.hasNext();) {
      Object obj = it.next();
      System.out.println(obj);
    }
  }
}

結(jié)果:

lisi1 21
lisi2 24
lisi3 23
Gaoxiao1 23
Gaoxiao3 21
Gaoxiao2 22

TreeSet集合

TreeSet集合以二叉樹(shù)數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)元素。二叉樹(shù)保證了元素之間是排過(guò)序且相互唯一的,因此實(shí)現(xiàn)TreeSet集合最核心的地方在于對(duì)象之間的比較。

比較對(duì)象有兩種方式:一是在對(duì)象類(lèi)中實(shí)現(xiàn)Comparable接口重寫(xiě)compareTo()方法;二是定義一個(gè)專(zhuān)門(mén)用于對(duì)象比較的比較器,實(shí)現(xiàn)這個(gè)比較器的方法是實(shí)現(xiàn)Comparator接口并重寫(xiě)compare()方法。其中Comparable接口提供的比較方法稱(chēng)為自然順序,例如字母按照字典順序,數(shù)值按照數(shù)值大小順序。

無(wú)論是哪種方式,每個(gè)待插入的元素都需要先轉(zhuǎn)型為Comparable,確定了將要存儲(chǔ)在二叉樹(shù)上的節(jié)點(diǎn)位置后,然后再轉(zhuǎn)型為Object存儲(chǔ)到集合中。

插入String類(lèi)對(duì)象。

由于String已經(jīng)重寫(xiě)了compareTo(),因此下面插入String對(duì)象到TreeSet集合中沒(méi)有任何問(wèn)題。

import java.util.*;
//
public class TestTreeSet {
 public static void main(String[] args) {
   Set t = new TreeSet();

   t.add("abcd2");
   t.add("abcd11");
   t.add("abcd3");
   t.add("abcd1");
   //t.add(23);
   //t.add(21);
   //t.add(21);

   for (Iterator it = t.iterator();it.hasNext();) {
     Object obj = it.next();
     System.out.println(obj);
   }
 }
}

但不能將上面"t.add(23)"等取消注釋?zhuān)m然Integer類(lèi)也重寫(xiě)了compareTo(),但在插入這些Integer類(lèi)元素時(shí),集合中已經(jīng)存在String類(lèi)的元素,String類(lèi)的compareTo()和Integer的compareTo()的比較方法不一樣,使得這兩類(lèi)元素之間無(wú)法比較大小,也就無(wú)法決定數(shù)值類(lèi)的元素插入到二叉樹(shù)的哪個(gè)節(jié)點(diǎn)。

插入實(shí)現(xiàn)了Comparable接口且重寫(xiě)了compareTo()的自定義對(duì)象。
例如Student對(duì)象,如果沒(méi)有重寫(xiě)compareTo()方法,將拋出異常,提示無(wú)法轉(zhuǎn)型為Comparable。

t.add(new Student("Malongshuai1",23));

結(jié)果:

Exception in thread "main" java.lang.ClassCastException: Student cannot be cast to java.lang.Comparable
   at java.util.TreeMap.compare(Unknown Source)
   at java.util.TreeMap.put(Unknown Source)
   at java.util.TreeSet.add(Unknown Source)
   at TestTreeSet.main(TestTreeSet.java:8)

所以,修改Student重寫(xiě)compareTo(),在重寫(xiě)應(yīng)該考慮哪個(gè)作為主排序?qū)傩裕膫€(gè)作為次要排序?qū)傩?。例如以name為主排序?qū)傩?,age為次排序?qū)傩浴ompareTo()返回正數(shù)則表示大于,返回負(fù)數(shù)則表示小于,返回0則表示等于。如下:

class Student implements Comparable {
 String name;
 int age;
 Student(String name,int n) {
   this.name = name;
   this.age = n;
 }

 public String toString() {
   return this.name + " " + this.age;
 }

 public int compareTo(Object obj) {
   if (!(obj instanceof Student)) {
     throw new ClassCastException("Class cast wrong!");
   }
   Student stu = (Student)obj;
   // compare name first, then age
   int temp = this.name.compareTo(stu.name);
   return temp == 0 ? this.age - stu.age :temp; 
 }
}

于是插入Student時(shí),將根據(jù)name中的字母順序,相同時(shí)再根據(jù)age大小順序,最后如果都相同,則認(rèn)為元素重復(fù),不應(yīng)該插入到集合中。

t.add(new Student("Malongshuai1",23));
t.add(new Student("Malongshuai3",21));
t.add(new Student("Malongshuai2",23));
t.add(new Student("Malongshuai1",23)); //重復(fù)
t.add(new Student("Malongshuai1",22));

結(jié)果:

Malongshuai1 22
Malongshuai1 23
Malongshuai2 23
Malongshuai3 21

使用比較器comparator實(shí)現(xiàn)排序。此時(shí)TreeSet的構(gòu)造方法為"TreeSet(Comparator comp)"。
當(dāng)使用了比較器后,插入數(shù)據(jù)時(shí)將默認(rèn)使用比較器比較元素。

比較器是一個(gè)實(shí)現(xiàn)了java.util.Comparator接口并重寫(xiě)了compare()方法的類(lèi),可以根據(jù)不同比較需求,創(chuàng)建不同的比較器。 例如創(chuàng)建一個(gè)根據(jù)age作為主排序?qū)傩?,name作為次排序?qū)傩缘谋容^器SortByAge,由于這個(gè)比較器是用來(lái)比較Student對(duì)象大小的,因此必須先轉(zhuǎn)型為Student。

import java.util.*;
//
public class SortByAge implements Comparator {
 public int compare(Object o1,Object o2) {
   //Cast to Student first
   if (!(o1 instanceof Student) || !(o2 instanceof Student)) {
     throw new ClassCastException("Wrong");
   }
   Student s1 = (Student)o1;
   Student s2 = (Student)o2;
   //compare age first, then name
   int temp = s1.age - s2.age;
   return temp == 0 ? s1.name.compareTo(s2.name) : temp;
 }
}

指定TreeSet的比較器為SortByAge,并插入一些Student對(duì)象:

public class TestTreeSet {
 public static void main(String[] args) {
   Set t = new TreeSet(new SortByAge());

   t.add(new Student("Malongshuai1",23));
   t.add(new Student("Malongshuai3",21));
   t.add(new Student("Malongshuai2",23));
   t.add(new Student("Malongshuai1",23)); //重復(fù)
   t.add(new Student("Malongshuai1",22));

   for (Iterator it = t.iterator();it.hasNext();) {
     Object obj = it.next();
     System.out.println(obj);
   }
 }
}

當(dāng)為T(mén)reeSet集合指定了比較器時(shí),結(jié)果將先按照age順序再按照name排序的,盡管Student類(lèi)中仍然重寫(xiě)了compareTo()方法:

Malongshuai3 21
Malongshuai1 22
Malongshuai1 23
Malongshuai2 23

總結(jié)

以上就是本文關(guān)于集合框架(Collections Framework)詳解及代碼示例的全部?jī)?nèi)容,希望對(duì)大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站:

java集合中的list詳解

詳解java各種集合的線(xiàn)程安全

Java集合框架源碼分析之LinkedHashMap詳解

如有不足之處,歡迎留言指出。感謝朋友們對(duì)本站的支持!

相關(guān)文章

  • scala中的隱式類(lèi)型轉(zhuǎn)換的實(shí)現(xiàn)

    scala中的隱式類(lèi)型轉(zhuǎn)換的實(shí)現(xiàn)

    這篇文章主要介紹了scala中的隱式類(lèi)型轉(zhuǎn)換的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • Spring Boot如何使用Undertow代替Tomcat

    Spring Boot如何使用Undertow代替Tomcat

    這篇文章主要介紹了Spring Boot如何使用Undertow代替Tomcat,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • Java常用時(shí)間工具類(lèi)總結(jié)(珍藏版)

    Java常用時(shí)間工具類(lèi)總結(jié)(珍藏版)

    這篇文章主要為大家詳細(xì)介紹了Java中一些常用時(shí)間工具類(lèi)的使用示例代碼,文中的代碼簡(jiǎn)潔易懂,對(duì)我們學(xué)習(xí)Java有一定幫助,需要的可以參考一下
    2022-07-07
  • PowerJob的HashedWheelTimer工作流程源碼解讀

    PowerJob的HashedWheelTimer工作流程源碼解讀

    這篇文章主要為大家介紹了PowerJob的HashedWheelTimer工作流程源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01
  • Java實(shí)現(xiàn)高并發(fā)秒殺的七種方式

    Java實(shí)現(xiàn)高并發(fā)秒殺的七種方式

    本文主要介紹了Java實(shí)現(xiàn)高并發(fā)秒殺的六種方式,包括使用緩存、數(shù)據(jù)庫(kù)樂(lè)觀(guān)鎖、數(shù)據(jù)庫(kù)悲觀(guān)鎖、分布式鎖、隊(duì)列限流、令牌桶算法和限流器,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-03-03
  • springboot logback如何從apollo配置中心讀取變量

    springboot logback如何從apollo配置中心讀取變量

    這篇文章主要介紹了springboot logback如何從apollo配置中心讀取變量的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Springmvc模式上傳和下載與enctype對(duì)比

    Springmvc模式上傳和下載與enctype對(duì)比

    這篇文章主要介紹了Springmvc模式上傳和下載與enctype對(duì)比,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-12-12
  • SpringBoot配置文件bootstrap和application區(qū)別及說(shuō)明

    SpringBoot配置文件bootstrap和application區(qū)別及說(shuō)明

    這篇文章主要介紹了SpringBoot配置文件bootstrap和application區(qū)別及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • java使用Runtime執(zhí)行系統(tǒng)命令遇到的問(wèn)題

    java使用Runtime執(zhí)行系統(tǒng)命令遇到的問(wèn)題

    這篇文章主要介紹了java使用Runtime執(zhí)行系統(tǒng)命令遇到的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • MyBatis流式查詢(xún)的項(xiàng)目實(shí)踐

    MyBatis流式查詢(xún)的項(xiàng)目實(shí)踐

    本文主要介紹了MyBatis流式查詢(xún)的項(xiàng)目實(shí)踐,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08

最新評(píng)論