一、集合框架的概述
1、概述:
1、簡述:
所謂集合,就是為方便對多個對象的操作,對對象進(jìn)行存儲。集合就是存儲對象最常用的一種方式。
2、集合與數(shù)組的區(qū)別:
數(shù)組:可存儲同種類型的數(shù)據(jù),但長度固定,也可存儲基本類型的數(shù)據(jù)
集合:只可存儲對象,長度可變,類型可以不同。
3、集合的特點:
只用于存儲對象,長度可變,也可存不同類型的對象。
集合是一個接口,將每種容器的共性提取,形成的一個體系。
4、數(shù)據(jù)結(jié)構(gòu):
由于每種容器對數(shù)據(jù)的存儲方式都各不相同,所以出現(xiàn)了不同的容器。此種存儲方式稱之為數(shù)據(jù)結(jié)構(gòu)。
2、集合體系如圖:
3、集合中的共性方法:
下面從增刪改查等方面對這些共性發(fā)方法進(jìn)行簡單介紹:
說明:
a.add方法中的參數(shù)類型是Object,以便于接收任意類型的對象
b.集合中存儲的都是對象的引用(即地址)。
1、增加:
add(Object obj);
-----> 添加元素
2、刪除:
remove();
-----> 刪除集合中指定參數(shù)的元素
removeAll();
-----> 刪除當(dāng)前集合中與另一集合相同的元素,即只保留與另一集合不同的元素
clear();
-----> 清空集合中的元素,集合還存在
3、獲取集合長度:
size();
-----> 獲取集合長度,即集合元素的個數(shù)
4、修改:
set(int index,e);
-----> 將指定位置(index)上的元素修改為給定的參數(shù)e
5、判斷:
boolean contains(e);
-----> 判斷給定元素e是否存在于集合中
6、迭代器:
iterator();
-----> 集合取出元素的方式
boolean hasNext();
-----> 判斷是否還有下一個元素
next();
-----> 取出下一個元素
示例:
import java.util.*;
class CollectionDemo
{
public static void sop(Object obj)
{
System.out.println(obj);
}
public static void main(String[] args)
{
base_method();
sop("------------------");
method2();
sop("------------------");
method_get();
}
public static void base_method()
{
//創(chuàng)建一個集合容器,使用Collection接口的子類,ArrayList
ArrayList al = new ArrayList();
//1.添加元素--->add(Object obj),多態(tài)
al.add("java01");
al.add("java02");
al.add("java03");
al.add("java04");
//打印原集合
sop("原集合" + al);
//3.刪除元素
al.remove("java02");
//清空集合中的元素
al.clear();
//4.判斷元素
//判斷集合中是否存在某個元素:contains()
sop("是否存在java03:" + al.contains("java03"));
//判斷集合是否為空:isEmpty()
sop("判斷集合是否為空:" + al.isEmpty());
//2.獲取個數(shù),集合長度
sop("size :" + al.size());
//打印改變后集合
sop(al);
System.out.println("Hello World!");
}
public static void method2()
{
//創(chuàng)建另一個新集合
ArrayList at1 = new ArrayList();
at1.add("java01");
at1.add("java02");
at1.add("java03");
at1.add("java04");
//打印原集合
sop("原集合at1:" + at1);
ArrayList at2 = new ArrayList();
at2.add("java01");
at2.add("java02");
at2.add("java05");
at2.add("java06");
sop("原集合at2:" + at2);
//removeAll()--->只保留和at2不相同的元素。
at1.removeAll(at2);
sop("remove后at1:" + at1);
sop("now集合at2:" + at2);
//取兩個集合的交集,at1中只保留交集的部分
at1.retainAll(at2);
sop("取完交集后的at1:" + at1);
}
public static void method_get()
{
//創(chuàng)建另一個新集合
ArrayList at1 = new ArrayList();
at1.add("java01");
at1.add("java02");
at1.add("java03");
at1.add("java04");
//打印原集合
sop("原集合at1:" + at1);
//獲取迭代器,用于去除集合中的元素
Iterator it = at1.iterator();
while (it.hasNext())
{
sop(it.next());
}
}
}
4、Collection接口包含的子類
Collection接口包含List與Set等子類
List:元素是有序的,元素可重復(fù),因該集合體系有索引
Set:元素是無序的,元素不可重復(fù)
下面具體總結(jié)一下關(guān)于兩種集合:
二、List集合
1、概述:
List集合包含三個子類:ArrayList、LinkedList以及Vector等。
具體區(qū)別如下:
1、ArrayList:底層的數(shù)據(jù)結(jié)構(gòu)是數(shù)組結(jié)構(gòu)
特點:查詢速度很快,因為有索引(角標(biāo)),但增刪速度稍慢。是線程不同步的。
2、LinkedList:底層使用的是鏈表數(shù)據(jù)結(jié)構(gòu)
特點:增刪速度很快,但查詢速度稍慢,因為每一個元素都鏈接到前一元素。
3、Vector:底層是數(shù)組結(jié)構(gòu),JDK1.0出現(xiàn),比較老。
特點:增刪查詢都很慢,被ArrayList替代了,線程是同步的。
2、對于List集合特有的方法:
凡是可操作角標(biāo)的方法都是該體系特有的方法,基本方法和Collection中的一樣。
1、增加:
add(int index,e);
-----> 在指定位置增加給定的元素
addAll(int index,Collection);
-----> 在指定位置增加給定集合中的所有元素,若省略位置參數(shù),則在當(dāng)前集合的后面依次添加元素
2、刪除:
remove(int index);
-----> 刪除集合中指定位置上的元素
3、修改:
set(int index,e);
-----> 將指定位置(index)上的元素修改為給定的參數(shù)e
4、查詢:
get(int index);
-----> 獲取指定位置上的元素
indexOf(e);
-----> 通過指定元素獲取其在集合中的位置
subList(int from,int to);
-----> 獲取從from到to位置上的元素
Iterator listIterator();
-----> 返回Iterator接口類型值
注:
1、listIterator是List特有的迭代器,是Iterator子接口。在迭代時,不可通過集合對象的方法操作集合中的元素,因為會發(fā)生ConcurrentModficationException異常。
所以,在迭代時,只能用迭代器的方法操作,可Iterator方法是有限的,若想要其他操作如增刪改寫等,就需要使用子接口,即ListIterator,該接口只能通過List集合的listIerator方法獲取。
2、在迭代時,循環(huán)中的next()調(diào)用一次,就要對hasNext判斷一次,不可判斷一次調(diào)用兩次。
3、List集合判斷元素是否相同,一句的是元素的equals方法,其中,contains中就是調(diào)用的equals方法。
示例:
import java.util.*;
class ListDemo
{
public static void sop(Object obj)
{
System.out.println(obj);
}
public static void main(String[] args)
{
methodAdd();
//methodListIterator();
}
public static void methodAdd()
{
//創(chuàng)建一個集合容器,使用Collection接口的子類,ArrayList
ArrayList list = new ArrayList();
//1.添加元素--->add(Object obj),多態(tài)
list.add("java01");
list.add("java02");
list.add("java03");
list.add("java04");
ArrayList list2 = new ArrayList();
//1.添加元素--->add(Object obj),多態(tài)
list2.add("java05");
list2.add("java06");
list2.add("java07");
list2.add("java08");
//打印原集合
sop("原集合:" + list);
sop("------------------");
//1.在某一個位置上添加元素:add(int index,"新元素")
list.add(1,"java001");
sop("增加后的集合:" + list);
sop("---------");
list.addAll(1,list2);
sop("在list中1位置后添加list2:" + list);
sop("------------------");
//2.刪除指定位置上的元素:
list.remove(2);
sop("刪除后的集合:" + list);
sop("------------------");
//3.改變某一位置上的元素:set(int index,"要改成的元素")
list.set(2,"java007");
sop("改變角標(biāo)為2的元素后的元素:" + list);
sop("------------------");
//4.獲取元素:get()
list.get(1);
sop("獲取角標(biāo)為1上的元素:" + list.get(1));
sop("------------------");
//通過某個元素獲取其在集合中的位置--indexOf("查找的元素")
int m = list.indexOf("java007");
sop("獲取“java007”所在的位置:" + m);
//獲取從某個位置到另一位置上的元素subList()
List l = list.subList(1,3);
sop("獲取從位置1到3上的元素:" + l);
sop("------------------");
//4.獲取全部元素
//get方法的for循環(huán)
sop("get方法:");
for (int i=0;i<list.size();i++)
{
sop("list(" + i + ")" + list.get(i));
}
sop("------------------");
//迭代器方法:Iterator()
for (Iterator it = list.iterator();it.hasNext(); )
{
sop("next:" + it.next());
}
sop("------------------");
}
public static void methodListIterator()
{
//演示列表迭代器:
ArrayList list = new ArrayList();
//1.添加元素--->add(Object obj),多態(tài)
list.add("java01");
list.add("java02");
list.add("java03");
list.add("java04");
//打印原集合
sop("原集合:" + list);
sop("------------------");
//在迭代過程中,準(zhǔn)備添加或刪除元素
for (ListIterator it = list.listIterator();it.hasNext(); )
{
Object obj = it.next();
if (obj.equals("java01"))
it.remove();
else if(obj.equals("java02"))
it.add("增加java200");
else if(obj.equals("java03"))
it.set("修改為java300");
sop("obj:" + obj);
}
sop("list :" + list);
}
}
3、Vector類:
Vector中有種特殊的取出方式,即為枚舉
1、枚舉和迭代器十分相似,其實兩者是一樣的,由于枚舉的名稱以及方法名都過長,因此,就被迭代器取代了。這里就不過多的介紹了。
4、LinkedList類特有方法:
一)JDK1.6之前的方法
1、增加:
addFirst(obj);
-----> 在集合頭部添加給定的元素
addLast(obj);
-----> 在集合尾部添加給定的元素
2、獲取:
getFirst();
-----> 獲取集合第一個元素,若集合中沒有元素,則出現(xiàn)NoSuchElementException
getLast();
-----> 獲取集合最后一個元素,若集合中沒有元素,則出現(xiàn)NoSuchElementException
3、刪除:
removeFirst();
-----> 獲取并刪除集合第一個元素,若集合中沒有元素,則出現(xiàn)NoSuchElementException
removeLast();
-----> 獲取并刪除集合最后一個元素,若集合中沒有元素,則出現(xiàn)NoSuchElementException
二)JDK1.6出現(xiàn)的替代方法:
1、增加:
offerFirst(obj);
-----> 在集合頭部添加給定的元素
offerLast(obj);``` -----> 在集合尾部添加給定的元素
2、獲取:
peekFirst();
-----> 獲取集合第一個元素,若集合中沒有元素,則返回null
peekLast();
-----> 獲取集合最后一個元素,若集合中沒有元素,則返回null
3、刪除:
pollFirst();
-----> 獲取并刪除集合第一個元素 ,若集合中沒有元素,則返回null
pollLast();
-----> 獲取并刪除集合最后一個元素,若集合中沒有元素,則返回null
三、Set 集合
1、概述:
1、Set集合的特點:
1)集合中的元素師無需的(存入和取出的順序不一定一致),且元素不可重復(fù)。
2)Set集合的功能和Collection集合的是一樣的,并沒有什么特別的方法。
這里主要說一下關(guān)于HashSet和TreeSet兩種集合的方法和特點:
2、HashSet類
1、特點:
底層數(shù)據(jù)結(jié)構(gòu)時哈希表,且元素取出方式只有迭代器方法
2、哈希表簡介:
1)哈希表是按照哈希值的大小進(jìn)行排列的,如果兩個哈希值不同,則大的值放后面;如果兩個哈希值相同,則再用equals方法比較兩個元素的對象是否相同,如果不同,則將第二個值順延,兩個值串起來,放在同一個位置上。
2)取值時是按照哈希值取出來的。
3)哈希值的取值方式:哈希值存入哈希表中,哈希表也稱散列表。散列表是存放記錄的數(shù)組。具體來說,散列表是根據(jù)散列函數(shù)H(Key)和處理沖突的方法將一組關(guān)鍵字映象到一個有限的連續(xù)的地址集(區(qū)間)上,并以關(guān)鍵字在地址集中的“象”,作為這條記錄在表中的存儲位置,這種表表稱為散列表,這一映象過程就稱為散列造表或散列,所存儲位置稱為散列地址(這是百度百科上的內(nèi)容)。我的理解是:存入的對象的地址是通過提取其信息摘要,通過散列函數(shù)計算而獲得的一個關(guān)鍵碼(Key)即哈希值,然后將這個值存入哈希表,而哈希表的存值方式是按照哈希值的大小順序存儲的,并且在這個哈希表中有自己的索引,但是哈希表的取值方式并不是按照索引的方式取出的。取出方式是按照哈希表中特有的算法取出的哈希值(注意,現(xiàn)在說的是哈希值,不是元素的取出),這些算法有直接尋址法、折疊法、平方取中法以及其他的一些方法等等。具體是按哪種算法查找的,我并不太清楚,所以,取出的哈希值可能就不是按照哈希值的大小順序取出的了。
3、HashSet如何保證元素的唯一性:
通過元素的兩個方法hasCode和equals方法來完成的。
如果元素的hashCode的值相同,才會判斷equals是否為true。如果元素的hashCode值不相同,就不會調(diào)用equals方法了。
注:
對于判斷元素是否存在和刪除(或添加)。依賴的方法是元素的hashcode和equals方法。
示例:
/*
思路:
1、對人描述,將數(shù)據(jù)封裝進(jìn)對象
2、定義容器,將人存入
3、取出
4、移除
*/
import java.util.*;
class TypeException extends Exception
{
TypeException(String message)
{
super(message);
}
}
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 int getAge()
{
return age;
}
public int hashCode()
{
return this.name.hashCode()+age*39;
}
public boolean equals(Object obj)
{
try
{
if (!(obj instanceof Person))
throw new TypeException("NoSuchTypeException");
}
catch (TypeException e)
{
System.out.println(e.toString());
}
Person p = (Person)obj;
return this.name.equals(p.name) && this.age == p.age;
}
}
class HashDemo
{
public static void sop(Object obj)
{
System.out.println(obj);
}
public static void printE(HashSet hs)
{
Iterator it = hs.iterator();
while (it.hasNext())
{
Person p = (Person)it.next();
sop(p.getName() + "--" + p.getAge());
}
}
public static void main(String[] args)
{
HashSet hs = new HashSet();
hs.add(new Person("a1",11));
hs.add(new Person("a2",12));
hs.add(new Person("a3",13));
hs.add(new Person("a2",12));
hs.add(new Person("a3",13));
sop("原集合:");
printE(hs);
sop("移除后的集合:");
hs.remove(new Person("a3",13));
printE(hs);
}
}
3、TreeSet類:
1、特點:
1)底層的數(shù)據(jù)結(jié)構(gòu)為二叉樹結(jié)構(gòu)(紅黑樹結(jié)構(gòu))
2)可對Set集合中的元素進(jìn)行排序,是因為:TreeSet類實現(xiàn)了Comparable接口,該接口強(qiáng)制讓增加到集合中的對象進(jìn)行了比較,需要復(fù)寫compareTo方法,才能讓對象按指定需求(如人的年齡大小比較等)進(jìn)行排序,并加入集合。
java中的很多類都具備比較性,其實就是實現(xiàn)了Comparable接口。
注意:排序時,當(dāng)主要條件相同時,按次要條件排序。
3)二叉樹示意圖:
我通過對二叉樹分布的測驗,畫了一個圖,不知對否,僅供參考
對于存入時每個元素的比較,并不是連續(xù)排下來的,而是隨著元素的個數(shù)而改變的,如圖中所示:
第①步:22比較完了,如圖①,之后會重新分布
第②部:以22為頂部,開始比較,如圖②
第③步:比完第一個33后的圖示,當(dāng)比第二個33重復(fù)元素的時候,又重新分布了
第④步:分布如圖,第二個33是按這個如比較的。
2、保證元素唯一性的依據(jù):
實現(xiàn)的compareTo方法的返回值,是正整數(shù)、負(fù)整數(shù)或零,則兩個對象較大、較小或相同。相等時則不會存入。
3、兩種比較方式:
排序有兩個要素:元素和集合
1)第一種排序方式:自然排序
讓元素自身具備比較性。元素需要實現(xiàn)Comparable接口,覆蓋compareTo方法,這種方式也稱為元素的自然排序或默認(rèn)排序方式。
示例:
/*
第一種排序方式:自然排序,實現(xiàn)Comparable接口,重寫compareTo方法
需求:
向TreeSet集合中存儲自定義對象學(xué)生
按照學(xué)生的年齡進(jìn)行排序
*/
import java.util.*;
//此接口強(qiáng)制讓Student實現(xiàn)比較性
class Student implements Comparable
{
//定義Student私有屬性
private String name;
private int age;
//構(gòu)造Student函數(shù),初始化
Student(String name,int age)
{
this.name = name;
this.age = age;
}
//公共訪問方法,訪問私有屬性
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
//復(fù)寫Comparator中的compare方法,自定義比較器
public int compareTo(Object obj)
{
//判斷是否屬于Student類型,否則拋異常
if (!(obj instanceof Student))
throw new RuntimeException("NotSuchTypeException");
//將Object類對象強(qiáng)轉(zhuǎn)為Student類
Student s = (Student)obj;
//System.out.println(this.age + "--compare-" + s.age);//測試用,查看比較情況
//按年齡大小比較,相同則比較姓名大小,不同返回兩年齡之差
if (this.age == s.age)
{
return this.name.compareTo(s.name);
}
else if (this.age <s.age)
return this.age-s.age;
return this.age-s.age;
}
/*
//如果按照存入順序輸出
public int compareTo()
{
return 1;//改為-1則按倒敘輸出
}
*/
}
//測試
class TreeSetTest
{
public static void main(String[] args)
{
//創(chuàng)建集合,并添加元素
TreeSet ts = new TreeSet();
ts.add(new Student("li01",25));
ts.add(new Student("li02",20));
ts.add(new Student("li01",22));
ts.add(new Student("li05",24));
ts.add(new Student("li08",40));
//打印集合中元素
printE(ts);
System.out.println("Hello World!");
}
//定義打印集合中元素的功能
public static void printE(TreeSet ts)
{
//迭代器方法獲取
Iterator it = ts.iterator();
while (it.hasNext())
{
//將返回的元素(Object類)強(qiáng)轉(zhuǎn)為Student類
Student s = (Student)it.next();
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
2)第二種排序方式:比較器
當(dāng)元素自身不具備比較性是,或者具備比較性,卻不是所需要的,這時就需要讓集合自身具備比較性。在集合初始化時就有了比較方式(即參閱構(gòu)造函數(shù))。
當(dāng)兩種排序方式都存在時,以比較器為主。
如何構(gòu)造比較器:定義一個類,實現(xiàn)Comparator接口,覆蓋compare方法。
注:字符串本身具備比較性,但是它的比較方式可能不是所需要的,這時,就只能使用比較器了。
示例:
import java.util.*;
//此接口強(qiáng)制讓Student實現(xiàn)比較性
class Student implements Comparable
{
//定義Student私有屬性
private String name;
private int age;
//構(gòu)造Student函數(shù),初始化
Student(String name,int age)
{
this.name = name;
this.age = age;
}
//公共訪問方法,訪問私有屬性
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
//復(fù)寫Comparator中的compare方法,自定義比較器
public int compareTo(Object obj)
{
//判斷是否屬于Student類型,否則拋異常
if (!(obj instanceof Student))
throw new RuntimeException("NotSuchTypeException");
//按年齡大小比較,相同則比較姓名大小,不同返回兩年齡之差
Student s = (Student)obj;
if (this.age > s.age)
return this.age-s.age;
else if (this.age == s.age)
{
return this.name.compareTo(s.name);
}
return this.age-s.age;
}
}
//定義比較器,實現(xiàn)Comparator接口
class MyCompare implements Comparator
{
//重寫Comparator中的compare方法,按姓名順序排序
public int compare(Object o1,Object o2)
{
//判斷給定對象是否為Student類,否則拋異常
if (!((o1 instanceof Student) && (o2 instanceof Student)))
throw new RuntimeException("NotSuchTypeException");
//將給定對象強(qiáng)轉(zhuǎn)為Student類
Student s1 = (Student)o1;
Student s2 = (Student)o2;
//比較名字,返回數(shù)值,相同則比較年齡
int n = s1.getName().compareTo(s2.getName());
if (n == 0)
return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));
return n;
}
}
//測試
class TreeSetComDemo
{
public static void main(String[] args)
{
//TreeSet ts = new TreeSet();
//創(chuàng)建集合,加入接口類參數(shù),并添加元素
TreeSet ts = new TreeSet(new MyCompare());
ts.add(new Student("li01",25));
ts.add(new Student("li02",20));
ts.add(new Student("li01",22));
ts.add(new Student("li05",24));
ts.add(new Student("li08",40));
//打印集合中元素
printE(ts);
}
//定義打印集合中元素的功能
public static void printE(TreeSet ts)
{
//迭代器方法獲取
Iterator it = ts.iterator();
while (it.hasNext())
{
//將返回的元素(Object類)強(qiáng)轉(zhuǎn)為Student類
Student s = (Student)it.next();
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
四、Map 集合
1、概述:
1、簡述:
Map<K,V>集合是一個接口,和List集合及Set集合不同的是,它是雙列集合,并且可以給對象加上名字,即鍵(Key)
2、特點:
1)該集合存儲鍵值對,一對一對往里存
2)要保證鍵的唯一性。
2、方法簡介:
Map集合中的方法和上面集合的方法是很相似的,這里不一一都具體說明了,用示例的形式體現(xiàn)一下:
1、添加: 添加單個元素:put(k key,V value); 添加一個集合:putAll(Map<? extends K,? extends V> m)
2、刪除: 獲取并移除:remove(Object key); 清空集合中元素:clear (Object key)
3、判斷: 判斷集合是否為空: isEmpty() ; 鍵對應(yīng)的值是否存在:containsKey(Object key); 值對應(yīng)的鍵是否存在:containsValue(Object obj) ----> 返回boolean類型
4、獲取: 獲取單個元素:get(Object key); 獲取長度:size(); 獲取Map集合中的所有值(value),返回Conllection集合。
注:
a.也可以通過get()方法的返回值來判斷一個鍵是否存在,通過返回null來判斷。
b.其中put方法:如果出現(xiàn)添加相同的鍵,那么后添加的值會覆蓋原有鍵對應(yīng)的值,并且該方法返回被覆蓋的值即原值。
示例:
/*
Map集合方法
*/
import java.util.*;
class MapDemo
{
public static void sop(Object obj)
{
System.out.println(obj);
}
public static void main(String[] args)
{
//創(chuàng)建Map集合,并添加元素
Map<Integer,String> map = new HashMap<Integer,String>();
map.put(1,"zhangsan");
map.put(2,"lisi");
map.put(3,"wangwu");
map.put(4,"heihei");
map.put(5,"xixi");
//獲取長度
int n = map.size();
//打印元素
sop("原集合:" + map + "\n長度為:" + n);
sop("---------------------------------------");
//創(chuàng)建新集合,并添加元素
Map<Integer,String> m = new HashMap<Integer,String>();
m.put(7,"zann");
m.put(9,"hewi");
m.put(13,"wangfei");
m.put(14,"huxi");
m.put(10,"anch");
//用putAll將元素添加到原集合中
map.putAll(m);
sop("putAll --新集合:" + map);
sop("----------------------------------");
//刪除元素
sop("remove:" + map.remove(01));
sop("新集合:" + map);
sop("----------------------------------");
/*
//清空集合中的元素
map.clear();
sop("新集合:" + map);
//判斷是否為空集合
boolean be = map.isEmpty();
sop("isEmpty ,null? :" + be);
sop("----------------------------------");
*/
//判斷元素是否存在
boolean bk = map.containsKey(01);
boolean bv = map.containsValue("wangwu");
sop("判斷:\n01?:" + bk + "----- wangwu?:" + bv);
sop("----------------------------------");
//獲取元素:get
String s = map.get(01);
String s1 = map.get(02);
//Integer i = map.get("lisi");
//Integer i2 = map.get("zhangsan");
sop(map.get("lisi"));
sop(map.get("zhangsan"));
sop("獲取元素get:01:" + s + ",02:" + s1);
sop("-----------------------------------------------");
//獲取集合中的所有元素:Value
Collection<String> coll = map.values();
sop("value--獲取集合中所有元素:" + coll);
sop("----------------------------------");
}
}
3、Map集合中的子類:
1、HashTable:特點 -- > 底層是哈希表數(shù)據(jù)結(jié)構(gòu),不可存入null鍵和null值。該集合是線程同步的,效率較低
2、HashMap:特點 -- > 底層是哈希表數(shù)據(jù)結(jié)構(gòu),允許使用null值和null鍵。該集合是線程同步的,效率較高
3、TreeMap:特點 -- > 底層是二叉樹數(shù)據(jù)結(jié)構(gòu),線程不同步,可以用于給Map集合中的鍵值進(jìn)行排序,和Set很像,其實,Set集合的底層就是使用了Map集合。
4、兩種獲取集合元素的方法:
重點說一下獲取方法中的兩個:keySet()和entrySet()方法
1、keySet()方法獲取元素
原理:將Map集合中的所有鍵存入到Set集合中,因為Set集合具備迭代器,所以可以用迭代方式取出所有的鍵,再根據(jù)get方法獲取每一個鍵對應(yīng)的值。
簡單說就是:Map集合---->Set集合 ---->迭代器取出
示例:
//keySet集合測試
import java.util.*;
class KeySetDemo
{
public static void main(String[] args)
{
//創(chuàng)建Map集合,并添加元素
Map<Integer,String> map = new HashMap<Integer,String>();
map.put(2,"zhangsan");
map.put(6,"lisi");
map.put(3,"wangwu");
map.put(4,"heihei");
map.put(5,"xixi");
//獲取map集合中的所有鍵的Set集合
Set<Integer> keySet = map.keySet();
//有了Set集合就可以獲取其迭代器,取值
Iterator<Integer> it = keySet.iterator();
while (it.hasNext())
{
Integer i = it.next();
String s = map.get(i);
System.out.println(i + " = " + s);
}
}
}
2、entrySet()方法獲取元素:
原理:
將Map集合中的映射關(guān)系存入到了Set集合中,而這個映射關(guān)系的數(shù)據(jù)類型是Map.Entry,在通過迭代器將映射關(guān)系存入到Map.Entry集合中,并通過其中的getKey()和getValue()放取出鍵值。
示例:
/*
entrySet取出方式:
*/
import java.util.*;
class EntrySetDemo
{
public static void main(String[] args)
{
//創(chuàng)建集合,存入元素
Map<String,String> map = new HashMap<String,String>();
map.put("01","lisi1");
map.put("02","lisi2");
map.put("03","lisi3");
map.put("04","lisi4");
//獲取map集合中的所有鍵,存入到Set集合中,
Set<Map.Entry<String,String>> entry = map.entrySet();
//通過迭代器取出map中的鍵值關(guān)系,迭代器接收的泛型參數(shù)應(yīng)和Set接收的一致
Iterator<Map.Entry<String,String>> it = entry.iterator();
while (it.hasNext())
{
//將鍵值關(guān)系取出存入Map.Entry這個映射關(guān)系集合接口中
Map.Entry<String,String> me = it.next();
//使用Map.Entry中的方法獲取鍵和值
String key = me.getKey();
String value = me.getValue();
System.out.println(key + " : " + value);
}
}
}
補充:關(guān)于Map.Entry
Map是一個接口,其實,Entry也是一個接口,它是Map的子接口中的一個內(nèi)部接口,就相當(dāng)于是類中有內(nèi)部類一樣。為何要定義在其內(nèi)部呢?
原因:
a.Map集合中村的是映射關(guān)系這樣的兩個數(shù)據(jù),是先有Map這個集合,才可有映射關(guān)系的存在,而且此類關(guān)系是集合的內(nèi)部事務(wù)。
b.并且這個映射關(guān)系可以直接訪問Map集合中的內(nèi)部成員,所以定義在內(nèi)部。
5、Map集合的應(yīng)用及擴(kuò)展:
1、何時使用Map集合:當(dāng)量數(shù)據(jù)之間存在著映射關(guān)系的時候,就應(yīng)該想到使用Map集合。
2、示例:
獲取該字符串中的字母出現(xiàn)的次數(shù),如:"sjokafjoilnvoaxllvkasjdfns";希望打印的結(jié)果是:a(3)c(0).....
通過結(jié)果發(fā)現(xiàn),每個字母都有對應(yīng)的次數(shù),說明字母和次數(shù)之間有映射關(guān)系。
測試如下:
import java.util.*;
class MapTest
{
public static void main(String[] args)
{
String s = "abcsjokafjoilnvoaxllvkasjdfnsde 0[fga8/-abbdc";
String str = LetterNum(s);
System.out.println(str);
}
public static String LetterNum(String str)
{
//將字符串轉(zhuǎn)換成字符數(shù)組,因為對每個字母進(jìn)行操作
char[] ch = str.toCharArray();
//定義一個Map集合,因為打印結(jié)果的字母有順序,所以使用TreeMap集合
TreeMap<Character,Integer> tm = new TreeMap<Character,Integer>();
int count = 0;
//遍歷字符數(shù)組,將每一個字母作為鍵去查map集合,
for (int i=0;i<ch.length;i++)
{
//判斷集合是否含有其他非字母,是則再次循環(huán)
if (!(ch[i]>= 'a' && ch[i] <= 'z' || ch[i] >= 'A' && ch[i] <= 'Z'))
continue;
//取出鍵對應(yīng)的值,不為則計數(shù)器加1,存入集合,并將計數(shù)器清零,用于下一個字母
Integer value = tm.get(ch[i]);
if (value != null)
count = value;
count++;
tm.put(ch[i],count);
count = 0;
}
//創(chuàng)建字符串容器,存入取出的鍵值即按順序排列的字符串
StringBuilder sb = new StringBuilder();
//迭代器取出相應(yīng)鍵值,并存入字符串容器
Iterator<Map.Entry<Character,Integer>> it = tm.entrySet().iterator();
while (it.hasNext())
{
Map.Entry<Character,Integer> me = it.next();
Character key = me.getKey();
Integer value = me.getValue();
sb.append(key + "(" + value + ")");
}
return sb.toString();
}
}
6、集合映射的擴(kuò)展:
在很多項目中,應(yīng)用比較多的是一對多的映射關(guān)系,這就可以通過嵌套的形式將多個映射定義到一個大的集合中,并將大的集合分級處理,形成一個體系。
示例如下:
/*
Map擴(kuò)展:一對多的映射關(guān)系
如:學(xué)校中的 班級:學(xué)生:學(xué)號三者關(guān)系
思路,
先創(chuàng)建大的集合,學(xué)校,如school
其中包含班級這個小集合,如room
班級中包含的學(xué)生有姓名,如name
學(xué)生有學(xué)號,如id
*/
import java.util.*;
//創(chuàng)建學(xué)生類,定義私有屬性
class Student
{
private String name;
private String id;
Student(String name,String id)
{
this.name = name;
this.id = id;
}
//復(fù)寫toString方法,給出指定字符串形式
public String toString()
{
return name + ":" + id;
}
}
//測試
class MapsDemo
{
public static void main(String[] args)
{
//創(chuàng)建一個學(xué)校,里面有班級
HashMap<String,HashMap<String,String>> czbk = new HashMap<String,HashMap<String,String>>();
//創(chuàng)建兩個班級,里面有學(xué)生
//基礎(chǔ)班
HashMap<String,String> base = new HashMap<String,String>();
//預(yù)熱班
HashMap<String,String> adv = new HashMap<String,String>();
//將信息分別加入集合
//學(xué)校中的班級信息
czbk.put("Base",base);
czbk.put("Adv",adv);
//基礎(chǔ)班中的學(xué)生信息
base.put("01","BS01");
base.put("02","BS02");
base.put("03","BS03");
//提高班中的學(xué)生信息
adv.put("01","ADV01");
adv.put("02","ADV02");
adv.put("03","ADV03");
//調(diào)用取出信息的方法
String all = getAll(czbk);
System.out.println("ALL: \n" + all);
}
//定義取出方法
//定義取出學(xué)校中班級的方法,并調(diào)用取出學(xué)生信息方法
public static String getAll(HashMap<String,HashMap<String,String>> school)
{
//定義存儲班級信息字符串容器
StringBuilder sb = new StringBuilder();
//初始化班級為空
String room = null;
//將學(xué)校中的班級存入Set集合,并用迭代器獲取
Set<String> keySet = school.keySet();
Iterator<String> it = keySet.iterator();
while(it.hasNext())
{
room = it.next();
//取出班級中的學(xué)生信息也放在容器中
HashMap<String,String> hm = school.get(room);
//將班級中的學(xué)生信息字符串形式存入字符串容器中
sb.append(room + ":\n"+ getRoom(hm) );
}
//返回一個容器字符串,為學(xué)生信息
return sb.toString();
}
//定義取出班級中學(xué)生信息的方法,被上面的方法調(diào)用
public static String getRoom(HashMap<String,String> room)
{
//定義存儲學(xué)生信息的字符串容器
StringBuilder sb = new StringBuilder();
//將班級中的學(xué)生信息存入Set集合,并用迭代器獲取
Set<Map.Entry<String,String>> entrySet = room.entrySet();
Iterator<Map.Entry<String,String>> it = entrySet.iterator();
while(it.hasNext())
{
//將映射關(guān)系存入Map.Entry的集合中,并獲取相應(yīng)鍵值
Map.Entry<String,String> me = it.next();
String key = me.getKey();
String value = me.getValue();
//將學(xué)生信息存入到字符串容器中
sb.append(key + "::" + value + "\n");
}
//放回存有學(xué)生信息的字符串容器
return sb.toString();
}
}
集合間區(qū)別
一、List接口
List接口對Collection進(jìn)行了簡單的擴(kuò)充,它的具體實現(xiàn)類常用的有ArrayList和LinkedList。
你可以將任何東西放到一個List容器中,并在需要時從中取出。
ArrayList從其命名中可以看出它是一種類似數(shù)組的形式進(jìn)行存儲,因此它的隨機(jī)訪問速度極快,而LinkedList的內(nèi)部實現(xiàn)是鏈表,它適合于在鏈表中間需要頻繁進(jìn)行插入和刪除操作。在具體應(yīng)用時可以根據(jù)需要自由選擇。
前面說的Iterator只能對容器進(jìn)行向前遍歷,而ListIterator則繼承了Iterator的思想,并提供了對List進(jìn)行雙向遍歷的方法。
二、Set接口
Set接口也是Collection的一種擴(kuò)展。
而與List不同的是,在Set中的對象元素不能重復(fù),也就是說你不能把同樣的東西兩次放入同一個Set容器中。
它的常用具體實現(xiàn)有HashSet和TreeSet類。
HashSet能快速定位一個元素,但是你放到HashSet中的對象需要實現(xiàn)hashCode()方法,它使用了前面說過的哈希碼的算法。
而TreeSet則將放入其中的元素按序存放,這就要求你放入其中的對象是可排序的,這就用到了集合框架提供的另外兩個實用類Comparable和Comparator。
一個類是可排序的,它就應(yīng)該實現(xiàn)Comparable接口。有時多個類具有相同的排序算法,那就不需要在每分別重復(fù)定義相同的排序算法,只要實現(xiàn)Comparator接口即可。
集合框架中還有兩個很實用的公用類:Collections和Arrays。
Collections提供了對一個Collection容器進(jìn)行諸如排序、復(fù)制、查找和填充等一些非常有用的方法,Arrays則是對一個數(shù)組進(jìn)行類似的操作。
三、Map
Map是一種把鍵對象和值對象進(jìn)行關(guān)聯(lián)的容器,而一個值對象又可以是一個Map,依次類推,這樣就可形成一個多級映射。
對于鍵對象來說,像Set一樣,一個Map容器中的鍵對象不允許重復(fù),這是為了保持查找結(jié)果的一致性;如果有兩個鍵對象一樣,那你想得到那個鍵對象所對應(yīng)的值對象時就有問題了,可能你得到的并不是你想的那個值對象,結(jié)果會造成混亂,所以鍵的唯一性很重要,也是符合集合的性質(zhì)的。
當(dāng)然在使用過程中,某個鍵所對應(yīng)的值對象可能會發(fā)生變化,這時會按照最后一次修改的值對象與鍵對應(yīng)。對于值對象則沒有唯一性的要求。你可以將任意多個鍵都映射到一個值對象上,這不會發(fā)生任何問題(不過對你的使用卻可能會造成不便,你不知道你得到的到底是那一個鍵所對應(yīng)的值對象)。
Map有兩種比較常用的實現(xiàn):HashMap和TreeMap。
HashMap也用到了哈希碼的算法,以便快速查找一個鍵,
TreeMap則是對鍵按序存放,因此它便有一些擴(kuò)展的方法,比如firstKey(),lastKey()等,你還可以從TreeMap中指定一個范圍以取得其子Map。
鍵和值的關(guān)聯(lián)很簡單,用pub(Object key,Object value)方法即可將一個鍵與一個值對象相關(guān)聯(lián)。用get(Object key)可得到與此key對象所對應(yīng)的值對象。