Java基礎—集合框架(二)

集合框架(二)

一、TreeSet

    1.TreeSet集合特點
      可以對Set集合中的元素進行自然排序。往TreeSet裏面的對象,必須具有比較性,纔可以實現排序。

    2.TreeSet集合排序實現方式
      1)元素實現Comparable接口
        要獲得元素的比較性,需要進行存儲的對象可以實現Comparable接口,並複寫compareTo()方法,自定義比較方式;這是通過讓元素獲得比較性的方法,完成TreeSet排序功能。

      2)集合初始化Comparator
        當元素自身不具備比較性時,或者具備的比較性不是所需要的,這時可以讓集合調用具備比較功能的構造方法,將比較器對象作爲參數傳給TreeSet集合的構造方法;這是讓集合自身具備比較功能,完成TreeSet排序。

注意:
-TreeSet集合保證元素唯一新的依據是comparteTo()方法的返回值爲0;
-當元素和集合都具備比較功能時,以集合的比較器爲主

    3.TreeSet集合運用
      1)繼承Comparable接口的方法實現元素的可比性
        使用TreeSet集合存儲自定義對象。

示例代碼:

package com.heisejiuhuche.Collection;

import java.util.Iterator;
import java.util.TreeSet;

public class TreeSetDemo {
    public static void main(String[] args) {
        /*
         * 聲明TreeSet集合
         */
        TreeSet ts = new TreeSet();
        /*
         * 添加Student對象
         */
        ts.add(new Student("lisi01", 11));
        ts.add(new Student("lisi08", 23));
        ts.add(new Student("lisi07", 19));
        ts.add(new Student("lisi09", 19));
        ts.add(new Student("lisi02", 225));

        /* 取出所有元素並打印 */
        for(Iterator it = ts.iterator(); it.hasNext(); ) {
            Student stu = (Student)it.next();
            System.out.println(stu.getName() + "..." + stu.getAge());
        }
    }
}

class Student implements Comparable {
    private String name;
    private int age;

    Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    /*
     * 實現comparaTo接口,使Student對象具有可比性
     */
    public int compareTo(Object obj) {
        if(!(obj instanceof Student)) {
            throw new RuntimeException("Not Student...");
        }
        Student stu = (Student)obj;
        if(this.age > stu.age) {
            return 1;
        } else if(this.age == stu.age) {
            //在主要條件相同時,要比較次要條件,完成排序
            //避免不同名但同年齡的對象無法儲存的現象
            return this.name.compareTo(stu.name);
        }
        return -1;
    }
}

程序輸出結果:

lisi01...11
lisi07...19
lisi09...19
lisi08...23
lisi02...225

注意:
排序時,當主要條件相同時,一定要判斷次要條件

      2)調用TreeSet具備比較器構造方法

示例代碼:

package com.heisejiuhuche.Collection;

import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

public class TreeSetDemo2 {
    public static void main(String[] args) {
        /*
         * 將Comparator對象牀給TreeSet構造方法,讓TreeSet具備比較功能
         */
        TreeSet ts = new TreeSet(new MyComparator());

        ts.add(new Student("lisi01", 11));
        ts.add(new Student("lisi01", 9));
        ts.add(new Student("lisi08", 23));
        ts.add(new Student("lisi007", 19));
        ts.add(new Student("lisi09", 19));
        ts.add(new Student("lisi09", 19));
        ts.add(new Student("lisi02", 225));
        ts.add(new Student("lisi04", 225));

        for (Iterator it = ts.iterator(); it.hasNext();) {
            Student stu = (Student) it.next();
            System.out.println(stu.getName() + "..." + stu.getAge());
        }
    }
}

class Student {
    private String name;
    private int age;

    Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return this.name;
    }

    public int getAge() {
        return age;
    }
}

/*
 * 實現Comparator接口
 */
class MyComparator implements Comparator {
    public int compare(Object o1, Object o2) {
        Student stu1 = (Student) o1;
        Student stu2 = (Student) o2;

        int num = stu1.getName().compareTo(stu2.getName());

        /*
         * 如果姓名相同,比較年齡
         */
        if (num == 0) {
            return new Integer(stu1.getAge()).compareTo(new Integer(stu2.getAge()));
        }

        return num;
    }
}

程序輸出結果:

lisi007...19
lisi01...9
lisi01...11
lisi02...225
lisi04...225
lisi08...23
lisi09...19

    4.二叉樹數據結構
      1)定義

這裏寫圖片描述

二叉樹數據結構的原則是將小的元素依次放在左邊,大的元素依次放在右邊。在取數據時,二叉樹從最小的元素開始,依次往上取到最大值。這樣的數據結構,可以減少比較次數,提高運行效率。

    2)二叉樹運用
      使用二叉樹原理實現數據存儲和取出順序一致。

示例代碼:

public int compareTo(Object obj) {
    return 1;
}

要使存儲和取出的順序一致,只需在複寫的compareTo()方法中,return 1;即可;存儲與取出成倒序,只需return -1;即可

    5.TreeSet練習
      1)按照字符串的長度排序

示例代碼:

package com.heisejiuhuche.Collection;

import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

public class TreeSetTest {
    public static void main(String[] args) {
        TreeSet ts = new TreeSet(new StrLenComparator());

        ts.add("abc");
        ts.add("z");
        ts.add("addbc");
        ts.add("aaa");
        ts.add("abdfsc");
        ts.add("abcfsa3r");

        for(Iterator it = ts.iterator(); it.hasNext(); ) {
            String str = (String)it.next();
            System.out.println(str);
        }
    }
}

class StrLenComparator implements Comparator {
    public int compare(Object o1, Object o2) {
        String str1 = (String)o1;
        String str2 = (String)o2;

        /*
         * 比較字符串的長度
         */
        int num = new Integer(str1.length()).compareTo(new Integer(str2.length()));

        /*
         * 如果字符串長度相等,比較字符串自然順序
         */
        if(num == 0) {
            return str1.compareTo(str2);
        }

        return num;
    }
}

程序輸出結果:

z
aaa
abc
addbc
abdfsc
abcfsa3r

二、泛型

    1.概述

由一個小例子引出泛型:

package com.heisejiuhuche.Collection;

import java.util.ArrayList;
import java.util.Iterator;

public class GenericDemo {
    public static void main(String[] args) {
        ArrayList arr = new ArrayList();

        /*
         * 添加字符串
         */
        arr.add("asdf");
        arr.add("adfsdf");
        arr.add("asdfgsf");
        arr.add("as346ndfdf");
        /*
         * 添加整型數據4
         */
        arr.add(4);

        for(Iterator it = arr.iterator(); it.hasNext(); ) {
            String str = (String)it.next();
            /*
             * 輸出字符串的長度
             */
            System.out.println(str.length());
        }
    }
}

如果沒有泛型的支持,後添加的整型數據4,在運行時會出現運行時期類型轉換異常,int類型無法被轉換爲String類型,但是這個異常在編譯時期,沒有任何提示。爲了避免這樣的情況,需要程序在編譯時期就給出異常,以便開發者解決問題,Java引入了泛型機制。

JDK5.0版本之後的新特性,用於解決集合存儲的安全問題。

泛型格式(以儲存String類型數據爲例):

ArrayList<String> arrayList = new ArrayList();

聲明一個ArrayList容器,存儲String類型的元素。這樣,上述程序在編譯時期就會報出異常。
容器聲明加上了泛型,那麼迭代器的聲明也應加上泛型:

Iterator<String> it = arrayList.iterator();

    2.泛型的優點
      1)將運行時期出現的ClassCastException轉移到編譯時期,方便開發者解決問題,更安全;
      2)避免了迭代時期類型強制轉換的麻煩;
      3)泛型類的出現優化了程序設計

    3.泛型的應用
      當在聲明容器的時候指定了泛型,後期實現Comparator接口和Comparable接口的時候,可以指定Comparator接口的泛型;這樣做,避免了比較時的類型轉換。

Comparator接口泛型示例代碼:

/*
* 加上了泛型的Comparator接口
*/
class MyComparator implements Comparator<Student> {
    public int compare(Student o1, Student o2) {
        /*
         * 不需要做類型轉換
         */
        int num = o1.getName().compareTo(o2.getName());

        /*
         * 如果姓名相同,比較年齡
         */
        if (num == 0) {
            return new Integer(o1.getAge()).compareTo(new Integer(o2
                    .getAge()));
        }

        return num;
    }
}

Comparable接口泛型示例代碼:

/*
* 加上了泛型的Comparable接口
*/
class Student implements Comparable<Student> {
    private String name;
    private int age;

    Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public int compareTo(Student obj) {
        /*
        * 無須類型轉換
        */
        if(this.age > obj.age) {
            return 1;
        } else if(this.age == obj.age) {
            return this.name.compareTo(obj.name);
        }
        return -1;
    }
}

注意:
複寫equals()方法的時候,傳的參數必須是Object obj;因爲Object類沒有泛型

    4.泛型類
      1)定義
        泛型類是在一個類要操作對象,而對象類型又不明確時出現的。

      2)泛型類格式
        聲明泛型類,只需要在類名後加上<T>即可:class GenericClass<T>

      3)泛型類應用
        當一個類中要操作的引用數據類型不確定的時候,早期定義Object來完成擴展,現在定義泛型來完成擴展。例如,當一個工具類中要操作不同的對象WorkerStudent時,早期通過接收Object對象,然後通過類型轉換類完成操作;現在只需定義泛型,無需類型轉換。

早期實現:

package com.heisejiuhuche.Collection;

public class GenericClassDemo {
    public static void main(String[] args) {
        Utils u = new Utils();
        u.setObjcet(new Worker());
        /*
         * 需要類型轉換來完成操作
         */
        Worker w = (Worker)u.getObject();
    }
}

class Worker {}

class Student {}

class Utils {
    private Object obj;

    /*
     * 接收Object爲參數
     */
    public void setObjcet(Object obj) {
        this.obj = obj;
    }

    public Object getObject() {
        return obj;
    }
}

如果現在要操作的是Worker對象,而在setObject()方法中誤傳入了Student對象,編譯不會報錯,但是運行時會出現類型轉換異常。

泛型實現:

package com.heisejiuhuche.Collection;

public class GenericClassDemo {
    public static void main(String[] args) {
        Utils<Worker> u = new Utils<Worker>();
        u.setObjcet(new Worker());
        /*
         * 不需要類型轉換來完成操作
         */
        Worker w = u.getObject();
    }
}

class Worker {}

class Student1 {}

class Utils<T> {
    private T t;

    /*
     * 接收泛型T作爲參數
     */
    public void setObjcet(T t) {
        this.t = t;
    }

    public T getObject() {
        return t;
    }
}

如果現在要操作的是Worker對象,而在setObject()方法中誤傳入了Student對象,編譯無法通過;並且泛型實現無須進行類型轉換,既簡化了代碼,又提升了安全性。

    5.泛型方法
      1)定義
        泛型類在使用的過程中有其侷限性。例如一個泛型類被定義了只能操作String類型的對象,那麼該類中需要接收參數的方法,都只能操作String對象;此時如果給該類中的方法傳遞了非String類型數據,編譯就會出錯。爲了程序更加靈活,現在想實現一個類中不同的方法操作不同類型的對象,而要操作的對象不能確定,那麼就可以使用泛型方法。

      2)泛型方法格式
        要聲明泛型方法,只需在方法修飾符後面,返回值的前面加上<T>即可:
        private <T> void genericMethod() {}

      3)泛型方法應用
      定義一個類,類中的兩個輸出方法可以輸出指定類型的對象。

示例代碼:

package com.heisejiuhuche.Collection;

public class GenericMethodDemo {
    public static void main(String[] args) {
        GenericMethod gm = new GenericMethod();
        //打印字符串類型
        gm.show("haha");
        //打印Integer類型
        gm.show(new Integer(3));
        gm.print("heihei");
    }
}

class GenericMethod {
    /*
     * 泛型方法show()
     */
    public <T> void show(T t) {
        System.out.println("show: " + t);
    }

    /*
     * 泛型方法print()
     */
    public <T> void print(T t) {
        System.out.println("print: " + t);
    }
}

程序輸出結果:

show: haha
show: 3
print: heihei

高能:
-可以在泛型類中定義泛型方法,互不影響;
-靜態方法不可以訪問類上定義的泛型;如果靜態方法操作的引用數據類型不確定,可以將其定義爲泛型方法
public static <T> void method() {}

三、泛型限定

    1.“?”通配符
      “?”是一個通配符,也可以理解爲佔位符。在泛型的使用中,當無法確定接收對象是什麼類型的時候,先用“?”佔位。例如,有存放Student對象和Person對象的兩個ArrayList,定義一個方法,可以遍歷打印這兩個集合。

代碼示例:

package com.heisejiuhuche.Collection;

import java.util.ArrayList;
import java.util.Iterator;

public class GenericDemo3 {
    public static void main(String[] args) {
        ArrayList<Programmer> arr1 = new ArrayList<Programmer>();
        ArrayList<Manager> arr2 = new ArrayList<Manager>();

        arr1.add(new Programmer("aaa1"));
        arr1.add(new Programmer("aaa2"));
        arr1.add(new Programmer("aaa3"));

        arr2.add(new Manager("bbb1"));
        arr2.add(new Manager("bbb2"));
        arr2.add(new Manager("bbb3"));

        printArr(arr1);
        printArr(arr2);
    }

    /* 用通配符 ? 接收任意集合並打印 */
    private static void printArr(ArrayList<?> arrayList) {
        for(Iterator<?> it = arrayList.iterator(); it.hasNext(); ) {
            System.out.println(it.next().toString());
        }
    }
}

class Programmer {
    private String name;

    Programmer(String name) {
        this.name = name;
    }

    /*
     * 複寫toString()方法,輸出名字
     */
    public String toString() {
        return this.name;
    }
}

class Manager {
    private String name;

    Manager(String name) {
        this.name = name;
    }

    /*
     * 複寫toString()方法,輸出名字
     */
    public String toString() {
        return this.name;
    }
}

程序輸出結果:

aaa1
aaa2
aaa3
bbb1
bbb2
bbb3

    2.泛型的限定:
      1)? extends E:可以接收E類型或者E類型的子類型;限定了上限;

示例代碼:

package com.heisejiuhuche.Collection;

import java.util.ArrayList;
import java.util.Iterator;

public class GenericDemo4 {
    public static void main(String[] args) {
        ArrayList<SalesManager> arr1 = new ArrayList<SalesManager>();
        ArrayList<Salesman> arr2 = new ArrayList<Salesman>();

        arr1.add(new SalesManager("kengdie09"));
        arr1.add(new SalesManager("kengdie02"));
        arr1.add(new SalesManager("kengdie06"));
        arr2.add(new Salesman("kengdie04"));
        arr2.add(new Salesman("kengdie08"));
        arr2.add(new Salesman("kengdie10"));

        printArr(arr1);
        printArr(arr2);

    }

    /*
     * 泛型限定,只爲Salesman和Salesman的子類提供打印服務
     */
    private static void printArr(ArrayList<? extends Salesman> arr) {
        for(Iterator<? extends Salesman> it = arr.iterator(); it.hasNext(); ) {
            System.out.println(it.next().getName());
        }
    }
}

class Salesman {
    private String name;

    Salesman(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

class SalesManager extends Salesman {
    private String name;

    SalesManager(String name) {
        super(name);
    }
}

程序輸出結果:

kengdie09
kengdie02
kengdie06
kengdie04
kengdie08
kengdie10

上述程序,可以在打印方法中使用“?”通配符,但是如果需求是,要打印一個範圍內(SalesmanSalesman的子類)對象,那麼就使用“? extends E”泛型限定。

      2)? super E:可以接收E類型或者E類型的付類型;限定了下限;

示例代碼:

package com.heisejiuhuche.Collection;

import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

public class GenericDemo4 {
    public static void main(String[] args) {
        TreeSet<SalesManager> ts1 = new TreeSet<SalesManager>(new MyComp());
        TreeSet<SalesAssist> ts2 = new TreeSet<SalesAssist>(new MyComp());

        ts1.add(new SalesManager("kengdie09"));
        ts1.add(new SalesManager("kengdie02"));
        ts1.add(new SalesManager("kengdie06"));
        ts2.add(new SalesAssist("kengdie04"));
        ts2.add(new SalesAssist("kengdie08"));
        ts2.add(new SalesAssist("kengdie10"));

        printTs(ts1);
        printTs(ts2);
    }

    /*
     * 使用泛型限定打印Salesman及Salesman子類對象
     */
    private static void printTs(TreeSet<? extends Salesman> ts) {
        for(Iterator<? extends Salesman> it = ts.iterator(); it.hasNext(); ) {
            System.out.println(it.next().getName());
        }
    }

}

/*
 * 使用泛型限定,此比較器可以比較Salesman及其子類
 * 也就是說,在Comparator<>的尖括號內限定父類,那麼
 * 該比較器就可以比較父類,及其子類,無須定義多個比較器
 */
class MyComp implements Comparator<Salesman> {
    public int compare(Salesman s1, Salesman s2) {
        return s1.getName().compareTo(s2.getName());
    }
}

class Salesman {
    private String name;

    Salesman(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

class SalesAssist extends Salesman {
    private String name;

    SalesAssist(String name) {
        super(name);
    }
}

class SalesManager extends Salesman {
    private String name;

    SalesManager(String name) {
        super(name);
    }
}

程序輸出結果:

kengdie02
kengdie06
kengdie09
kengdie04
kengdie08
kengdie10

泛型限定,用於泛型擴展,讓程序更加靈活。

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