[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>