Java學習筆記——集合與泛型-2018-07-18

集合:就像是一種容器。用於存儲、獲取、操作對象的容器。

1. 數組的弊端

     ①數組的長度不可變    

     ②數組沒有提供可以查看有效元素個數的方法

 

2. 集合的特點

     ①集合的長度是可變的

     ②集合可以存儲任意類型的對象

     ③集合只能存儲對象

 

3. 集合框架

     java.util.Collection : 集合層次的根接口

        |--- java.util.List: 有序的可以重複的。

            |--- ArrayList: 採用數組結構存儲元素。 查詢操作多時選擇

            |--- LinkedList: 採用鏈表結構存儲元素。 增刪操作多時選擇

            |--- Vector:

        |--- java.util.Set: 無序的不允許重複

            |--- HashSet : 是 Set 接口的典型實現類。

                判斷元素是否存在的依據是:比較 hashCode 值,若 hashCode 存在,通過 equals() 比較內容

                若 hashCode 值不存在,則直接存儲

 

            注意:重寫 hashCode 和 equals 二者需要保持一致!

            |--- LinkedHashSet: 相較於 HashSet 多了鏈表維護元素的順序遍歷效率於 HashSet , 增刪效率於 HashSet

            |--- TreeSet : 擁有自己排序方式

                |-- 自然排序(Comparable):

                    ①需要添加 TreeSet 集合中對象的類實現  Comparable 接口

                    ②實現 compareTo(Object o) 方法

                |-- 定製排序(Comparator)

                    ①創建一個類實現 Comparator 接口

                    ②實現 compare(Object o1, Object o2) 方法

                    ③將該實現類的實例作爲參數傳遞給 TreeSet 的構造器 

 

4. 集合的遍歷

① 增強 for 循環

// for(被遍歷集合中元素的數據類型  變量名 :  被遍歷的集合){     }   
ArrayList al = new ArrayList();
al.add("AA");
al.add("BB");

for(Object obj : al){
 System.out.println(obj);
}

② 使用 Iterator 迭代器

//1)獲取當前集合的迭代器
Iterator it = al.iterator();

while(it.hasNext()){
    Object obj = it.next();
    System.out.println(obj);
}
/* 錯誤的做法:通常一個 hasNext() 配合一個 next() 使用
Iterator it = al.iterator();

while(it.hasNext()){
    Object obj = it.next();
    System.out.println(it.next());
} */

③ ListIterator : 列表迭代器,是List特有的迭代器(瞭解)

ListIterator li = al.listIterator();

while(li.hasNext()){
    Object obj = li.next();
    if(obj.equals("BB")){
        li.set("BBBBBBBBBBb");
    }
}

Map系列集合

java.util.Map : 用於存儲成對對象的集合。具有 key(鍵)-value(值)對映射關係的集合。一個 key 對應着一個 value。 key不允許重複的。

    |--- HashMap:是 Map接口的典型實現類

        |--- LinkedHashMap : 相較於 HashMap 多了鏈表維護元素的順序

    |--- Hashtable: 是線程安全的,因此效率低

        |--- Properties : 用於操作屬性文件

    |--- TreeMap : 根據 key 擁有自己的排序方式

        |-- 自然排序(Comparable):

        |-- 定製排序(Comparator):

 

    //使用 Properties 操作屬性文件

@Test
public void test1() throws FileNotFoundException, IOException{
//1. 創建 Properties 對象
Properties props = new Properties();

//2. 通過 load() 方法加載屬性文件
props.load(new FileInputStream("hello.properties"));

//3. 通過  getProperty() 方法根據key獲取對應的value
String userName = props.getProperty("username");
String password = props.getProperty("password");

System.out.println(userName);
System.out.println(password);
}

1. Map的常用方法:

添加、刪除操作:

Object put(Object key,Object value)
Object remove(Object key)
void putAll(Map t)
void clear()

元素查詢的操作:

Object get(Object key)
boolean containsKey(Object key)
boolean containsValue(Object value)
int size()
boolean isEmpty()
boolean equals(Object obj)

2. Map 的遍歷:

Map map = new HashMap();
map.put("AA", 123);
map.put("BB", 456);

keySet();

//遍歷Map的方式一: 獲取 Map 中所有的 key   
Set set = map.keySet();

values();

//遍歷Map的方式二:獲取 Map中所有的 value
Collection coll = map.values();

// 遍歷Map的方式三: 獲取Map中所有的 Entry (是Map 的一個內部類,一個Entry對應着Map中的一個key一個value)

// entrySet法
Set entrySet = map.entrySet();
for(Object obj : entrySet){
    Entry entry = (Entry)obj;
    Object key = entry.getKey();
    Object value = entry.getValue();
}

// Iterator 法
Iterator it = entrySet.iterator();
while(it.hasNext()){
    Entry entry = (Entry)it.next();
    Object key = entry.getKey();
    Object value = entry.getValue();
}

爲什麼使用泛型

若集合中不使用泛型,意味着集合中可以添加任意類型的對象。若需要具體到某一個類型時,需要強制類型轉換可能引發 ClassCastException

泛型類泛型方法同時具備可重用性類型安全效率,這是非泛型類和非泛型方法無法具備的。

泛型: 在 Java 中以 "<>" 的形式呈現,<> 中寫引用數據類型

          用於限制集合中存放元素的類型

1. 在集合中應用泛型

2. 自定義泛型接口方法

class DAO<T>{     // T:Type E:Element K:Key V:Value
    private List<T> list = new ArrayList<T>();
    public void add(T t){
        list.add(t);   
    }

    public T get(int id){
        return list.get(id);
    }

    //自定義泛型方法
    // <E>類似泛型類開頭的<T>,表示指定這個類或者方法的類型    E[]表示返回值類型
    public <E> E[] srot(E[] e){
         
    }
    // 上述自定義泛型方法可寫成如下
    public T[] sort(T[] t){
         
    }
}

3. 通配符    ?

雖然 Person 是  Student 的父類,但是 List<Person> 就不是  List<Student> 的父類

//需求:   

//public void show(List<Student> list){}
//public void show1(List<Man> list){}   

public void show(List<? extends Person> list){}

List<?> : 可以接收任意帶泛型類型的集合
List<? extends Person>  : 可以接收 Person 本類類型及 Person子類類型帶泛型類型的集合
List<? super Person> :  可以接收 Person 本類類型及 Person父類類型帶泛型類型的集合   

通配符相關

主要包括無界通配符,通配符上界,通配符下界

通配符

通配符不是用來定義泛型的,而是用來代表任何一種類型實參!自己一直困在一種誤區中,就是以爲通配符是可以在定義泛型類、泛型方法或者泛型接口時使用的。如class Generic<?>{}這種語法是錯誤的。

泛型中沒有邏輯上的父子關係,如List<Number>並不是List<Integer>的父類。兩者擦除之後都是List,所以形如

void m(List<Number> numbers) { }

void m(List<String> strings) { }

如果想讓List<Number>邏輯上成爲List<Integer>的父類(實際的應用場景中就是向方法傳入的實際參數是方法聲明的參數的子類),則可以使用泛型的通配符”?”,它表示任意一種不確定的類型。如:

/**
* 可以傳入泛型爲任何類型的List實現類
* 但是因爲list並不知道你傳入的具體會是什麼類型,所以只可以使用每個元素從Object繼承的方法。
*/
static void genericWildcard(List<?> list){
    list.forEach(java.lang.Object::toString);
}
public static void main(String[] args) {

    List<Number> list = new ArrayList<>();
    genericWildcard(list);
    List<Integer> list1 = new ArrayList<>();
    genericWildcard(list1);

    //還可以傳入泛型爲String類型
    List<String> list2 = new ArrayList<>();
    genericWildcard(list2);
}

通配符邊界

有時候希望傳入的類類型有一個指定的範圍,從而可以進行一些允許的操作,這時候就是通配符邊界登場的時候了。泛型的邊界分兩種:上界和下界。 

對於通配符的部分,可以從三方面理解(以List爲例,方便理解):

含義

查詢

與泛型有關的操作

先看三個很簡單的類:

public abstract class Animal {
    public abstract void animalMethod();
    @Override
    public String toString() {
        return "Animal";
    }
}

public class Dog extends Animal {
    @Override
    public void animalMethod() {
        System.out.println("DOG method");
    }
    @Override public String toString() {
        return "Dog";
    }
}

public class Fish extends Animal {
    @Override
    public void animalMethod() {
        System.out.println("Fish method");
    }
    @Override 
    public String toString() {
        return "Fish";
    }
}

Animal是一個抽象類,Fish,Dog是其實現。

通配符上界

extends關鍵字聲明瞭類型的上界,表示參數化的類型可能是所指定的類型,或者是此類型的子類。

//list中所有的元素都是Animal或者是其子類
List<? extends Animal> list;

/**
 * List<? extends Animal> animals;
 * 含義:表示animals集合中所有的元素都是animal或者是animal的子類。
 * 查詢:如方法體中所寫,因爲animals中所有元素都是其子類,所以可以調用其子類從animal中實現的方法。
 * 增加:見方法體
 */
public static void genericUpperWildcard(List<? extends Animal> animals){
	animals.forEach(Animal::animalMethod);

	/*
	 * 下面兩行都是編譯錯誤,如果瞭解前面文章中提到的擦除,則很好理解。
	 * 編譯後的方法簽名參數List所用的泛型類會被轉換成Animal。假如此時向List中添加的是Dog類,那麼當我們使用Dog類特有的方法時
	 * 肯定是不存在的。所以出於類型安全問題,不允許向含有通配符下界的泛型類中添加元素——null除外,但是添加null沒有任何意義。
	 */
//        animals.add(new Dog());
//        animals.add(new Animal());
}

public static void main(String[] args) {
	List<Dog> dogs = new ArrayList<>();
	dogs.add(new Dog());
	genericUpperWildcard(dogs);
}

通配符的下界

super關鍵字聲明瞭類型的下界,表示參數化的類型可能是所指定的類型,或者是此類型的父類型,直至Object。

/**
 * List<? super Animal> list;
 * 含義:表示list中所有的元素都是Animal類或者是其父類
 * 查詢:因爲返回的結果並不能保證是那個當初添加的Animal類或則其父類,返回的查詢結果的類型只能是Object.
 * 增加:可以向list中添加任何Animal實例或者Animal子類,因爲list在編譯之後泛型先被擦除然後轉換爲Animal類,
 * 此時向裏添加的任何類型都是Animal類,所以調用Animal中的任何非私有方法都是允許的。
 *
 */

public static void genericLowerWildcard(List<? super Animal> list) {
	list.add(new Animal());  //抽象類無法實例化,這裏有問題
	list.add(new Dog());

//        Animal animal1 = list.get(0); 編譯出錯,
	Object animal = list.get(0);
}

泛型的通配符,尤其是邊界,比較難理解,但是明白其原理之後很多問題就迎刃而解了。瞭解泛型的擦除,以及編譯之後泛型轉換成的普通Java代碼是什麼樣的很重要。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章