用於存儲對象(對象的引用地址)
集合的結構圖
Collection
|--List:元素是有序的,按存入次序依次存放,元素可以重複。因爲該集合體繫有索引。
|--ArrayList : 底層的數據結構使用的是數組結構。特點:查詢速度很快,但是增刪稍慢。線程不同步。
|--LinkedList : 底層使用的是鏈表數據結構。特點:增刪速度很快,但查詢稍慢
|--vector : 底層是數組數據結構。 線程同步。 被ArrayList替代了。
|--Set :元素是無序的,按指定排序方法存放,元素不可以重複
|--HashSet : 底層數據結構是哈希表
|--TreeSet : 底層數據結構是二叉樹,可以對Set集合中的元素進行排序。
保證元素唯一性的依據:compareTo 方法 return 0;
Set 集合的功能和Collection 是一致的,其實,Set底層就是使用了Map集合。
Map
|--Hashtable : 底層是哈希表數據結構,不可以存入null鍵null值。該集合是線程同步的。JDK1.0 效率低
|--HashMap: 底層是哈希表數據結構,允許使用null值和null鍵,該集合是不同步的。將hashtable替代,jdk1.2 效率高
|--TreeMap : 底層是二叉樹數據結構。線程不同步。可以用於給Map集合中的鍵進行排序。
Collection 接口
集合的根接口。
1, add方法參數類型是Objext 以便於接收任意類型對象
2,集合中存儲的都是對象的引用(地址)
3,集合轉數組。
例:String s = new String[集合.size()] ;
爲什麼要將集合變數組?
爲了限定對元素的操作,(不需要進行增刪了,只能查就行)
注:Arrays 類中有大量操作數組的靜態方法。
Collections 類
此類完全由在 collection 上進行操作或返回 collection 的靜態方法組成.
常用方法:
sort(List<T> list) 根據元素的自然順序 對指定列表按升序進行排
swap(List<?> list, int i, int j)在指定列表的指定位置處交換元素
shuffle(List<?> list)對集合元素隨機排序
reverse(List<?> list)反轉指定列表中元素的順序
reverseOrder() 返回一個比較器,它強行逆轉實現Comparable 接口的對collection 的自然順序。
reverseOrder(Comparator<T> cmp)返回一個比較器,它強行逆轉指定比較器的順序
例 :
TreeSet<String> ts = new TreeSet<String>(Collections.reverseOrder(new Mycomparator()));
class Mycomparator implements Comparator<String>
{
public int compare(String s1,String s2)
{
int num = s1.length()-s2.length();
if(num>0)
return 1;
if(num<0)
return -1;
return s1.compareTo(s2);
}
}
下面是一些常用的集合:
List 集合
特點:所存入的元素是按存入順序存放的,元素可以重複。
List集合特有的迭代器 : ListIterator 是Iterator的子接口。
如果想要操作如添加,修改等,就需要使用其子接口 ListIterator
如何將數組轉變成集合呢?
Arrays :用於操作數組的工具類,裏面都是靜態方法。
數組轉集合有什麼好處?
可以使用集合的思想和方法來操作數組中的元素。
注意:將數組變成集合,不可以使用集合的增刪方法。
因爲數組的長度是固定的。
如果增刪會發生 UnsupportedOperationException 異常。
注意: 如果數組中的元素都是對象,那麼變成集合時,數組中的元素就直接轉成集合中的素。
如果數組中的元素都是基本數據類型,那麼會將 該數組 作爲集合的元素存在。
例:
String s [] ={"a","bbbd","kcd","z"};
List<String> list = Arrays.asList(s);
int a [] = {2,4,5};
List<int []> li = Arrays.asList(a); //2,4,5表示的是基本數據類型
Integer num[] = {2,4,5};
List<Integer> li2 = Arrays.asList(num); //2,4,5會自動裝箱,本身就是對象了
下面兩個常用的List集合,如果想支持隨機訪問,而不必在首尾的任何位置插入或刪除元素,那麼使用ArrayList 集合,如果要對集合進行頻繁的添加和刪除操作,對集合的首尾操作多,那麼使用LinkedList 集合更好。它提供了很多處理元素的方法。
1,ArrayList
底層的數據結構使用的是數組結構。
特點:查詢速度很快,但是增刪稍慢。線程不同步。
該類封裝了一個動態再分配的Object[]數組,每個ArrayList對象有一個capacity.這個capacity表示存儲列表中元素的數組的容量,在向集合中加入元素時,capacity在自動增加。
注意:contains(Object o) 方法,內部自動調用equals方法來比較的。
(o==null ? e==null : o.equals(e))
例:
//去重複元素
public static ArrayList method(ArrayList al)
{
List newal = new ArrayList();
Iterator it = al.iterator();
while(it.hasNext())
{
Object obj = it.next();
if(!newal.contains(obj))
newal.add(obj);
}
return newal;
}
2,LinkedList
底層使用的鏈表數據結構。特點:增刪速度很快,但查詢稍慢
該類添加了一些處理列表兩端元素的方法。實現所有可選的列表操作,並且允許所有元素
(包括null)。除了實現 List 接口外,LinkedList 類還爲在列表的開頭及結尾 get、remove 和 insert 元素提供了統一的命名方法。
3,Vector
底層是數組數據結構。 線程同步。 被ArrayList替代了。
枚舉就是Vector特有的取出元素方式
其實枚舉和迭代是一樣的,因爲枚舉的名稱以及方法的名稱都過長,所以被換代器取代了
使用方法:
Vector v = new Vector();
v.add("java001");
v.add("java002");
Enumeration en = v.elements();
while(en.hasMoreElements())
{
System.out.println(en.nextElement());
}
4,Stack
Stack 類表示後進先出(LIFO)的對象堆棧。它通過五個操作對類 Vector 進行了擴展 ,允許將向量視爲堆棧。它提供了通常的 push 和 pop 操作,以及取堆棧頂點的 peek 方法、測試堆棧是否爲空的 empty 方法、在堆棧中查找項並確定到堆棧頂距離的 search 方法.
Set 集合
特點:所存入的元素按指定排序方法存放元素(即不是按存入順序存放),不可以重複元素。
Set底層是使用了Map集合。
1,HasHSet
數據結構是哈希表,線程是非同步的
保證元素唯一性的原理:
判斷元素的hashCode值是否相同。如果相同,還會繼續判斷元素的equals方法,是爲true 如果不同,不會判斷equals
一般使用HashSet 都須要覆蓋hashCode()、equals() 兩個方法
例:
往HashSet 集合中存入自定義對象。
假設:姓名和年齡相同爲同一個人,是重複元素。
public int hashCode() //自動調用 ,複寫父類。調用這個纔有機會去調用equals方法
{
return name.hashCode()+age*37;//只要返回的hashCode值唯一就行
}
public boolean equals(Object obj) //複寫父類
{
if(!(obj instanceof Person))
return false;
Person p = (Person)obj;
return this.name.equals(p.name) && this.age == p.age;
}
注意:對於判斷元素是否存在,以及刪除等操作,依賴的方法是元素的hashCode和equals方法
記住:排序時,當主要條件相同時,一定要判斷一下次要條件。
hashCode()作用:
hashCode()值可以說成是對象在內存中計算出來的一個值,如果值等,說明對象是同一個對象,所以這裏程序員可以通過複寫hashCode()來判斷讓其那些是同一類型。爲了提高效率,讓其在查找集合中是否有相同對象的時候,把hashCode()值在內存中進行了分區存放的,這樣查找起來就效率提高了。正因爲這樣,所以在一個對象存入hash集合中的時候,就不要去更改對象中的值了,否則就會產生內存泄漏(就是內存浪費)。爲什麼呢?因爲更改了值後,導致hashCode的改變,導致存放的區域發生了變化,所以再去刪除這個對象的時候,這個對象已不在這個區域了,是無法刪除到這個對象的,刪除的是沒改之前的對象,而這個對象已沒用了。放在這裏就是一處內存泄漏。
2,TreeSet
底層數據結構是二叉樹。
特點:可以對Set集合中的元素進行排序。
但必須指明排序要求。
例:
TreeSet ts = new TreeSet(new MyCompare());
new MyCompare() 就是比較器
保證元素唯一性的依據:compareTo 方法 return 0;
排序方式: 兩種
TreeSet排序的第一種方式:
讓元素自身具備比較性。這種方式也稱爲元素的自然順序,或者叫做默認順序。
使用方法:
1,該類實現Comparable接口
2,覆蓋compareTo方法
例 :
class Student implements Comparable // 1,實現Comparable接口 該接口強制讓學生具備比較性
{
public int compareTo(Object obj) // 2,覆蓋compareTo方法,是自動調用的
{
//按照學生的年齡進行排序
if(!(obj instanceof Student))
throw new RuntimeException("不是學生對象");
Student s = (Student)obj;
if(this.age>s.age)
return 1;
if(this.age==s.age)
{
return this.name.compareTo(s.name); //字符串的比較功能
}
return -1;
/*
注意:如果想實現按怎麼進來的怎麼出去
只須 compareTo方法 return -1;或 return 1; 即可。
因爲二叉樹:存放的時候是大的放在右邊,小的放在左邊,
默認輸出從小到大。
-1 表示小 0 表示等 1表示大
*/
}
}
TreeSet的第二種排序方式:
當元素自身不具備比較性時,或者具備的比較性不是所需要的。
這時就需要讓集合自身具備比較性。
使用方法:
1,定義比較器,實現Comparator接口
2,覆蓋compare方法
3,將比較器對象作爲參數傳遞給TreeSet集合的構造函數。
例:
需求:現在要求按學生姓名排序。
class MyCompare implements Comparator //1,定義比較器,實現Comparator接口
{
public int compare(Object o1,Object o2) //2,複寫compare方法
{
Student s1 = (Student)o1;
Student s2 = (Student)o2;
int num = s1.getName().compareTo(s2.getName());
if(num==0)
{
if(s1.getAge()>s2.getAge())
return 1;
if(s1.getAge()<s2.getAge())
return -1;
return 0;
}
return num;
}
}
注意:如果自身具有比較性,但又傳入了比較器,那麼以比較器爲主。
Map 集合
Map接口不是Collection接口的繼承。
和Set很像,其實,Set底層就是使用了Map集合。
什麼時候使用map集合呢?
當數據之間存在映射關係時,就可以使用map集合,因爲Map集合中存放就是映射關係。
Map集合的兩種取出元素方式:
Map集合的取出原理:將Map集合轉成Set集合,在通過迭代器取出。
一,Set<K> keySet
將Map中所有的鍵存入到Set集合,因爲Set具備迭代器。
所以可以用迭代方式取出所有的鍵,在根據get方法。獲取每一個鍵對應的值。
例:
Map<String,String> map = new HashMap<String,String>();
Set<String> keySet = map.keySet(); //1,獲取Map集合的所有鍵,保存到Set集合
Iterator<String> it1 = keySet.iterator(); 以鍵值迭代
while (it1.hasNext())
{
String skey = it1.next();
String svalue = map.get(skey); 通過鍵值獲取對應的值。
System.out.println("key:"+skey+"_____"+svalue);
}
二,Set<Map.Entry<k,v>> entrySet
將Map集合中的映射關係存入到Set集合中,而這個關係的數據類型就是:Map.Entry然後在通過迭代器來取數據。
例:
Map<String,String> map = new HashMap<String,String>();
//將Map集合中的映射關係取出,存入到Set集合中
Set<Map.Entry<String,String>> entrySet = map.entrySet();
Iterator<Map.Entry<String,String>> it = entrySet.iterator();
while (it.hasNext())
{
Map.Entry<String,String> me = it.next();//迭代器中返回的值的類型和泛型<>裏的類型一樣
String key = me.getKey();
String value = me.getValue();
System.out.println(key+"..."+value);
}
1,HashMap
底層是哈希表數據結構,允許使用null值和null鍵,該集合是不同步的。將hashtable替代,jdk1.2 效率高 , 適合在集合中插入,刪除和定位元素。
例:
Map<String,String> map = new HashMap<String,String>();
map.put("01","zhangsan1");
map.put("01","zhangsan1");//如果出現添加相同的鍵,那麼後添加的值會覆蓋原有鍵對應值,並返回被覆蓋的值。
判斷一個鍵是否存在?
可以通過get方法的返回值來判斷一個鍵是否存在。通過返回null來判斷
System.out.println( map.get("04") );//返回null
獲取map集合中的 Collection 視圖。
Collection<String> coll = map.values();
2,TreeMap
底層是二叉樹數據結構。線程不同步。可以用於給Map集合中的鍵進行排序。
如果想按自然順序或自定義順序排序,使用該集合。
例:
字符串“osjfosj0fwamfw0fimdsoja”獲取該字符串中的字母出現的次數。
希望打印結果:a(1)b(3)c(2)....
思路:通過結果可以看到,每一個都有對應的次數,說明字母和次數之間都有映射關係。
所以選擇Map集合,因爲輸出結果是有序的,所以使用TreeMap
步驟:
1,將字符串轉換成字符數組,因爲要對每一個字母進行操作。
2,定義一個map集合,因爲打印結果的字母有順序,所以使用TreeMap集合
3,遍歷字符數組
將每一個字母作爲鍵去查map集合
如果返回null ,將該字母和次數1存入到map集合
如果返回不是null ,說明字母已存在,把對應的次數加1
4,遍歷map集合以指定形式打印。
public static void charCount2(String str)
{
char chs[] = str.toCharArray(); //1,返回字符數組
//泛型裏放的是引用對象
TreeMap<Character,Integer> tm = new TreeMap<Character,Integer>();
int count = 0;
for(int i=0;i<chs.length;i++)
{
Integer value = tm.get(chs[i]); //不存在就返回null
if(value!=null)
count = value;
count++;
tm.put(chs[i],count); //加入集合,如有就更新這對值
count = 0;
}
Set<Map.Entry<Character,Integer>> entrySet = tm.entrySet();
Iterator<Map.Entry<Character,Integer>> it = entrySet.iterator();
while (it.hasNext())
{
Map.Entry<Character,Integer> me = it.next();
Character ch = me.getKey();
Integer value = me.getValue();
System.out.print(ch+"("+value+")");
}
}
3,Hashtable
底層是哈希表數據結構,不可以存入null鍵null值。該集合是線程同步的。JDK1.0 效率低
擴展練習:
一個學校有多個班級,一個班級有多個學生(學號 姓名)。
拿一個集合來放班級。 (學校---班級)
拿一個集合來放學生。(班級--學生)
如:
學校集合,存放 班級
HashMap<String,HashMap<String,String>> czbk =
new HashMap<String,HashMap<String,String>>();
班級集合,存放 學生(學號 姓名)
HashMap<String,String> yure = new HashMap<String,String>();
HashMap<String,String> jiuye = new HashMap<String,String>();
//學校增加班級
czbk.put("yureban",yure);
czbk.put("jiuyeban",jiuye);
//班級增加學生
yure.put("01","zhansan");
yure.put("02","lisi");
jiuye.put("01","wangwu");
jiuye.put("02","zhaoliu");
//輸出也是,先取班級,再通過班級取出學生信息。或也可以使用其它的方法來完成,但使用這種方法呢,我們還得對建立Student類,來保存 學生信息,如果使用Map鍵值對呢,就可以不用寫。
例:
學校集合,存放 班級
HashMap<String,List<Student>> czbk = new HashMap<String,List<Student>>();
班級集合,存放 學生對象
List<Student> yure = new ArrayList<Student>();
List<Student> jiuye = new ArrayList<Student>();
//學生信息
class Student(){ }
Comparable 與 Comparator 區別?
Comparable :適用於一個類自身具有比較性,即具有自然順序的時候使用。
Comparator : 定義自己的比較方式(比較器),即本身不具備比較性,或具有的比較性,不是把需要的。
注意:當兩種排序都存在時,以比較器(Comparator )爲主。(比較常用)
一般寫好的代碼不要去修改,實現接口就行了。(接口:功能擴展)
例:
按字符串長度排序
字符串本身具備比較性,但是它的比較方式不是所需要的,這時就只能使用比較器Comparator 。
TreeSet ts = new TreeSet(new StrLenComparator());
class StrLenComparator implements Comparator //比較器
{
public int compare(Object o1,Object o2)
{
String s1 = (String)o1;
String s2 = (String)o2;
int num = s1.length()-s2.length();
if(num==0)
return s1.compareTo(s2);
return num;
}
}
Iterator 迭代器
什麼是迭代器?
就是從集合裏取出元素的方式。
因爲每一種集合都有取出元素操作,所以把這個取方式封裝成了一個類 Iterator
使用方法:
集合:ArrayList a1 = new ArrayList() ;
Iterator it = a1.iterator(); //獲取迭代器,用於取出集合中的元素
while(it.hasNext()) // 正向取出
{
System.out.println(it.next());
}
注意:在迭代時,不可以通過 集合對象 的方法操作集合中的元素。因爲會發生併發操作異常
所以,在迭代器時,只能用迭代器的方法操作元素。
但Iterator方法有限,只能對元素進行判斷,取出,刪除等操作.
List集合特有的迭代器 :ListIterator
它可以添加,修改,按任一方向遍歷列表,獲得迭代器在列表中的當前位置等。該接口只能通過List集合的ListIterator方法獲取。
例:
ListIterator li = a1.listIterator(al.size()) ; //這裏注意指定指針位置。
while(li.hasPrevious())//從後往前取出
{
System.out.prinln( li.previous() );
}
心得:
這麼多集合,我們在使用集合的時候要注意應該選用什麼樣的集合,選對集合,對於我們操作對象什麼都是很方便的,List集合中存放的元素是無序的,適用於對元素無須排序,而且元素可以重複出現。Set集合呢,元素是不能重複出現的,存放的元素進行過排列的。適用於要對對象按一定要求進行排序,去除重複元素等。這裏排序工作可以由Compareble或Comparetor (自定義比較器)來完成。Set集合底層就是由Map集合來完成的。Map集合呢,適用於保存那些有映射關係的。List 、Set 集合都是使用Iterator 迭代器來輸出集合內容,對於Liste還有一個特有的ListIterator 迭代方法,這個提供了操作集合的一些方法,如:刪除,修改等。Map集合的輸出要先通過自己的方法,轉換成Set集合,然後Set集合在通過Iteraor來輸出。