(十)Core Java 集合框架TreeSet与泛型 -02 (102)
1 ) . 集合框架(TreeSet)2 ) . 集合框架(TreeSet存储自定义对象)3 ) . 集合框架(二叉树)4 ) . 集合框架(实现Comparator方式排序)
5 ) . 集合框架(TreeSet练习)6 ) . 集合框架(泛型概述)7 ) . 集合框架(泛型使用)8 ) . 集合框架(泛型类)9 ). 集合框架(泛型方法)10 ).集合框架(静态方法泛型)11 ).集合框架(泛型接口)12 ).集合框架(泛型限定)13 ).集合框架(泛型限定 2)
一. 集合框架(TreeSet)
1 ) . TreeSet -->最大的特点就是依靠ASCII码表,对set中的元素进行自然排序2 ) . Demo :import java.util.*;/*set -->元素是无序(存入和取出的顺序不一定一致),元素不可重复-HashSet : 底层元素结构是哈希表保证元素唯一性的原理是: 判断元素的hashCode的值是否相同,若相同再通过equlas判断-treeSet : 可以对set集合中的元素进行排序*/class TreeSetDemo{//输出方法public static void sop(Object obj){System.out.println(obj);}//主方法public static void main(String args[]){TreeSet ts = new TreeSet();ts.add("day01");ts.add("aay02");ts.add("bay04");ts.add("cay03");for(Iterator it= ts.iterator(); it.hasNext();){sop(it.next());}//小结: 依靠ASCII码表,进行自然字母的自然排序}}
小结 :1. TreeSet-->自然排序
二. 集合框架(TreeSet存储自定义对象)
1 ) . 需求 : 将自定义对象存入TreeSet集合中,并且将对象以年龄的大小排序,若年龄相等,则用名字排序,并且保证无重复对象2 ) . Text :import java.util.*;/*set -->元素是无序(存入和取出的顺序不一定一致),元素不可重复-HashSet : 底层元素结构是哈希表保证元素唯一性的原理是: 判断元素的hashCode的值是否相同,若相同再通过equlas判断-treeSet : 可以对set集合中的元素进行排序*/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;}//复写底层的compareTo方法用来比较public int compareTo(Object obj){//判断实例化对象是否是student对象if(!(obj instanceof Student))throw new RuntimeException("不是学生对象");//向下转型Student s=(Student)obj;//判断当前年龄是否大于传入年龄,大于则往上排if(this.age>s.age)return 1;if(this.age==s.age){return this.name.compareTo(s.name);}return -1;}}class TreeSetText{//输出方法public static void sop(Object obj){System.out.println(obj);}//主方法public static void main(String args[]){TreeSet ts = new TreeSet();ts.add(new Student("summer",12));ts.add(new Student("winter",15));ts.add(new Student("spring",18));ts.add(new Student("sring",18));ts.add(new Student("autumn",19));ts.add(new Student("autumn",19));//迭代输出,结果是 按年龄排序,并且 当年龄相等按 名字排序的无重复的集合for(Iterator it= ts.iterator(); it.hasNext();){Student s =(Student)it.next();sop(s.getName()+"::"+s.getAge());}}}
小结 :1. 使用treeSet存储自定义对象,若要保证对象的唯一性,则需要复写底层的compareTo()方法
三. 集合框架(二叉树)
1 ) . 二叉树 : 存的时候通过比较的方式左边小,右边大,最终形成一个树状结构,我们称之为 二叉树 ,也叫 红黑树 ;取的时候 从左边依次取出,左边没值,再取右边的最左边的值,直到最后取完
1.1 优势 : 可减少比较次数,提高系统性能1.3 特点 : 当元素过多时,进行存时,二叉树会自动取折中值,以减少比较次数1.4 重点 : treeSet存取的顺序底层依赖的是compareTo()方法的判断,因此可根据此方法变更存取顺序1.5 底层依赖的数据结构是 --> 二叉树1.6 保证数据的唯一性的依据是 compareTo()方法 return 01.7 TreeSet排序的第一种方式是 :
[1] 让元素自身具备比较性,元素需实现Comparable接口,覆盖compareTo方法,这种方式称为元素的自然顺序,或者叫默认顺序
2 ) . Text :import java.util.*;/*set -->元素是无序(存入和取出的顺序不一定一致),元素不可重复-HashSet : 底层元素结构是哈希表保证元素唯一性的原理是: 判断元素的hashCode的值是否相同,若相同再通过equlas判断-treeSet : 可以对set集合中的元素进行排序底层依赖的数据结构是二叉树保证元素唯一性的依据是方法 compareTo方法return 0TreeSet排序的第一种方式是:让其元素自身具备比较性,实现comparable接口,覆盖compareTo方法,具体详情见下文*/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;}//复写底层的compareTo方法用来比较public int compareTo(Object obj){return 1 ; //1 , 0;//返回1是 怎么存入怎么取//返回-1是怎么存入倒着取//返回0是怎么存入都只能取第1个//这要涉及到treeSet中底层的数据结构也就是二叉树的存取方式/*//判断实例化对象是否是student对象if(!(obj instanceof Student))throw new RuntimeException("不是学生对象");//向下转型Student s=(Student)obj;//判断当前年龄是否大于传入年龄,大于则往上排if(this.age>s.age)return 1;if(this.age==s.age){return this.name.compareTo(s.name);}return -1;*/}}class TreeSetText1{//输出方法public static void sop(Object obj){System.out.println(obj);}//主方法public static void main(String args[]){TreeSet ts = new TreeSet();ts.add(new Student("summer",12));ts.add(new Student("winter",15));ts.add(new Student("spring",18));ts.add(new Student("sring",18));ts.add(new Student("autumn",19));//迭代输出,结果是怎么传入怎么传出,详情见上边compareTo方法的return处注释for(Iterator it= ts.iterator(); it.hasNext();){//向下转型Student s =(Student)it.next();sop(s.getName()+"::"+s.getAge());}}}3 ) . 手绘二叉树图解 :小结 :1. 二叉树的核心原则是,右下是大数,左下是小数2. 优势在于后期数量大时比较更少的数以提高效率
四. 集合框架(实现Comparator方式排序)
1 ) . TreeSet有两种自定义排序方式 -->TreeSet默认排序是自然排序, 两种自定义排序一种是让其元素具备比较性而排序,一种是让其容器具备比较性而排序 -->详情见下文2 ) . Text:import java.util.*;/*TreeSet的第一种排序方式是通过实现Comparable,重写compareTo方法完成 -->第一种是让元素自身具备比较性而后比较TreeSet的第二种排序方式是通过外部创建比较器以参数的形式传入TreeSet的方式完成 -->第二种是让容器具备比较性而后比较两者的优先级是, 先比较器为主,再 以元素的compareTo为主ps:当自身元素不具备比较性时,或者具备的比较性不是所需要的时,这时用第二种方式让容器自身具备比较性方式 : 定义比较器,将比较器对象以参数传递给TreeSet集合的构造函数*///自定义比较器 -->首先按名字排序,而后按年龄排序class MyCompare implements Comparator{public int compare(Object o1 ,Object o2){if(!(o1 instanceof Student && o2 instanceof Student))throw new RuntimeException("不是学生对象");Student s1 = (Student)o1;Student s2 = (Student)o2;//首先按年龄排序int num = s1.getName().compareTo(s2.getName());//这里比较等于0.是因为上边若想等则num=0,则就往下边比较if(num==0){ //通过将数值放入对象的方式然后调用compareTo进行比较return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));}return num;}}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;}//原始的排序//复写底层的compareTo方法用来比较public int compareTo(Object obj){//判断实例化对象是否是student对象if(!(obj instanceof Student))throw new RuntimeException("不是学生对象");//向下转型Student s=(Student)obj;//判断当前年龄是否大于传入年龄,大于则往上排if(this.age>s.age)return 1;if(this.age==s.age){return this.name.compareTo(s.name);}return -1;}}class TreeSetText2{//输出方法public static void sop(Object obj){System.out.println(obj);}//主方法public static void main(String args[]){TreeSet ts = new TreeSet();ts.add(new Student("summer",12));ts.add(new Student("winter",15));ts.add(new Student("spring",18));ts.add(new Student("sring",18));ts.add(new Student("autumn",19));//迭代输出,结果是怎么传入怎么传出,详情见上边compareTo方法的return处注释for(Iterator it= ts.iterator(); it.hasNext();){//向下转型Student s =(Student)it.next();sop(s.getName()+"::"+s.getAge());}}}小结 :1. 不同的集合如何确保值唯一?
[1]LinkedList底层依赖的是equals , 是链表结构[2]ArrayList底层依赖的是equals ,是数组结构[3]vector 底层依赖的是 equals ,是 数组结构[4]HashSet底层依赖的是HashCode和Equals 是哈希表结构[5]TreeSet底层依赖的是CompareTo ,是二叉树结构2.自定义比较器实现的接口是Comparator(让集合拥有比较性) , 元素本身的比较器实现的接口是 Comparable(让元素拥有比较性)
五. 集合框架(TreeSet练习)
1 ) . 需求 : 按照字符串的长度排序2 ) . Text :import java.util.*;/*需求:按照字符串的长度排序*/class SingleTreeSet implements Comparator{public int compare(Object o1, Object o2){//先将传进来的值向下转型String s1= (String)o1;String s2= (String)o2;//先依据长度判断排序 -->通过new对象方式判断,因为对象中有自带compareTo方法int num= new Integer(s1.length()).compareTo(new Integer(s2.length()));//若长度出现相等的,则第二重判断依据值排序if(num==0)return s1.compareTo(s2);//若第二重判断也相等,则返回num,意味着走第一回合判断,重复的值刷了下去return num;/* 这个是 第一轮判断的复杂方式if(o1.length()>o2.length())return 1;if(o1.length()==o2.length())return 0;return -1; */}}class TreeSetText3{//输出方法public static void sop(Object obj){System.out.println(obj);}//主方法public static void main(String args[]){TreeSet ts = new TreeSet(new SingleTreeSet());ts.add("abhgs");ts.add("as");ts.add("ahdddd");ts.add("asa");ts.add("aefg");//迭代输出for(Iterator it=ts.iterator(); it.hasNext();){sop(it.next());}}}3 ) . 切记在实际开发中,会用到多重判断,第一轮判断过后,再判断第二轮,以免丢失数据
小结 :1. 若类中只有一个方法,也可以用匿名类的方式去实现功能
六. 集合框架(泛型概述)
1 ) . 引入 :
1.1 问题 ; JDK版本1.5之前,集合中不能存入两种不同类型的数值,若存入则报错
[1] 理解 : 若集合像数组一样初始化时便声明了类型则不会出现类似的安全隐患
1.2 解决 : JDK版本1.5 之后,引入的泛型,解决了集合类型不统一的问题
2 ) . Demo :import java.util.*;/*泛型 : -->广泛的类型,用<>修饰,放在实例化对象类的后边,因为<>中可以放任何类型用来限制传进集合的值,因此成为泛型1.介绍 : JDK1.5版本以后出现新特性,用于解决安全问题,是一个类型安全机制2.好处:[1] 将运行时出现问题classCastException,转移到了编译时期,也就是从用户角度转移到了编程员角度方便编程员解决问题,让运行问题减少,安全[2] 避免了强制转换麻烦*///泛型示例class genericityDemo{//输出方法public static void sop(Object obj){System.out.println(obj);}//主方法public static void main(String args[]){ArrayList<String> al = new ArrayList<String>();al.add("day01");al.add("day02");al.add("day03");al.add("day04");for(Iterator<String> it = al.iterator();it.hasNext();){sop(it.next());}}}
小结 :1. {}用户程序层次结构, ()用于参数 ,<>用于集合泛型,[]用于数组类型
七. 集合框架(泛型使用)
1 ) . 简述 :
1.1 泛型的格式 : 通过<>来定义要操作的引用数据类型1.2 在使用java提供的对象时,什么时候写泛型呢?
[1] 通常在集合框架汇总很常见,在API中见到带<>的集合就是要定义泛型[2] 其实<>在集合中是用来接收类型的,当使用集合时,将集合中要存储的数据类型作为参数传递到<>中,再传递到add( )方法中来限制
2 ) . Text:import java.util.*;/*泛型 : -->广泛的类型,用<>修饰,放在实例化对象类的后边,因为<>中可以放任何类型用来限制传进集合的值,因此成为泛型1.介绍 : JDK1.5版本以后出现新特性,用于解决安全问题,是一个类型安全机制2.好处:[1] 将运行时出现问题classCastException,转移到了编译时期,也就是从用户角度转移到了编程员角度方便编程员解决问题,让运行问题减少,安全[2] 避免了强制转换麻烦3.常用 : 泛型在集合框架中很常见,但凡见到<> ,就是用来传入泛型的类型的,当使用集合时,我们会发现add()方法内的参数类型就是泛型所约束的*///泛型示例class genericityText{//输出方法public static void sop(Object obj){System.out.println(obj);}//主方法public static void main(String args[]){TreeSet<String> ts = new TreeSet<String>(new SortTreeSet());ts.add("asdf");ts.add("wertq");ts.add("zx");ts.add("uts");for(Iterator<String> it = ts.iterator();it.hasNext();){sop(it.next());}}}//排序 -->正序/* class SortTreeSet implements Comparator<String>{public int compare(String s1 ,String s2){//先依据长度判断int num = new Integer(s1.length()).compareTo(new Integer(s2.length()));//当长度相等时,再依据值判断if(num==0)return s1.compareTo(s2);return num;}} *///排序 -->倒序class SortTreeSet implements Comparator<String>{public int compare(String s1 ,String s2){//先依据长度判断int num = new Integer(s2.length()).compareTo(new Integer(s1.length()));//当长度相等时,再依据值判断if(num==0)return s2.compareTo(s1);return num;}// 小结 : 正序与倒序的区别,在于先拿什么去判断什么}小结 :1. 泛型避免了强转2. 正序变倒序最快捷的方式就是在程序中让比较的元素调换位置
八. 集合框架(泛型类)
1 ) . 通过泛型类来控制类中传入引用数据类型的唯一性2 ) . Text :import java.util.*;/*需求 : 实现一个泛型类,让其用一个类可以完成创建其它不同的类的操作什么时候定义泛型类?当类中要操作的引用数据类型不确定的时候,早期定义Object来完成扩展,现在定义泛型类来完成扩展*/class Student{}class Worker{}//早期方式 -->通过boject的方式class Util{private Object obj;public void setObject(Object obj){this.obj=obj;}public Object getObject(){return obj;}}//现在方式 -->通过泛型的方式class Utils<Genericity>{private Genericity gen;public void setObject(Genericity gen){this.gen=gen;}public Genericity getObject(){return gen;}}//泛型示例class genericityClassText{//主方法public static void main(String args[]){//老方式/* Util t =new Util();//采用一个工具类创建实体类对象t.setObject(new Worker());Worker w = (Worker)t.getObject();t.setObject(new Student());Student s = (Student)t.getObject(); *///新方式//采用一个工具类创建实体类对象/* Utils<Worker> t =new Utils<Worker>();t.setObject(new Worker());Worker w = t.getObject();Utils<Student> t =new Utils<Student>();t.setObject(new Student());Student s =(Student)t.getObject(); *///通过泛型类保证了传入对象的唯一性}//输出方法public static void sop(Object obj){System.out.println(obj);}}小结 :1. 泛型类也可让问题发生在编译时期,以为运行时期较少问题2. 泛型的出现避免了强转,还将问题从运行时期转移到了编译时期
九. 集合框架(泛型方法)
1 ) . 泛型方法一可以用来在不确定传入啥类型时用来定义,也可用来确定传入啥类型时用来限制,都可以2 ) . Demo :import java.util.*;/*需求 : 实现一个泛型方法,让其用一个方法可以传入不同的参数类型的值解说 : 泛型类定义的泛型,在整个类中有效,若被方法使用,则泛型类的对象明确要操作的具体类型后,所要操作的方法上的类型就已固定,因此泛型方法出现了泛型方法 : 为了让不同方法可以操作不同类型,而且在类型还不确定的情况下,可以将泛型定义在方法上*///泛型类的方式的方法使用 -->类的作用范围的整个类,全部方法一个类型 -->这种方式不可存入不同类型的值,由此出现了泛型方法,见下一个class Person<T>{public void print(T t){System.out.println(t);}public void show(T t){System.out.println(t);}}//泛型方法使用 -->泛型方法的作用范围在泛型方法上 ,传入的参数类型统一class PersonNew{public <S> void print(S s){System.out.println(s);}public <T> void show(T t){System.out.println(t);}}//泛型方法示例class genericityMethodText{//主方法public static void main(String args[]){//泛型类的方法演示/* Person<String> p = new Person<String>();p.print("day01");p.show("day02"); *///泛型方法演示PersonNew p = new PersonNew ();p.print("day01");p.show(123);}}
小结 :1. 泛型方法有一定局限性,当传入泛型类的类型是什么,那么泛型方法的参数类型就被确定了
十. 集合框架(静态方法泛型)
1 ) . 特殊之处 :
1.1 静态方法不可访问类上定义的泛型类型,若静态方法操作的应用类型不确定,可将泛型类型定义在方法上
2 ) . Text :import java.util.*;/*需求 : 实现一个泛型方法,让其用一个方法可以传入不同的参数类型的值总结 :[1]普通方法可以用在方法上定义的泛型类型,也可用在类上定义的类泛型[2]静态方法只能用在方法上定义的泛型类型*/class Person<T>{//此时方法上的类型用的是类上定义的public void print(T t){System.out.println(t);}//此时方法上的类型用的是方法上定义的public <S> void show(S s){System.out.println(s);}//此时静态方法上的类型只能用方法上定义的类型public static <Z> void show1(Z z){System.out.println(z);}}//泛型方法示例class genericityStaticMethodText{//主方法public static void main(String args[]){Person p = new Person<String>();p.print("普通方法......"+"Class01");p.show("泛型方法输出....."+"Method01");p.show1("静态泛型方法输出....."+"StaticMethod01");}public void sop(Object obj){System.out.println(obj);}}
小结 :1. 泛型在方法上定义在返回值类型的前边, 泛型在类上定义在类名的后边2. 静态先加载,后来才有实例化对象3. 静态泛型方法只能用方法上定义的类型,而不能运用类上定义的,因为静态先加载,而后实例化,因此会导致静态不可引用非静态,
十一. 集合框架(泛型接口)
1 ) . 泛型接口也可实现在不确定性的情况下实现功能扩展2 ) . Demo:import java.util.*;/*需求 : 将泛型定义在接口上*/interface Inter<T>{void show(T t);}/* 第一种方式 : 不确定接口类型.确定实现类类型class InterImple implements Inter<String>{public void show(String t){System.out.println("show:"+t);}}*///第二种方式 : 不确定接口类型.不确定实现类类型class InterImple1<T> implements Inter<T>{public void show(T t){System.out.println("show:"+t);}}//泛型接口示例class genericityInterfaceText{//主方法public static void main(String args[]){//第一种方式/* InterImple in =new InterImple();in.show("Day01"); *///第二种方式InterImple1<Integer> in1 =new InterImple1<Integer>();in1.show(123);}public void sop(Object obj){System.out.println(obj);}}小结 :1. 创建对象时的泛型-->泛型类-->普通泛型方法 --> 静态泛型方法-->泛型泛型 -->都可实现在不确定性的情况下的传参,另外也都可对接口/类/方法/自定义做限制
十二. 集合框架(泛型限定)
1 ) . 泛型限定 :
1.1 ? extedns E : 可以接收E类型或E的子类型, --> 指上限1.2 ? super E : 可以接收E类型或者E的父类型 -->指下限
2 ) . 泛型限定的使用场景 :
2.1 上限用于存在继承关系时,父类需要用到子类的时候 可使用到下限2.2 下限用于存在继承关系时,子类用到父类的时候,可使用到上限
3 ) . Demo:import java.util.*;/*需求 : 泛型限定格式 : ArrayList<? extends Person> arr? 通配符 ,可以理解为占位符泛型的限定:? extends E : 可以接收E类型或E的子类型 -->指上限? super E : 可以接收E类型或者E的父类型 -->指下限*///关于上限class Person{private String name;Person(String name){this.name=name;}public String getName(){return name;}}class Student extends Person{Student(String name){super(name);}}//泛型限定class genericityDemo{//关于下限class student implements Comparable<Person> <? supper E>{public int compareTO(Person p){this.getName();}}class Comp implements Comparator<Student>{public int compare(Student s1,Student s2) //这里填person 也就是student的父类,也可编译通过,实现,这是泛型的上限{return s1.getName().compareTO(s2.getName());}}TreeSet<Student> ts =new TreeSet(Student)(new Comp())ts.add(new Student("add1"));ts.add(new Student("add2"));ts.add(new Student("add3"));public static void sop(Object obj){System.out.println(obj);}//主方法public static void main(String args[]){ArrayList<Person> arr = new ArrayList<Person>();arr.add(new Person("summer"));arr.add(new Person("autumn"));ArrayList<Student> arr1 = new ArrayList<Student>();arr1.add(new Student("Lily"));arr1.add(new Student("mater"));// printColl2(arr1);printColl2(arr);/*ArrayList<String> arr =new ArrayList<String>();arr.add("day01");arr.add("day02");arr.add("day03");arr.add("day04");ArrayList<Integer> arr1 =new ArrayList<Integer>();arr1.add(12);arr1.add(13);arr1.add(14);arr1.add(15); *//* 方式一调用 :printColl(arr);printColl(arr1);*///方 式二调用 ;/* printColl2(arr);printColl2(arr1); */}//用来传入继承对象的 -->关于上限public static void printColl2(ArrayList<? extends Person> arr) // 或者 <? supper Student>{for(Iterator<? extends Person> it = arr.iterator(); it.hasNext();){sop(it.next().getName());}}//方式一 : 通过使用占位符的方式传入不同参数的值public static void printColl(ArrayList<?> arr){for(Iterator<?> it = arr.iterator(); it.hasNext();){sop(it.next());}}//方式二 : 通过使用静态方法的方式传入不同参数的值/* public static <T> void printColl2(ArrayList<T> arr){for(Iterator<T> it = arr.iterator(); it.hasNext();){sop(it.next());}} */}小结 :1. 泛型的上限与下限,用在继承上较多,用来限制只允许 存在继承关系的导入数据
十三. 集合框架(泛型限定-2)
1 ) . 关于泛型的高级用法 :
1.1 只想用单一类型 : 例子 : <String> ; 特定类型1.2 什么类型都可以传入 : 例子 : <?> ; 占位符 ,所有类型1.3 只想存在继承关系的类型的可以传入 : 例如 : 上限 --> <? extends E> 或者 下限 --> <? supper E>
2 ) . Demo :import java.util.*;/*需求 : 泛型限定格式 : ArrayList<? extends Person> arr? 通配符 ,可以理解为占位符泛型的限定:? extends E : 可以接收E类型或E的子类型 -->指上限? super E : 可以接收E类型或者E的父类型 -->指下限*///父类class Person{private String name;Person(String name){this.name=name;}public String getName(){return name;}}//子类class Student extends Person{Student(String name){super(name);}}//一个比较器,无论传入子类还是父类都可搞定,这就是上限,在<>内填入父类来表示上限class Comp implements Comparator<Person>{public int compare(Person s1,Person s2){return s1.getName().compareTo(s2.getName());}}//泛型限定class genericityDemo1{public static void sop(Object obj){System.out.println(obj);}//主方法public static void main(String args[]){//打印子类-->观察比较器.观察泛型即可发现不同点TreeSet<Student> ts = new TreeSet<Student>(new Comp());ts.add(new Student("summer"));ts.add(new Student("autumn"));ts.add(new Student("winter"));for(Iterator<Student> it =ts.iterator(); it.hasNext();){sop(it.next().getName());}//打印父类-->观察比较器.观察泛型即可发现不同点TreeSet<Person> ts1 = new TreeSet<Person>(new Comp());ts1.add(new Person("summerFatcher"));ts1.add(new Person("autumnFatcher"));ts1.add(new Person("winterFatcher"));for(Iterator<Person> it =ts1.iterator(); it.hasNext();){sop(it.next().getName());}}}3 ) . 泛型的限定 可用在TreeSet中排序,依靠自定义比较器时,可以经典的用到
小结 :1. 泛型也可限定treeSet中的比较器的比较元素2. 泛型限定是用于泛型扩展用的