Collection单列集合二

Collection集合2

泛型

泛型概述

1.什么是泛型

泛型就是参数化的数据类型,也可以称为类型参数.格式:<类型>.(ArrayList)

2.为什么要使用泛型

1.它提供了编译时类型安全检测机制,把运行时期的问题提前到了编译期间

2.避免了强制类型转换

public class MyCollectionDemo8 {
    public static void main(String[] args) {
        ArrayList arrayList = new ArrayList();
        arrayList.add(123);
        arrayList.add(12.3);
        arrayList.add("xj");
        arrayList.add('a');
        // 上面的代码我没有写泛型,所有类型的元素都可以添加进去
        // 我们在编译阶段无法发现是否有错误,直到我们运行的时候,才会出现错误,那时候再进行修改,或强制类型转换是一件很麻烦的事
    }
}

泛型的使用

1.泛型用在什么地方

  • 类后面 ------> 泛型类
  • 方法声明上------>泛型方法
  • 接口后面------>泛型接口

2.什么是泛型类

如果一个类的后面有,表示这是一个泛型类

3.如何使用泛型类

创建泛型类对象时,必须要给这个泛型确定具体的数据类型

自定义泛型类

1.如何定义泛型

1.<类型>:指定一种类型的格式

​ 尖括号里面可以任意书写,一般只写一个字母

​ 例如:,,,

2.<类型1,类型2,…>:指定多种类型的格式,多种类型之间用逗号隔开

​ 例如<E,T><K,V><Q,M>

泛型字母总结

E: 元素(Element),多用于java集合框架

K: 关键字,(Key)

N:数字,(Number)

T:类型 (Type)

V:值(Value)

2.如何定义泛型类

定义格式修饰符 class 类名<类型>{}

例如: public class Genneric<T>{},此处的T可以推荐使用常见的T,E,K,V等形式参数来表示泛型

  • 示例代码
public class MyCollectionDemo9 {
    public static void main(String[] args) {
        Box<String> box = new Box<>();
        box.setElement("爱生活,爱JAVA");
        String str = box.getElement();
        System.out.println(str);
    }
}
// 泛型类
class Box<E>{
    private <E> element;
    // 泛型方法
    public <E> getElement(){
        return this.element;
    }
    public void setElement(E element){
        this.element = element;
    }
    // 泛型接口
    interface Dx<E>{
		// 仅仅为了展示格式
    }
}

泛型方法的使用

1.什么是泛型方法

方法声明中有泛型的方法,就是一个泛型方法

2.泛型方法的格式

public <T> T[] toArray(T[] arr){}

T是数组的运行时的参数类型

arr 是要存储列表元素的数组;如果它不够大,为此目的分配T类型的新数组

public class MyETDemo1 {
    public static void main(String[] args) {
        ArrayList<String>arrayList = new ArrayList<>();
        arrayList.add("张三");
        arrayList.add("李四");
        arrayList.add("王五");
        arrayList.add("赵六");
        // 如果我们没写ArrayList的泛型
        // 我们在把这个集合转换成数组的时候,类型需要写成object类型
        Object[] objects = arrayList.toArray();
        System.out.println(Arrays.toString(objects));

        // 写了泛型之后
        // 代码解析:
        // 调用了ArrayList类下toArray方法的重载方法,形参是一个数组,
        // 这里我把创建数组和传入形参写在一起了,
        // 因为在这里这个形参和这个实参只使用这一次,所以我直接省略了创建过程
        String[] str = arrayList.toArray(new String[arrayList.size()]);
        // 因为传入的值是一个字符串数组,对于数组我们要么用for遍历,要么使用toString方法打印它
        // 但是这个数组用的是object下的toString方法,String中没有对toString方法进行重写,
        // 而object下的toString方法,返回的是地址
        // 所以我去数组的工具类Arrays中,寻找toString方法,发现他的返回值是String类型的值,而不是地址,所以选择使用它
        System.out.println(Arrays.toString(str));
    }
}

自定义泛型方法

1.定义格式

修饰符 <类型> 返回值类型 方法名(类型 变量名){}

例如:public <T> void show(){}

泛型接口

定义格式

修饰符 interface 接口名<类型>{}

例如:interface Gu<E>{}

类型通配符

泛型通配符的使用

1.类型通配符<?>

ArrayList<?>:表示元素类型未知的ArrayList,它的元素可以匹配任何的类型,但是并不能把元素添加到ArrayList中了,获取出来的也是父类类型

public class MyETDemo2 {
    public static void main(String[] args) {
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add("张三");
        arrayList.add("李四");
        arrayList.add("王五");
        arrayList.add("赵六");
        ArrayList<?> arrayList1 =arrayList;
        // arrayList1中不能再添加元素,因为它的类型是?
        // 注意这里用增强for遍历的数据类型是object
        for (Object o : arrayList1) {
            System.out.println(o);
        }
    }
}

类型通配符上限:<? extends 类型>

ArrayList<? extends Number>:它表示的类型是Number或者其子类类型

类型通配符下限:<? super 类型>

ArrayList<? super Number>:它表示的类型是Number或者其父类型

public class MyETDemo3 {
    public static void main(String[] args) {
       ArrayList<Number> arrayListTest1 = new ArrayList<>();
       ArrayList<Integer> arrayListTest2 = new ArrayList<>();
       ArrayList<String> arrayListTest3 = new ArrayList<>();
       ArrayList<Object> arrayListTest4 = new ArrayList<>();
       method1(arrayListTest1);
       method1(arrayListTest2);
      // method1(arrayListTest3); // String类不是Number的子类,所以报错
      // method1(arrayListTest4);// String类不是Number的子类,所以报错

        method2(arrayListTest1);
        // method2(arrayListTest2); // Iteger不是Number的父类
        // method2(arrayListTest3);// String不是Number的父类
        method2(arrayListTest4);

        // method3(arrayListTest1);  // Number不是String的子类
        // method3(arrayListTest2); // Inteage不是String的子类
        method3(arrayListTest3);
        // method3(arrayListTest4); // Object不是String的子类
        
        // 所有的类都是继承于object的
        method4(arrayListTest1);
        method4(arrayListTest2);
        method4(arrayListTest3);
        method4(arrayListTest4);
    }
    // 泛型的上限
    public static void method1(ArrayList<? extends Number> number){}
    // 泛型的下限
    public static void method2(ArrayList<? super Number > number){}
    public static void method3(ArrayList<? extends String> number){}
    public static void method4(ArrayList<? extends Object> number){}
}

小结

1.为什么要使用泛型

使用泛型的目的就是为了类型的安全,将类型的问题从运行时期,提到编译时进行处理

2.什么是泛型

它提供了编译时类型安全检测机制

3.怎么使用

在创建声明或创建时给泛型类型指定具体的类型

4.用在哪里

泛型类,泛型方法,泛型接口

5.泛型通配符

<?>,<? extends 类/接口> // 上限,<? super 类型/接口> //下限

Set集合

Set概述

1.Set集合的特点

  • 不可以存储重复元素
  • 存取顺序不一致
  • 没有索引

2.Set集合的使用

public class MySetTree {
    public static void main(String[] args) {
        TreeSet<String> treeSet = new TreeSet<>();
        treeSet.add("主宰");
        treeSet.add("默默");
        treeSet.add("三千");
        treeSet.add("三千");
        // set集合是没有索引的,所以不能通过索引获取元素
        // 增强for遍历
        for (String s : treeSet) {
            System.out.println(s);
        }
        // 迭代器遍历
        Iterator<String> iterator = treeSet.iterator();
        while(iterator.hasNext()){
            String result = iterator.next();
            System.out.println(result);
        }
    }
}

TreeSet集合

TreeSet集合元素的特点
  • 不易存储重复元素
  • 没有索引
  • 可以将元素按照规则进行排序
自然排序Comparable的使用
  • 案例需求
    • 存储学生对象并遍历,创建TreeSet集合,使用无参的构造方法
    • 要求:按照年龄从小到大排序
  • 示例代码
// 学生类
public class Student implements Comparable<Student> {
    private String name;
    private int age;

    public Student() {
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {

        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age >= 18 && age <= 25) {
            this.age = age;
        } else {
            throw new IndexOutOfBoundsException("年龄超出范围");
        }
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    // 通过实现Comparable接口,重写方法compareTo方法,来完成排序                
    @Override
    public int compareTo(Student o) {
        // o.age代表存入的值,返回结果为正,存入右边,为负存入左边/this.age表示现在要存入的
        int result =this.age-o.age;
        result = result == 0?this.name.compareTo(o.getName()):result;
        return result;
    }
}
public class MyTreeSetDemo3 {
    public static void main(String[] args) {
        TreeSet<Student> treeSet = new TreeSet<>();
        Student student1 = new Student("张三",21);
        Student student2 = new Student("张1",23);
        Student student3 = new Student("张2",27);
        Student student4 = new Student("张4",22);
        treeSet.add(student1);
        treeSet.add(student2);
        treeSet.add(student3);
        treeSet.add(student4);

        treeSet.forEach(s->{
            System.out.println(s);
        });
    }
}

比较器排序Comparator的使用

  • 案例需求
    • 存储老师对象并遍历,创建TreeSet集合使用带参构造方法
    • 要求:按照年龄从小到大排序,年龄相同时,按照名字的字母排序

提示

int compare(T O1,T O2)

比较两个参数,返回的是负整数,零或正整数.

// teacher类
public class Teacher {
    private String name;
    private int age;

    public Teacher() {
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
// 测试类
public class TeacherTreeSet {
    public static void main(String[] args) {
        Teacher teacher = new Teacher("wang",24);
        Teacher teacher1 = new Teacher("li",31);
        Teacher teacher2 = new Teacher("zhang",24);

        // 重写方法,来规定排序的规则
        /*TreeSet<Teacher> teacherTreeSet = new TreeSet<>(new Comparator<Teacher>() {
            @Override
            public int compare(Teacher o1, Teacher o2) {
                int result = o1.getAge()-o2.getAge();
                result = result==0?o1.getName().compareTo(o2.getName()):result;
                return result;
            }
        });*/
        // 采用lambda方法设置排序规则
        TreeSet<Teacher> teacherTreeSet = new TreeSet<>(((o1, o2) -> {
            int result = o1.getAge()-o2.getAge();
            result = result==0?o1.getName().compareTo(o2.getName()):result;
            return result;
        }));

        teacherTreeSet.add(teacher);
        teacherTreeSet.add(teacher1);
        teacherTreeSet.add(teacher2);
        for (Teacher teacher3 : teacherTreeSet) {
            System.out.println(teacher3);
        }
    }
}

两种比较方式总结

1.不同点

用到的接口不同

自然排序: 自定义类实现Comparable接口,重写compareTo方法,根据返回值进行排序

比较器排序:创建TreeSet对象的时候,传递Comparator的实现类对象,重写compare方法,根据返回值进行排序

使用场景不同

自然排序能满足大部分情况

存储没有修改权限的类时,可以使用(例如:改变String类型比较规则,包装类)比较器排序

2.相同点

返回值的规则

如果返回值为负,表示当前值是较小值,存左边

如果返回值是0,表示当前元素跟存入的元素重复了,不存

如果返回值是正,表示当前值是较大值,存右边

3.任意被Object类实现的方法都不是抽象方法,因此可以使用匿名内部类,也可以使用Lambda表达式

{一个非常有特点的例子,就是Comparator接口中有equals和compare两个方法,却能使用Lambda表达式,原因是因为equals方法被object类实现了}当然,源码中也有这样一句话

Note that it is always safe not to override
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值