JavaDay18 泛型


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
}

兩種【遵從】帶有自定義泛型的接口方式:

  1. 更加自由,需要使用的泛型類型,在創建對象時確定,類似ArrayList
  2. 適合原本這個類就沒有使用泛型的情況,例如:一個類遵從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);
		}
	
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章