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

泛型限定,用于泛型扩展,让程序更加灵活。