tags :
- java基礎
flag: yellow
@toc
JavaDay18 泛型
一、複習
1.ArrayList
ArrayList底層維護的是一個Object類型的數組,使用無參構造方法,創建一個ArrayList集合對象.默認的元素個數爲10
- 特徵:
- 查詢快,增刪慢
- 查詢快:
使用的是數組的下標訪問方式,可以直達目標位置 - 增刪慢:
- 增加: 有可能會觸發數組的擴容機制,會創建一個新的數組,新的數組元素個數大約是原數組的1.5倍。然後會有一個數組拷貝的過程,這個過程是將原數組裏面的每一個元素挨個複製到新數組中,這個操作消耗大量的時間和空間;
- 增加的原理是:
newCapacity = oldCapacity + (oldCapacity >> 1);
- 刪除: 是從Object數組中刪除是一個元素,刪除之後,後面元素會向前移動,移動的過程是一個複製的過程,這個操作,比較浪費時間;
Vector
線程安全,效率較低的ArrayList JDK1.0
2.LinkedList:
底層維護的是一個鏈表 《數據結構》
增刪快,查找慢
3.HashSet
底層維護的是一個哈希表,存儲效率極高
- 存儲原理:
調用存入對象的hashCode方法,獲取到該對象的hashCode的值,通過【移位 計算】,計算該對象應該放到哈希表的哪一個位置-
情況1:
該位置沒有任何元素,直接添加 -
情況2:
該位置存在其他元素,這時候會調用對象的equals方法,和在該位置保存的其他元素進行一一比較,如果所有的比較結果都爲false,表示不相同,可以添加。如果出現任何一個true,表示和該位置其他的元素相同,不能添加
-
4.TreeSet
存入TreeSet的元素必須有自然順序或者是存在比較方式
自定義類對象,想要放入到TreeSet集合中,有兩種方式:
- 該自定義類【遵從】Comparable接口,實現compareTo(Object o) 方法
- 給TreeSet提供自定義比較器,創建自定義比較器需要【遵從】Comparator接口實現compare(Object o1, Object o2);
二、泛型
(一)引言
【問題】:
- 在 ArrayList 中可以放入任意類型的數據,但實際操作中數據類型不一致會導致更多的錯誤;
- 例如下列代碼中,就算明知取出的數據時 string 類型,但是還是得通過強制類型轉換才能得到 string 類型數據;
ArrayList list = new ArrayList();
list.add(new Demo1); //Demo1爲該類名稱
list.add("hello");
String str = (String)(list.get(1)); //必須通過強轉才能獲得
【期望】:
集合中的數據類型能夠統一,即涉及到數據類型一致化問題;
【解決方式】:
泛型(JDK1.5 之後)
(二)使用
泛型作用:
- 解決集合中數據類型不一致的問題,要求保存什麼數據,只能保存什麼數據,否則報錯,即將異常提前;
- 從集合中取數據,保存的是什麼數據,取出也是什麼數據,不需要強制類型轉換
標準格式:
ArrayList<String> arrayList = new ArrayList<String>();
代碼示例:
package study;
import java.util.ArrayList;
public class FanXin_Use {
public static void main(String[] args) {
//這裏<String>即爲泛型,要求這個ArrayList集合中有且只能保存String類型的數據
ArrayList<String> arrayList = new ArrayList<String>();
arrayList.add("hello");
arrayList.add("world");
//無法保存其他數據類型
//arrayList.add(1);
String string = arrayList.get(1);
System.out.println(string);
}
}
(三)泛型在函數中的使用
【需求】
定義一個方法,可以接受任意數據類型,而且要求返回的數據類型,就是你傳入的數據類型;例如:傳入 String 類型,返回 string 類型,傳入 Demo1 類型,返回 Demo1 類型;
泛型的使用需要:
佔位符,即一個大寫字母,只是一個佔位符,沒有實際含義,不同地方定義的佔位符沒有聯繫;
泛型在函數中使用的格式:
修飾符 <聲明的自定義泛型佔位符> 返回值類型(可以使用自定義泛型) 函數名(形式參數列表(可以使用泛型)){
函數體(函數體中,所有使用到自定義泛型的地方,數據類型都被傳入數據類型替換)
}
代碼示例:
package study;
public class FanXin_Fun {
public static void main(String[] args) {
String string = getType("hello"); //這裏傳入的“hello”是字符串類型,因此調用的時候,所有的E相當於就是字符串類型;
FanXin_Fun fanXin_Fun = getType(new FanXin_Fun());
int num = getType(5);
}
//<E>是自定義泛型的佔位符,表示在該函數中可以使用佔位符E,而E的具體數據類型由傳入的參數控制,這樣的操作可以讓函數多元化,更加簡單;
public static <E> E getType(E e) {
return e;
}
}
上面代碼中 num 是 Integer 類型,是一個包裝類
包裝類:
Java 是完全面向對象的語言,在 Java 中萬物皆對象,如果是要保存類對象,那麼八大基本數據類型就無法使用,所以,Java 提供了一個包裝機制,包裝基本數據類型,讓其變成類對象;稱爲自動封箱;
基本數據類型 | 封裝之後 |
---|---|
short | Short |
int | Integer |
byte | Byte |
long | Long |
double | Double |
float | Float |
boolean | Boolean |
char | Character |
如果使用包裝類直接賦值給普通的基本數據類型,該操作稱爲 拆箱;
(四)在類內使用泛型和匿名內部類
格式:
class 類名<自定義泛型的佔位符> {
//在這裏所用到的泛型和用戶創建對象時候聲明的是一致的;
}
注意事項:
- 一個類聲明的自定義泛型,如果在創建該類對象的時候,確定了泛型的具體數據類型,那麼整個類內所有用到該泛型佔位符的非靜態成員方法,使用的數據類型都是創建時候確定的類型;
- 如果創建使用了自定義泛型類對象,但是沒有確定泛型的具體類型,那麼編譯器會把這個泛型認爲是 Object 類型;
- 類中聲明的自定義泛型,不能在類中的靜態方法使用,如果想讓靜態方法使用泛型,則需要自己聲明、自己使用,類似於方法中使用泛型;
- 4.建議:如果在代碼中出現了多個使用不同泛型的地方,使用不同名字的佔位符,一般常用的佔位符爲:T 和 E
代碼示例:
package study;
import java.util.Comparator;
//異常類
class InvalidArrayException extends Exception{
public InvalidArrayException(String message) {
super(message);
}
}
class InvalidComparatorException extends Exception{
public InvalidComparatorException (String message) {
super(message);
}
}
//泛型類
class ArrayTools<A>{
/**
* 利用泛型,來滿足不同數據類型的排序算法,可以在創建類對象時約束
* @param array A類型,泛型的數組,可以是任意類型
* @param com <? super A> 是A類型的比較器或者其父類的比較器
* @throws InvalidArrayException 數組無效異常
* @throws InvalidComparatorException 比較器無效異常
*/
public void selectSortUsingCompare(A[ ] array , Comparator<? super A> com) //這裏的?super A表示可以傳入A及其父類的數據類型
throws InvalidArrayException,InvalidComparatorException{
//參數合法性判斷
if (null == array || array.length == 0) {
throw new InvalidArrayException("數組無效");
}else if (null == com) {
throw new InvalidComparatorException("比較器無效");
}
for (int i = 0; i < array.length - 1; i++) {
int index = i;
for (int j = i; j < array.length; j++) {
if (com.compare(array[index], array[j]) > 0) {
index = j;
}
}
if (index != i) {
A temp = array[index];
array[index] = array[i];
array[i] = temp;
}
}
}
public void printArray(A[] array) {
for (A a : array) {
System.out.println(a);
}
}
public static <T> void test(T a) { //因爲靜態方法比類加載早,所以如果想要使用泛型,則需要自己聲明 ,類裏面的泛型與之無關
System.out.println(a);
}
}
public class FanXin_class {
public static void main(String[] args) throws InvalidArrayException,InvalidComparatorException{
Integer [] array = {1,3,4,3,6,2,8,1};
ArrayTools<Integer> tools = new ArrayTools<Integer>();
tools.selectSortUsingCompare(array, new Comparator<Integer>() {
@Override
public int compare(Integer arg0, Integer arg1) {
return arg0 - arg1;
}
});
tools.printArray(array);
}
}
(五) 接口中使用泛型
在接口中定義泛型:
格式 :
interface 接口名<自定義泛型的佔位符> {
//成員變量 缺省屬性:public static final 定義時必須初始化
//成員方法 缺省屬性:abstract
}
兩種【遵從】帶有自定義泛型的接口方式:
- 更加自由,需要使用的泛型類型,在創建對象時確定,類似ArrayList
- 適合原本這個類就沒有使用泛型的情況,例如:一個類遵從Comparable接口 實現 compareTo方法,這裏可以在【遵從】時,確定Comparable需要的泛型具體數據類型,減少沒有必要的強制類型轉換
代碼示例:
interface A<T> {
public void testA(T t); //這個方法中使用了定義接口時聲明的自定義泛型
}
//PlanA
//一個類【遵從】接口,而且類中聲明的自定義泛型和接口泛型一致,沒有確定泛型的具體類型,由調用者來確定
class TestClass1<T> implements A<T> {
@Override
public void testA(T t) {
System.out.println(t.getClass() + "類型!!!");
}
}
//PlanB
//一個類【遵從】接口,但是接口的泛型已經被確定的數據類型替代
class TestClass2 implements A<String> {
@Override
public void testA(String t) {
System.out.println("String類型的方法");
}
}
public class Demo5 {
public static void main(String[] args) {
TestClass1<Integer> test = new TestClass1<Integer>();
test.testA(5);
TestClass2 test2 = new TestClass2();
test2.testA("233333");
}
}
(六)泛型的上下限
-
泛型的上下限
<? super T> <? extends T> T 泛型的佔位符 ? 通配符(表示一個字符) super: 調用父類方法的關鍵字 extends: 繼承的關鍵字 -
需求:定義一個函數接受任意類型數值的集合,但是這個數據必須是數值類型
數值類型:Number 已知子類:Integer Short Long Double Float (包裝類)要求傳入的對象是Number類對象或者其子類對象
Integer extends Number; //Integer 是 Number 的子類
Float extends Number;
Double extends Number;? extends Number;//這就是泛型的上限:
<? extends E> //通用類型 -
需求:定義一個函數能夠傳入一個任意類型的集合,但是要求集合裏面保存的數據必須是Number類對象,或者其父類對象
<? super E>泛型的下限
例如:<? super Number>
能夠保存的數據是Number類對象本身或者其父類對象
package com.qfedu.a_generticity;
import java.util.ArrayList;
import java.util.Collection;
public class Demo6 {
public static void main(String[] args) {
ArrayList<Number> list1 = new ArrayList<Number>();
ArrayList<Double> list2 = new ArrayList<Double>();
ArrayList<String> list3 = new ArrayList<String>();
ArrayList<Object> list4 = new ArrayList<Object>();
//傳入的實際參數是一個ArrayList對象,裏面保存的數據是Number類型
test1(list1);
test1(list2);
//test1(list3); 不行,因爲ArrayList<String> 裏面保存的String類型不是Number的子類
test2(list1);
test2(list4);
}
/**
*
* @param c Collection<? extends Number> 要求傳入的是一個Collection集合接口的實現類
* 而且要求該實現類裏面保存的數據是Number類對象本身或者其子類對象
*/
public static void test1(Collection<? extends Number> c) {
System.out.println(c.toString());
}
public static void test2(Collection<? super Number> c) {
System.out.println("泛型的下限!!!");
}
}
三、Map
- 回顧:
—| Collection
------| List
----------| ArrayList 查詢快 增刪慢
----------| LinkedList 查詢慢,增刪快
----------| Vector 線程安全的ArrayList
------| Set
----------| HashSet 掌握它的存儲原理
----------| TreeSet
----|比較器:
Comparable接口 實現compareTo方法
Comparator接口 實現compare方法
生活中有關聯,有關係的數據更多一點。例如:賬號 密碼;鑰匙 鎖
Map
—| Map<K, V> 雙列集合,這是一個接口
------| HashMap 實現類
------| TreeMap
K:Key 鍵 !!! 唯一值!!! 不允許重複!!!
V:Value 值 一個鍵(Key)對應一個值(Value) 可以重複的
在Map<K, V> 雙列集合中,保存的只能是一個鍵(Key)值(Value)對!!!
Map中要學習的方法:
- 增
- put(K key, V value); 添加一個鍵(Key)值(Value)對
- putAll(Map<? extends K, ? extends V> map);添加一個符合數據類型的Map雙列集合
- 刪
- clear(); 清空所有的鍵(Key)值(Value)對
- remove(Object key); 根據Key刪除對應的鍵(Key)值(Value)對
- 改
- put(K key, V value); 當鍵(Key)存在時,這個操作是重新修改值(Value)
- 查
-
size(); 獲取鍵值對個數
-
get(Object key); 通過鍵(Key)找出對應的值(Value)
-
containsKey(Object key); 查看這個Key是否在Map中存在
-
containsValue(Object value); 查看這個Value是否在Map存在
-
keySet(); 返回所有鍵(Key)Set集合
-
values(); 返回所有值(Value)Collection集合
-
代碼示例:
package com.qfedu.b_map;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Demo1 {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
//使用put(K k, V v)添加元素
map.put("薛之謙", "高磊鑫");
map.put("鹿晗", "關曉彤");
map.put("宋仲基", "宋慧喬");
map.put("余文樂", "王棠雲");
map.put("王寶強", "馬蓉");
System.out.println(map);
Map<String, String> map2 = new HashMap<String, String>();
map2.put("科比", "瓦妮莎");
map2.put("TT", "卡戴珊");
//添加另一個Map
map.putAll(map2);
System.out.println(map);
//清空當前Map雙列集合
map2.clear();
System.out.println(map2.isEmpty());
//根據Key刪除對應的鍵值對
map.remove("TT");
System.out.println(map);
//當Key存在時,這個操作修改對應Value
map.put("王寶強", null);
System.out.println(map);
System.out.println(map.size());
System.out.println(map.containsKey("謝霆鋒"));
System.out.println(map.containsKey("薛之謙"));
System.out.println(map.containsValue("高磊鑫"));
System.out.println(map.containsValue("王菲"));
System.out.println(map.get("科比"));
System.out.println(map.get("TT"));
Set<String> set = map.keySet();
for (String string : set) {
System.out.println(string);
}
System.out.println("-------------------------------");
Collection<String> c = map.values();
for (String string : c) {
System.out.println(string);
}
}
}