1. 程式人生 > >[Java]集合框架知識點總結(逐步更新)

[Java]集合框架知識點總結(逐步更新)

1.迭代器

迭代器是一個介面,是一種取出集合中元素的方式。每個集合的內部以內部類的方式實現了此介面,用於取出集合的元素。這些內部類都符合迭代器介面。集合通過一個iterator方法提供這個取出元素的方式。不同的集合因為底層資料結構不同,而實現取出元素的方式不同。但都符合此介面提供的共性方法用於操縱集合元素。

常見的迭代取出方式:

public void test() {
		// TODO Auto-generated method stub
		ArrayList al = new ArrayList();
		al.add("java01");
		al.add("java02");
		al.add("java03");
		al.add("java04");

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

結果:

java01
java02
java03
java04

為什麼定義為內部類:如果定義在外部,想操縱集合元素就要建立集合物件。所謂內部類就是一種事物內部的事物,並且這種事物可以直接訪問容器中的成員,而無需建立物件。集合的內部類還妙在其實現了一個對外的介面(iterator),只要用方法返回這個內部類物件,就可用介面的共性方法實現操縱元素的目的,而無需關心其內部如何實現。

JDK的AbstractList中關於iterator的內部實現方式:

public Iterator<E> iterator() {
        return new Itr();
    }
...
private class Itr implements Iterator<E> {

 public boolean hasNext() {
            return cursor != size();
        }
...
}

2.幾種不允許的和允許的泛型形式

@Test
	public void test3() {
		// 幾種不允許的和允許的形式

		// 如果兩邊都使用泛型,形式必須一樣
		// ArrayList<Object> list1 = new ArrayList<String>();//
		// 不行,前面的接收任何型別,後面的強制只接受String
		// ArrayList<String> list2 = new ArrayList<Object>();// 同樣不行

		ArrayList<String> list3 = new ArrayList();// 可以,相容性考慮,老程式使用新程式介面時

		ArrayList list4 = new ArrayList<String>();// 可以,相容性考慮,新程式使用老程式介面時
	}

	// 老程式呼叫新程式
	public void bb() {
		aa(new ArrayList());
	}

	public void aa(ArrayList<String> list) {

	}

	// 新程式呼叫老程式
	public void dd() {
		cc(new ArrayList<String>());
	}

	public void cc(ArrayList list1) {

	}

3.HashSet:

Set集合:元素無序,不可重複。無序指存入和取出的順序不一定一致

HashSet:底層資料結構是雜湊表--->元素按雜湊值存放,不按新增順序存放,hashCode方法設定雜湊值,增刪改查,先比較雜湊值,如果雜湊值相同,再用equals比較是否是同一個物件(元素不允許重複)

例1:相同的字串(常量)具有相同的雜湊值------->即使給String物件起名字,也不過是引用

<pre name="code" class="java">public class HashSetDemo {
	public static void main(String[] args) {
		HashSet hs = new HashSet();

		hs.add("java01");// 無序,不按插入順序
		hs.add("java03");
		hs.add("java01");

		hs.add("java02");//轉成String物件---->集合只能存物件(引用)
		hs.add("java01");
		hs.add(new String("java01"));// 同樣判斷重複,沒插進去
		hs.add(new String("java05"));

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

}

結果:

java05java02java03java01

例2:

package it.cast.practice2;

public class Person {

	private String name;
	private int age;

	Person(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public int hashCode() {

		return name.hashCode() + age * 5;// name是String型別,相同字串地址相同
	}

	// 覆寫equals---->引數別寫成Person,必須是Object才是複寫,都是Object中的方法!
	public boolean equals(Object obj) {// 如果不覆寫,會簡單比較地址,都是new物件,地址當然不同,無法去除重複元素
		// 加上這一句
		if (!(obj instanceof Person))
			return false;// 直接返回false---->不能拋異常,但在內部try可以,不可以拋比父類方法更多的異常
		Person p = (Person) obj;// 必須強轉,上面的引數和Object中一致


		return this.name.equals(p.getName()) && this.age == p.getAge();
	}

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

主程式:

public class HashSetPerson {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		HashSet hs = new HashSet();
		hs.add(new Person("lixiaoming", 35));// 要求姓名年齡相同視為同一個人
		hs.add(new Person("lixiaoming", 35));
		hs.add(new Person("lixiaoming", 35));
		hs.add(new Person("lixiaoming", 35));
		hs.add(new Person("lixiaoming", 36));
		hs.add(new Person("lixiaomin", 35));// hashCode不同直接不同
		hs.add(new Person("lixiao", 35));

		System.out.println(hs);
		System.out.println(hs.contains(new Person("lixiaomin", 35)));//先比較雜湊值,相同再用equals比較,可以將equals直接return true驗證
		hs.remove(new Person("lixiaomin", 35));//這個方法也是一樣
		System.out.println(hs.contains(new Person("lixiaomin", 35)));
		System.out.println(hs);// 複寫hashCode前,全部插入
		// hashCode直接return
		// 1,則比較hashCode相同後,用equals發現物件也相同,只插入一個!但姓名年齡不完全相同的也具有同樣hashCode值不合理,應該不一樣,直接不比equals了!
		// 同一個人:保證hashCode相同,並且equals返回true,那麼把hashCode方法和equals都寫成名字年齡組合形式,同樣的名字年齡返回同樣雜湊值,並且equals為true.

	}

}

結果:

[lixiaoming...36, lixiaoming...35, lixiao...35, lixiaomin...35]
true
false
[lixiaoming...36, lixiaoming...35, lixiao...35]

4.TreeSet:

public class TreeSetDemo {

	public static void main(String[] args) {
		TreeSet ts = new TreeSet();

		ts.add(new Person("lisi", 29));

		System.out.println(ts);
	}

}

只插入一個沒問題:

[lisi...29]

再插一個:

ts.add(new Person("zhangsan", 30));

結果:

Exception in thread "main" java.lang.ClassCastException: cn.itcast.treeset.Person cannot be cast to java.lang.Comparable
    at java.util.TreeMap.put(TreeMap.java:542)
    at java.util.TreeSet.add(TreeSet.java:238)
    at cn.itcast.treeset.TreeSetDemo.main(TreeSetDemo.java:12)

型別轉換異常,無法轉換成Comparable

原因:

TreeSet集合:會對元素進行排序,要求元素具有比較性,即實現Comparable介面,這種排序被稱作類的自然排序

那麼:修改Person類為實現Comparable介面,實現compareTo方法,按照年齡排序

package cn.itcast.treeset;

public class Person implements Comparable {//注意這裡一定要實現Comparable介面!

	private String name;
	private int age;

	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public int compareTo(Object obj) {// 其實這個有泛型
		// 先判斷
		if (!(obj instanceof Person)) {
			throw new RuntimeException("不是學生物件");// 不能宣告!
		}
		// 強轉
		Person p = (Person) obj;
		// 比較此物件與指定物件的順序。如果該物件小於、等於或大於指定物件,則分別返回負整數、零或正整數。
		return this.age - p.getAge();
	}

	/**
	 * @return the name
	 */
	String getName() {
		return name;
	}

	/**
	 * @param name
	 *            the name to set
	 */
	void setName(String name) {
		this.name = name;
	}

	/**
	 * @return the age
	 */
	int getAge() {
		return age;
	}

	/**
	 * @param age
	 *            the age to set
	 */
	void setAge(int age) {
		this.age = age;
	}

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

結果:

[lisi...29, zhangsan...30]

問題:同一年齡不同姓名的按照compareTo判斷相同後,不能插入,因為Set集合沒有重複元素。而要求按照姓名年齡排序,也有個主次要條件的問題:先比較年齡,年齡相同比較姓名---------->注意是在compareTo中,返回負數,0,正數,而String型別已實現了Comparable介面,直接呼叫其compareTo方法即可排序。改進的compareTo方法:(按照姓名年齡判斷和排序,返回0判定為相同元素,Set集合)

public int compareTo(Object obj) {// 其實這個有泛型
		// 先判斷
		if (!(obj instanceof Person)) {
			throw new RuntimeException("不是學生物件");// 不能宣告!
		}
		// 強轉
		Person p = (Person) obj;
		// 比較此物件與指定物件的順序。如果該物件小於、等於或大於指定物件,則分別返回負整數、零或正整數。
		// return this.age - p.getAge();
		if (this.age < p.getAge()) {
			return -1;
		}
		if (this.age == p.getAge()) {// 年齡相同的,比較姓名
			return this.name.compareTo(p.getName());
		}
		return 1;
	}

主程式:
public class TreeSetDemo {

	public static void main(String[] args) {
		TreeSet ts = new TreeSet();

		ts.add(new Person("lisi", 29));
		ts.add(new Person("lisi", 30));
		ts.add(new Person("liasi", 29));
		ts.add(new Person("liasi", 30));
		ts.add(new Person("zhangsan", 30));

		System.out.println(ts);
	}

}

結果:(年齡主要條件,姓名次要條件,姓名按照String類的自然排序)

[liasi...29, lisi...29, liasi...30, lisi...30, zhangsan...30]

問題:如果元素自身不存在比較性(無法修改),或者具備的比較性不是所需,該怎麼辦呢?此時就需要集合具有比較元素的功能,java提供了一個比較器用於對集合初始化,提供比較功能。

例子:

Person類保持不變,自定義比較器類:

package cn.itcast.treeset;

import java.util.Comparator;

public class MyComparator implements Comparator {// 實現Comparator介面

	// 實現其compare方法,接收兩個Object型別物件,注意這個在後面也有泛型
	public int compare(Object o1, Object o2) {
		System.out.println("Hi there,Comparator");
		if (!(o1 instanceof Person) || !(o2 instanceof Person)) {
			throw new RuntimeException("非人類");
		}
		// 強轉
		Person p1 = (Person) o1;
		Person p2 = (Person) o2;

		if (p1.getAge() > p2.getAge()) {
			return 1;
		}
		if (p1.getAge() == p2.getAge()) {
			return p1.getName().compareTo(p2.getName());
		}
		return -1;
	}
}

把比較器傳給TreeSet容器,新增元素演示:
public class TreeSetDemo {

	public static void main(String[] args) {
		TreeSet ts = new TreeSet(new MyComparator());

		ts.add(new Person("lisi", 29));
		ts.add(new Person("lisi", 30));
		// ts.add(new Person("lisi", 30));
		ts.add(new Person("lisi", 30));
		ts.add(new Person("liasi", 29));
		// ts.add(new Person("liasi", 30));
		// ts.add(new Person("zhangsan", 30));
		// ts.add(new Person("zhangsan", 29));

		System.out.println(ts);
	}

}

結果:

Hi there,Comparator
Hi there,Comparator
Hi there,Comparator
Hi there,Comparator
[liasi...29, lisi...29, lisi...30]

(注:TreeSet元素以二叉樹結構(有序)先和最小的比,找到自己位置後就不比了)

TreeSet集合比較元素,以Comparator為主(覆蓋元素自身的自然排序比較方式)

練習:

需求:TreeSet儲存字串,要求以字串長度排序

分析:字串(String物件)本身具備比較性,但不是所需要的(並且無法更改),所以要自定義比較器傳給TreeSet容器

定義前的自然排序:

public class StringLengthCompare {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		TreeSet ts = new TreeSet();

		ts.add("asljfslfjslfjsl");
		ts.add("fksdf");
		ts.add("Askfhskfskfhksdfvskfv");
		ts.add("b");

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

}

結果:

Askfhskfskfhksdfvskfv
asljfslfjslfjsl
b
fksdf

自定義比較器,按字串長度排序

package cn.itcast.treeset;

import java.util.Comparator;

public class StringComparator implements Comparator {

	public int compare(Object o1, Object o2) {
		if (!(o1 instanceof String) || !(o2 instanceof String)) {
			throw new RuntimeException("非字串類");
		}
		String s1 = (String) o1;
		String s2 = (String) o2;

		return s1.length() - s2.length();
	}
}

主程式:傳入比較器初始化TreeSet集合
public class StringLengthCompare {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		TreeSet ts = new TreeSet(new StringComparator());

		ts.add("asljfslfjslfjsl");
		ts.add("fksdf");
		ts.add("Askfhskfskfhksdfvskfv");
		ts.add("b");

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

}

結果:

b
fksdf
asljfslfjslfjsl
Askfhskfskfhksdfvskfv

改進:如果長度相同而內容不同,則無法插入,不合理,所以增加次要排序,修改比較器:

<pre name="code" class="java">public int compare(Object o1, Object o2) {
		if (!(o1 instanceof String) || !(o2 instanceof String)) {
			throw new RuntimeException("非字串類");
		}
		String s1 = (String) o1;
		String s2 = (String) o2;

		// return s1.length() - s2.length();
		/*
		 * int i1 = new Integer(s1.length()); int i2 = new Integer(s2.length());
		 * 
		 * if (i1 > i2) { return 1; } if (i1 == i2) { return s1.compareTo(s2);//
		 * 字串自然順序 } return -1;
		 */
		int num = new Integer(s1.length()).compareTo(new Integer(s2.length()));
		if (num == 0)
			return s1.compareTo(s2);
		return num;
	}
}

主程式:
public class StringLengthCompare {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		TreeSet ts = new TreeSet(new StringComparator());

		ts.add("asljfslfjslfjsl");
		ts.add("fksdf");
		ts.add("fksdf");
		ts.add("xksdf");
		ts.add("aksdf");
		ts.add("Bskfhskfskfhksdfvskfv");
		ts.add("Askfhskfskfhksdfvskfv");
		ts.add("b");

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

}

結果:

b
aksdf
fksdf
xksdf
asljfslfjslfjsl
Askfhskfskfhksdfvskfv
Bskfhskfskfhksdfvskfv

注:Comparator可以直接在TreeSet初始化處定義為匿名內部類。

5.Map集合

示例:

import java.util.*;
class  MapDemo
{
	public static void main(String[] args) 
	{
		//HashMap與HashTable底層都是雜湊表結構
		//容量 是雜湊表中桶 的數量,初始容量 就是雜湊表建立時的容量。注意,雜湊表的狀態為 open:在發生“雜湊衝突”的情況下,單個桶會儲存多個條目,這些條目必須按順序搜尋。
		Map<String,String> map=new HashMap<String,String>();

		System.out.println(map.put("01","zhangsan"));//返回的是其替換了的值
		System.out.println(map.put("01","zhangsan"));//key的hasCode相同
		System.out.println(map.put("01","wangwu"));//相同的鍵(hashCode比較),新的值會替換舊值!返回的是其替換了的值!
		map.put("02","zhangsan1");
		map.put("03","zhangsan2");
		map.put("04","zhangsan3");
		System.out.println("-----------------");

		System.out.println(map);
		System.out.println("containsKey:"+map.containsKey("01"));
		System.out.println("-----------------");

		Collection<String> coll=map.values();
		for(String s:coll)
		{
			System.out.println(s);
		}
		System.out.println("-----------------");

		//Map集合沒有迭代器,其取出原理是將其轉化成Set集合用迭代器
		Set<String> set=map.keySet();

		for(Iterator<String> its=set.iterator();its.hasNext();)
		{
			String key=its.next();
			String value=map.get(key);
			System.out.println(key+"="+value);
		}
		System.out.println("-----------------");

		//Map.Entry型別和迭代器實現原理相似,都是在集合類內部實現的,通過entrySet方法可以獲得這個物件
		//Entry也是一個介面,它是Map介面中的一個內部介面
		/*內部實現:
		interface Map
		{
			//關係是Map內部事物,並且直接訪問Map內部元素,所以定義成了內部規則(介面)
			public static interface Entry//靜態,並且對外暴露,只有內部接口才能加static關鍵字(類成員)
			{
				public abstract Object getKey();
				public abstract Object getValue();
			}
		}

		class HashMap implements Map
		{
			class Somename implements Map.Entry
			{
				public Object getKey(){}//實現
				public Object getValue(){}//實現
			}
		}
		*/
		Set<Map.Entry<String,String>> entryset=map.entrySet();
		for(Map.Entry<String,String> entry:entryset)
		{
			System.out.println(entry.getKey()+"="+entry.getValue());
		}
		System.out.println("-----------------");
	}
}

結果輸出:

D:\java\practice4>javac MapDemo.java

D:\java\practice4>java MapDemo
null
zhangsan
zhangsan
-----------------
{04=zhangsan3, 01=wangwu, 02=zhangsan1, 03=zhangsan2}
containsKey:true
-----------------
zhangsan3
wangwu
zhangsan1
zhangsan2
-----------------
04=zhangsan3
01=wangwu
02=zhangsan1
03=zhangsan2
-----------------
04=zhangsan3
01=wangwu
02=zhangsan1
03=zhangsan2
-----------------

D:\java\practice4>