Java高級特性 第1節 集合框架和泛型
Java中,存儲多個同類型的數據,可以用數組來實現,但數組有一些缺陷:
- 數組長度固定不變,布恩那個很好的適應元素數量動態變化的情況
- 可以通過數組.length獲取數組長度,卻無法直接獲取數組中實際存儲的元素個數
- 數組采用在內存中分配連續空間的方式存儲,根據元素信息查找時的效率比較低,需要多次比較
Java提供了一套性能優良、使用方便的接口和類,他們都位於java.util包中。
一、Java中的集合
Java集合類主要由Map接口和Collection接口派生而來,Collection接口有兩個常用的子接口,所以說Java集合框架通常由三大類構成:Map接口、List接口、Set接口
二、List接口
Collection接口是最基本的集合接口,可以存儲一組不唯一、無序的對象。
List接口繼承自Collection接口,是有序集合,用戶可以使用索引訪問List接口中的元素,List接口中允許存放重復元素,即:list可以存儲一組不唯一、有序的對象
List接口常用的實現類有ArrayList和LinkedList:
- 使用ArrayList類動態存儲數據
ArrayList集合類對數組進行了封裝,實現了長度可變的數組,和數組采用同樣的存儲方式,在內存中分配連續的空間,也稱ArrayList為動態數組;
但他不等同於數組,ArrayList集合中可以添加任何類型0 1 2 3 4 5 ... aaaa dddd cccc aaaa eeee dddd ...
ArrayList類的常用方法:
方法名 | 說明 |
boolean add(Object obj) | 將指定元素obj追加到集合的末尾 |
boolean add(int index, Object obj) | 將指定元素obj插入到集合中指定的位置 |
Object get(int index) | 返回集合中指定位置上的元素 |
int size() | 返回集合中的元素個數 |
Object get(int index) | 返回指定索引處的元素,去除的元素是Object類型,使用前需強制轉換 |
Object set(int index, Object obj) | 用指定元素obj替代集合中指定位置上的元素 |
blooean contains (Object o) | 判斷類表中是否村子啊指定元素o |
int indexOf(Object obj) | 返回指定元素在集合中出現的索引位置 |
blooean remove(Object o) | 從列表中刪除元素0 |
Object remove(int index) | 從列表中刪除指定位置元素,起始索引位置從0開始 |
void clear() | 清空集合中所有元素 |
package cn.CollectionAndMap; import java.util.*; //導包 /* * ArrayList集合 */ //狗狗類 class Dog{ private String name; private String strain; //品種 public Dog(String name,String strain){ this.name = name; this.strain = strain; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getStrain() { return strain; } public void setStrain(String strain) { this.strain = strain; } } public class ArrayListTest { public static void main(String[] args) { // 1、創建多個狗狗對象 Dog ououDog = new Dog("歐歐", "雪娜瑞"); Dog yayaDog = new Dog("亞亞", "拉布拉多"); Dog meimeiDog = new Dog("美美", "雪娜瑞"); Dog feifeiDog = new Dog("菲菲", "拉布拉多"); // 2、創建ArrayList集合對象並把多個狗狗對象放入其中 List dogs = new ArrayList(); dogs.add(ououDog); dogs.add(yayaDog); dogs.add(meimeiDog); dogs.add(2, feifeiDog); // 3、輸出刪除前集合中狗狗的數量 System.out.println("刪除之前共計有" + dogs.size() + "條狗狗。"); // 4、刪除集合中第一個狗狗和feifeiDog狗狗 dogs.remove(0); dogs.remove(feifeiDog); // 5、顯示刪除後集合中各條狗狗信息 System.out.println("\n刪除之後還有" + dogs.size() + "條狗狗。"); System.out.println("分別是:"); for (int i = 0; i < dogs.size(); i++) { Dog dog = (Dog) dogs.get(i); System.out.println(dog.getName() + "\t" + dog.getStrain()); } //6、判斷集合中是否包含指定狗狗信息 if(dogs.contains(meimeiDog)) System.out.println("\n集合中包含美美的信息"); else System.out.println("\n集合中不包含美美的信息"); } }
- ArrayList集合優點:遍歷和隨機訪問元素的效率比較高,對數據頻繁檢索時效果較高
2.使用LinkedList類動態存儲數據
LinkedList類是List接口的鏈接列表實現類,他支持實現所有List接口可選的列表的操作,並且允許元素值是任何數據,包括null。
- LinkedList采用鏈表存儲方式存儲數據,優點:對數據添加、刪除、修改比較多時效率比較高(但查找效率低)
存儲方式示意圖如上圖。
LinkedList常用方法:
方法名 | 說明 |
void addFirst(Object obj) | 將指定元素插入到當前集合的首部 |
void addLast(Object obj) | 將指定元素插入到當前集合的尾部 |
Object getFirst() | 獲取當前集合的第一個元素 |
Object getLast() | 獲取當前集合的最後一個元素 |
Object removeFirst() | 移除並返回當前集合的第一個元素 |
Object removeLast() | 移除並返回當前集合的最後一個元素 |
三、Set接口
Set集合中的對象並不按特定的方式排序,並且不能保存重復的對象,即:Set接口可以存儲一組唯一、無序的對象。
註意,Set集合中存儲對象的引用時,也不能保存重復的對象引用。
1.使用HashSet類動態存儲數據
HashSet集合的特點:集合內的元素時無序排列的;HashSet類時非線程安全的;允許集合元素值為null;
常用方法:
方法名 | 說明 |
blooean add(Object o) | 如果此Set中尚未包含制定元素o,則添加元素o |
void clear() | 移除此Set中所有元素 |
int size() | 返回Set中元素的數量 |
blooean isEmpty() | 如果此Set中不包含任何元素,則返回true |
blooean contains(Object o) | 如果此Set中包含指定元素o,則返回true |
blooean remove(Object o) | 如果指定元素在此Set中,則將其移除 |
package cn.CollectionAndMap; import java.util.HashSet; import java.util.Set; public class HashSetTest { public static void main(String[] args) { Set set=new HashSet(); String s1=new String("java"); String s2=s1; String s3="java"; String s4="jav"+"a"; String s5=new String("JAVA"); set.add(s1); set.add(s2); set.add(s3); set.add(s4); set.add(s5); System.out.println(set.size()); //遍歷,HashSet中沒有get()方法 for (Object obj:set){ String string = (String)obj; System.out.println(string); } } }
四、Iterator接口
Iterator接口表示對集合進行叠代的叠代器,Iterator接口為集合而生,專門實現集合的遍歷。此接口主要有2個方法:
- hasNext():判斷是否存在下一個可訪問的元素,如果仍有元素可以叠代,則返回true;
- next():返回要訪問的下一個元素。
凡是由Collection接口派生而來的接口或類,都實現了iterator()方法,iterator()方法返回一個Iteraator對象。
- 使用Iterator遍歷集合
package cn.CollectionAndMap; import java.util.*; public class ArrayListTest { public static void main(String[] args) { //1、創建ArrayList集合對象 List list= new ArrayList(); list.add(“張三”); list.add(“李四”); list.add(“王五”); list.add(“李明”); System.out.println("使用Iterator遍歷,元素分別是:"); // 2、獲取叠代器 Iterator it = list.iterator(); while(it.hasNext()){ String name = (String)it.next(); System.out.println(name); } }
五、Map接口
Map接口存儲一組成對的鍵(key)-值(value)對象,提供key到value的映射,通過key來檢索。Map接口中的key不要求有序,不允許重復,value同樣不要求有序,但允許重復。
常用方法:
方法名 | 說明 |
Object put(Object key,Object value) | 將互相關聯的一個key和value放入該集合,,如果已經存在key對應的value,則舊值將被替換 |
Objectremove(Object key) | 從當前集合中移除與指定key相關聯的映射,並返回該key關聯的舊的value值,如果key沒有任何關聯,則返回null |
Object get(Object key) | 獲得與key關聯的value,如果key沒有任何關聯,則返回null |
int size() | 返回集合中的元素個數 |
blooean containsKey(Object key) | 判斷集合中是否存在指定key |
Object keySet(int index, Object obj) | 用指定元素obj替代集合中指定位置上的元素 |
blooean containsValue (Object value) | 判斷集合中是否存在指定value |
void clear() | 清除集合中所有的元素 |
blooean isEmpty() | 判斷集合中是否存在元素 |
Collection values(int index) | 獲取所有值的集合 |
1.使用HashMap類動態存儲數據
最常用的Map實現類,優點:查詢指定元素效率較高。
- 數據添加到HashMap集合中後,所有數據的數據類型將轉換為Object類型,所以從中獲取數據時需要進行強制轉換;
- HashMap類不保證映射的順序,特別是不保證順序恒久不變。
- 遍歷HashMap時可以遍歷鍵集和值集
package cn.CollectionAndMap; import java.util.*; /** * 測試HashMap的多個方法。 */ public class HashMapTest { public static void main(String[] args) { // 1、使用HashMap存儲多組國家英文簡稱和中文全稱的鍵值對 Map countries = new HashMap(); countries.put("CN", "中華人民共和國"); countries.put("RU", "俄羅斯聯邦"); countries.put("FR", "法蘭西共和國"); countries.put("US", "美利堅合眾國"); // 2、顯示"CN"對應國家的中文全稱 String country = (String) countries.get("CN"); System.out.println("CN對應的國家是:" + country); // 3、顯示集合中元素個數 System.out.println("Map中共有"+countries.size()+"組數據"); /*4、兩次判斷Map中是否存在"FR"鍵*/ System.out.println("Map中包含FR的key嗎?" + countries.containsKey("FR")); countries.remove("FR"); System.out.println("Map中包含FR的key嗎?" + countries.containsKey("FR")); /* 5、分別顯示鍵集、值集和鍵值對集*/ System.out.println(countries.keySet()); System.out.println(countries.values()); System.out.println(countries); /*6、遍歷可使用:增強for循環、普通for循環、叠代器Iterator*/ Iterator iterator = countries.keySet().iterator(); if(iterator.hasNext()){ //iteratorV.next() 遍歷key集 } Iterator iteratorV = countries.values().iterator(); if(iteratorV.hasNext()){ //iteratorV.next() 遍歷值集 } /* 7、清空 HashMap並判斷*/ countries.clear(); if(countries.isEmpty()) System.out.println("已清空Map中數據!"); } }
六、使用Collections類操作集合
Collections類時Java提供的一個集合操作工具類,它包含了大量的靜態方法,用於實現對集合的排序、查找和替換等操作。
Collections和Collection是不同的,前者是集合的操作類,後者是集合接口。
package cn.CollectionAndMap; import java.util.*; /* * Collections操作類測試 */ //1)compareTo()方法用於比較此對象與指定對象的順序,若該對象小於、等於、大於指定對象,分別返回-1、0、1 //實現Compare接口的對象列表(或數組)可以通過Collections.sort()(或Arrays.sort())進行自動排序 class Student implements Comparable{ int number = 0;//學號 String name = ""; //姓名 String gender = ""; //性別 public Student(int num){ this.number = num; } public int compareTo(Object obj){ Student student = (Student)obj; if(this.number>student.number){ return 1; }else if(this.number==student.number){ return 0; }else{ return -1; } } } //測試類 public class CollectionsMethodsTest { public static void main(String[] args) { //1) 排序(Sort),使用sort方法可以根據元素的自然順序對指定列表按升序進行排序。 // 列表中的所有元素都必須實現 Comparable 接口。 // 此列表內的所有元素都必須是使用指定比較器可相互比較的 System.out.println("---------------(1)------------------------"); Student student1 = new Student(112); Student student2 = new Student(111); Student student3 = new Student(23); Student student4 = new Student(456); Student student5 = new Student(231); List lists = new ArrayList(); lists.add(student1); lists.add(student2); lists.add(student3); lists.add(student4); lists.add(student5); Collections.sort(lists); for (int i = 0; i < lists.size(); i++) { Student stu = (Student)(lists.get(i)); System.out.println(stu.number); //結果:112,111,23,456,231 } //2) 混排(Shuffling) //混排算法所做的正好與 sort 相反: 它打亂在一個 List 中可能有的任何排列的蹤跡。 // 也就是說,基於隨機源的輸入重排該 List, 這樣的排列具有相同的可能性(假設隨機源是公正的)。 // 這個算法在實現一個碰運氣的遊戲中或在生成測試案例時是非常有用的。 System.out.println("-----------------(2)----------------------"); List list = new ArrayList(); list.add(student1.number); list.add(student2.number); list.add(student3.number); list.add(student4.number); list.add(student5.number); Collections.shuffle(list); for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); ////結果可能是:112,111,23,456,231 } //3) 反轉(Reverse) //使用Reverse方法可以根據元素的自然順序 對指定列表按降序進行排序。 System.out.println("----------------(3)-----------------------"); Collections.reverse(list); for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); //結果:231,456,23,111,112 } //4) 替換所以的元素(Fill) //使用指定元素替換指定列表中的所有元素。 System.out.println("-----------------(4)----------------------"); List tempList = new ArrayList(); tempList.add(11); tempList.add(12); tempList.add(13); Collections.fill(tempList,"aaa"); for (int i = 0; i < tempList.size(); i++) { System.out.println("tempList[" + i + "]=" + tempList.get(i)); //結果:aaa,aaa,aaa,aaa,aaa } //5) 拷貝(Copy) //用兩個參數,一個目標 List 和一個源 List, 將源List的元素拷貝到目標List,並覆蓋它的內容。 // 目標 List 至少與源List一樣長。如果它更長,則在目標 List 中的剩余元素不受影響。 //Collections.copy(list,li): 後面一個li是目標列表 ,前一個list是源列表 System.out.println("----------------(5)-----------------------"); List targetList = new ArrayList(); String str[] = {"dd","aa","bb","cc","ee"}; for(int j=0;j<str.length;j++){ targetList.add(str[j]); } Collections.copy(targetList,list); for (int i = 0; i <targetList.size(); i++) { System.out.println("targetList[" + i + "]=" + targetList.get(i)); } //6) 返回Collections中最小元素(min) //根據指定比較器產生的順序,返回給定 collection 的最小元素。 // collection 中的所有元素都必須是通過指定比較器可相互比較的 System.out.println("-------------------(6)--------------------"); int min = (int)Collections.min(list); System.out.println("list[最小]=" + min); //結果:23 //7) 返回Collections中最小元素(max) //根據指定比較器產生的順序,返回給定 collection 的最大元素。 // collection 中的所有元素都必須是通過指定比較器可相互比較的 System.out.println("----------------(7)-----------------------"); int max = (int)Collections.max(list); System.out.println("list[最小]=" + max); //結果:456 //8) lastIndexOfSubList、IndexOfSubList //返回指定源列表中最後一次、第一次出現指定目標列表的起始位置 //Collections.lastIndexOfSubList(list,li); list源列表 li目標列表 System.out.println("-------------------(8)--------------------"); int arr[] = {111}; List targetList1 = new ArrayList(); for(int j=0;j<arr.length;j++){ targetList1.add(arr[j]); } int locations = Collections.lastIndexOfSubList(list,targetList1); System.out.println("最後一次出現位置:"+ locations); //結果 3 int locations1 = Collections.indexOfSubList(list,targetList1); System.out.println("第一次出現位置:"+ locations1); //結果 1 } }
運行結果:
---------------(1)------------------------ 23 111 112 231 456 -----------------(2)---------------------- 111 112 23 231 456 ----------------(3)----------------------- 456 231 23 112 111 -----------------(4)---------------------- tempList[0]=aaa tempList[1]=aaa tempList[2]=aaa ----------------(5)----------------------- targetList[0]=456 targetList[1]=231 targetList[2]=23 targetList[3]=112 targetList[4]=111 -------------------(6)-------------------- list[最小]=23 ----------------(7)----------------------- list[最小]=456 -------------------(8)-------------------- 最後一次出現位置:4 第一次出現位置:4
七、泛型
泛型時jdk1.5的新特性,泛型的本質是類型轉換,也即是說所操作的數據類型被指定為一個參數,使代碼可以以適應於多種類型。
Java引進泛型的好處是安全簡單,其所有強制轉換都是自動和隱式進行的,提高了代碼的重用率。
- 泛型的定義
將對象類型作為參數,指定到其他類或方法上,從而保證類型轉換的安全性和穩定性,泛型的本質是參數化類型
語法:類1或者接口<類型實參>對象 = new 類2<類型實參>()
註意:首先,“類1”可以是“類2”本身,可以是“類1”的子類,還可以是接口的實現類;其次,“類2”的類型實參必須與“類1”中的類型實參相同。例如:ArrayList<String> list = new ArrayList<String>() - 泛型在集合中的應用
使用泛型集合在創建集合對象時指定集合中元素的類型,從集合中去除元素時無需進行類型的強制轉換,並且如果把非指定類型對象放入集合,會出現編譯錯誤。
package cn.CollectionAndMap; import java.util.*; /* * ArrayList集合 */ //狗狗類 class Dog{ private String name; private String strain; //品種 public Dog(String name,String strain){ this.name = name; this.strain = strain; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getStrain() { return strain; } public void setStrain(String strain) { this.strain = strain; } } public class ArrayListTest { public static void main(String[] args) { /* 1、創建多個狗狗對象*/ Dog ououDog = new Dog("歐歐", "雪娜瑞"); Dog yayaDog = new Dog("亞亞", "拉布拉多"); Dog meimeiDog = new Dog("美美", "雪娜瑞"); Dog feifeiDog = new Dog("菲菲", "拉布拉多"); /* 2、創建Map集合對象並把多個狗狗對象放入其中*/ Map<String,Dog> dogMap=new HashMap<String,Dog>(); dogMap.put(ououDog.getName(),ououDog); dogMap.put(yayaDog.getName(),yayaDog); dogMap.put(meimeiDog.getName(),meimeiDog); dogMap.put(feifeiDog.getName(),feifeiDog); /*3、通過叠代器依次輸出集合中所有狗狗的信息*/ System.out.println("使用Iterator遍歷,所有狗狗的昵稱和品種分別是:"); Set<String> keys=dogMap.keySet();//取出所有key的集合 Iterator<String> it=keys.iterator();//獲取Iterator對象 while(it.hasNext()){ String key=it.next(); //取出key Dog dog=dogMap.get(key); //根據key取出對應的值 System.out.println(key+"\t"+dog.getStrain()); } /*//使用foreach語句輸出集合中所有狗狗的信息 for(String key:keys){ Dog dog=dogMap.get(key); //根據key取出對應的值 System.out.println(key+"\t"+dog.getStrain()); }*/ } }
- 深入理解泛型
泛型在接口、類、方法等方面也有著廣泛的應用。泛型的本質是參數化類型,其重要性在於允許創建一些類、接口和方法,其所操作的數據類型被定義為參數,可以在真正使用時指定其類型;
- 參數化類型:包含一個類或者接口,以及實際的參數列表
- 類型變量:是一種非限定性標識符,用來指定類、接口或方法的類型
1.定義泛型類、泛型接口和泛型方法
1)泛型類:具有一個或多個類型參數的類
訪問修飾符 class className<TypeList>
創建泛型類實例:new className<TypeList>(argList)
2)泛型接口:具有一個或多個類型參數的接口
interface interfaceName<TypeList>
泛型類實現泛型接口:訪問修飾符 class className<TypeList> implements interfaceName<TypeList>
package cn.CollectionAndMap; //1.定義泛型接口 interface TestInterface<T>{ public T getName(); } //2.定義泛型類 class Students<T> implements TestInterface<T>{ private T name; //設置的類型你有外部決定 public Students(T name){ this.setName(name); } @Override public T getName() { //返回類型由外部決定 return name; } public void setName(T name) { this.name = name; } } public class Generices{ public static void main(String[] args) {
//3.實例化 TestInterface<String> testInterface = new Students<String>("李四"); System.out.println("學生的名字是:"+testInterface.getName()); } }
3)泛型方法:帶有類型參數的方法,一些方法常常要對某一類數據進行處理,若處理的數據不確定,則可以通過泛型方法的方式來定義
訪問修飾符 <參數類型> 返回值 方法名(類型參數列表){.....}
泛型類實現泛型接口:訪問修飾符 class className<TypeList> implements interfaceName<TypeList>
package cn.CollectionAndMap; //泛型方法 public class GenericesMethods { //定義泛型方法 public<Integer> void showSize(Integer o){ System.out.println(o.getClass().getName()); } public static void main(String[] args) { GenericesMethods ger = new GenericesMethods(); ger.showSize(10); } }
- 多個參數類型的泛型類
泛型類的類型參數可以有多個,如HashMap<K,V>一個指定key的類型,一個指定value的類型。
package cn.CollectionAndMap; //創建泛型類 public class GenericesMethods<T,V> { private T a; private V b; public GenericesMethods(T a,V b){ this.setA(a); this.setB(b); } public T getA() { return a; } public void setA(T a) { this.a = a; } public V getB() { return b; } public void setB(V b) { this.b = b; } public void showType(){ System.out.println("a的類型是:"+a.getClass().getName()); System.out.println("b的類型是:"+b.getClass().getName()); } public static void main(String[] args) { GenericesMethods<String,Integer> ger = new GenericesMethods<String,Integer>("李白",23); ger.showType(); } }
-
從泛型類派生子類
面向對象的特性同樣適用於泛型類,所以泛型類也可以繼承,不過,繼承了泛型類的子類,必須也是泛型類。
語法:class 子類<T> extends 父類<T>{....}package cn.CollectionAndMap; //創建泛型類 abstract class GenericesFatherClass<T,V>{ public abstract void print(); } //定義一個泛型子類繼承泛型父類 public class GenericesMethods<T,V> extends GenericesFatherClass<T,V>{ private T a; private V b; public GenericesMethods(T a,V b){ this.setA(a); this.setB(b); } public T getA() { return a; } public void setA(T a) { this.a = a; } public V getB() { return b; } public void setB(V b) { this.b = b; } //重寫父類方法 public void print(){ System.out.println("名字:"+a+",年齡:"+b); } public void showType(){ System.out.println("a的類型是:"+a.getClass().getName()); System.out.println("b的類型是:"+b.getClass().getName()); } public static void main(String[] args) { GenericesMethods<String,Integer> ger = new GenericesMethods<String,Integer>("李白",23); ger.showType(); ger.print(); } }
Java高級特性 第1節 集合框架和泛型