1-16 Collection集合之Set集合和泛型

作业回顾

1.“千锋学员入学资格检测系统”。–>自定义异常

约定:
合格的学员应该满足:
年龄:18~40
学历:大专,本科,硕士,博士

异常一共分为两种 Error【错误】   Exception【异常】  --》 父类  Throwable
Error 不归程序猿处理
Exception 又分为两类
1.运行时异常 RuntimeException(只当程序运行起来才会出现)
2.编译时异常 Exception(在编译代码就会出现异常提示)
PS:RuntimeException 继承于 Exception
 
处理异常方式
1. 捕获处理 --try-catch  【多写在方体内部】  --finally【永远都执行(释放资源)】
2. 抛出异常 --throws 【在方法声明之后(形参列表的后面)】 
           --throw 主要作用:就是提示信息(异常) 在方法体的内部 相当于是一个return作用   
    
系统异常无法满足编程需求,可以自定义异常
如果自定义异常继承于RuntimeException(运行时),在编译阶段不会出现问题,只会在运行时才会出现异常提示
如果自定义异常继承于Exception(编译时),在编写代码阶段就会提示出异常,需要及时处理
PS:只要继承完毕仿照父类生成自己的构造方法即可
 package com.qfedu.HomeWork;
//自定义学生类异常
public class StudentException  extends  RuntimeException {
    // 只要仿照父类生成自己的构造方法即可

    public StudentException() {
    }

    public StudentException(String message) {
        super(message);
    }

    public StudentException(String message, Throwable cause) {
        super(message, cause);
    }

    public StudentException(Throwable cause) {
        super(cause);
    }

    public StudentException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}
 package com.qfedu.HomeWork;

public class Student {
     private int age;
     private String education;
    public Student() {
    }
    public Student(int age, String education) {
        this.age = age;
        this.education = education;
    }

    public int getAge() {
        return age;
    }

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

    public String getEducation() {
        return education;
    }

    public void setEducation(String education) {
        this.education = education;
    }

    @Override
    public String toString() {
        return "学生的信息:"+age +" "+ education;
    }
}

package com.qfedu.HomeWork;



import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;

public class Test {
    public static void main(String[] args) {
        System.out.println("--------------------------千锋学员入学资格--------------------------");
        Scanner input = new Scanner(System.in);
        System.out.println("请输入年龄:");
        //hasNextXXX 方法仅限JavaSE阶段 控台阶段
            int age = input.nextInt();
        System.out.println("请输入学历");
        String education = input.next();
        //创建学生对象
        Student stu = new Student(age,education);
        //快捷获取代码块 ctrl + atl + t
        try {
            boolean res = checkStudentAducationAndAge(stu);
            if (res){
                System.out.println("恭喜入学成功!!!");
            }
        } catch (StudentException e) {
            System.out.println(e.getMessage());
        }


    }

    /**
     * 检查入学学生的信息
     * @param stu  学生
     * @return  true 满足入学条件  false 着直接抛出异常
     */
    public static boolean checkStudentAducationAndAge(Student stu) {
        ArrayList list = new ArrayList();
        Collections.addAll(list,"大专","本科","硕士","博士");
        if(stu.getAge() >= 18 && stu.getAge()<=40){
            for(int i = 0; i<list.size();i++){
                if(stu.getEducation().equals(list.get(i))){
                    return  true;
                }
            }
        }
        throw new  StudentException("不符合入学条件");
    }
}
  

List集合的特点允许存储重复数据,并且存数据有序【有用下标序号】

List集合在API中体现是一个【接口】,所以先学习实现类

ArrayList【数组实现(查询和修改快,增加和删除慢 )】

LinkedList【双向链表(辅助实现,数组,队列和栈)–》(增加和删除块,查询和修改慢)】

List集合

1.List集合是允许出现重复

例如:
    ArrayList list = new ArrayList();
    list.add(1);
    list.add(1);
    System.out.println(list); ---[1,1] ---》存了两个1,证明允许出现重复

2.记录添加先后顺序

例如:
    无论是ArrayListLinkedList,都提供了类似于数组下标的操作方法【get】,所以可以通过下标值来完成对数据的操作
     ArrayList list = new ArrayList();
    list.add(1);
    list.add(1);
    System.out.println(list.get(0)); // 返回对应下标位置值 

List是ArrayList和LinkedList的【“父类”】,即当前的两个类都实现了List接口,所以就允许面向接口编程【允许接口多态】

List集合对象创建
    语法:  集合接口类型 对象名  = new 实现接口实现类();
    		List list = new ArrayList();List list = new LinkedList();
PS:  
   1. 此时任何一对象进行对List的创建,都会进行对象向上转型,此时就会触发父类的方法,子类的方法无法调用
       
   2.List接口是Collection接口子类,而Collection是除Map集合之外所有集合的超级父类
     所以Collection集合允许 List集合的子类为期创建对象
     创建Collection集合对象
       		Collection collection = new ArrayList();Collection collection = new LinkedList();
  此时任何一对象进行对Collection的创建,都会进行对象向上转型,此时就会触发父类的方法,子类的方法无法调用
      
    3. 上面这些创建都不涉及到泛型,所以如果需要使用泛型创建需要使用下标这个语法
      接口类型<集合中存储元素的数据类型>  对象名 = new 实现集合接口的实现类<>();

List集合中提供的方法完全参考ArrayLis的实现方式即可

Vector

PS:ArrayList类是Java集合框架开发出来之后替代Vector类使用,二者底层实现原理都是一样【基于数据实现】

ArrayList里面的API完全兼容Vector

Vector出现比ArrayList要早,出现是提供了一个线程安全的操作集合,但是效率低

Java为了提供对集合的操作效率,所以提供ArrayList集合替代Vector,ArrayList线程不安全,但是效率高

在线程安全【单线程】的情况下,建议选择ArrayList来进行数据操作

在线程不安全【多线线程】的情况下,建议选择ArrayList来进行数据操作

PS:Vector已经过时了,基本不会再使用,你能碰到出现Vector只能证明这个项目最少存在10年以上

JDK1.5之后对这些线程不安全的集合提供统一的处理方式,提供一个工具类Collections

Collections工具类中提供一个方法,这个方法可以将线程不安全集合转变为线程安全的集合

ArrayList list = Collections.synchronizedList(参数是要转换的List集合对象);

栈(Stack集合) 和 队列(Queue集合)

队列集合

队列是一种特殊的线性列表,特殊在处于开始位置可以进行操作【队列头】,而在结束位置也可以继续进行操作【队列尾】
PS:线性列表就是一个"直线性",即从前到后的一个数据存储方式    
"单向队列"Queue: 先进先出(FIFO),只能在队尾插入数据,只能从队头删除数据
"双向对列"Deque】:数据可以从队头和队尾进行数据插入和删除
API了解即可  
PS:队列会在线程中介绍一个特殊队列,线程安全的队列    

栈集合

栈又名 "堆栈", 他是一种运算受限的线表,只允许后进先出(LIFO) 或  先进后出(FILO)
栈集合操作的时候,它向栈中插入一个新元素,它会被称为"进栈【入栈或压栈】",它会把新元素放到所有元素的最上面,使之成为所有元素"栈顶"
从一个栈中删除元素又称为"出栈【退栈或弹栈】",它是把栈顶位置的数据进行删除,后续元素会成为新的栈顶
API了解即可       

2.已知有一个工人Worker类 属性:姓名 年龄 工资, 行为 void work()
a.在集合中添加三个工人对象,信息如下:
zhang3 18 3000
li4 25 3500
wang5 22 3200
b. 在li4之前插入一个工人 信息为: zhao6 24 3300
c. 删除wang5的信息
d. 利用for循环遍历,打印几个中所有工人的信息
e. 利用迭代器遍历,对集合中的所有工人调用work方法

package com.qfedu.HomeWork;

public class Worker {
     private String name;
     private int age;
     private int money;
    public Worker() {
    }

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

    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;
    }

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }

    public void doWork(){
        System.out.println(name+"在工作");
    }

    @Override
    public String toString() {
        return "Worker{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", money=" + money +
                '}';
    }
}
package com.qfedu.HomeWork;

import java.util.*;


/**
 * 	a.在集合中添加三个工人对象,信息如下:
 * 		zhang3  18   3000
 * 		li4      25  3500
 * 		wang5    22  3200
 * 	b. 在li4之前插入一个工人 信息为: zhao6  24  3300
 * 	c. 删除wang5的信息
 * 	d. 利用for循环遍历,打印几个中所有工人的信息
 * 	e. 利用迭代器遍历,对集合中的所有工人调用work方法
 */
public class WorkerTest {
    public static void main(String[] args) {
        //1.向集合中添加指定对象
        List list = new ArrayList();
        Collections.addAll(list
                ,new Worker("zhang3",18,3000)
                ,new Worker("li4",25,3500)
                ,new Worker("wang5",22,3200));
        System.out.println("在没有进行任何修改之前的集合:"+list);
        insertWorkerList(new Worker("zhao6",24,3000),list);
        System.out.println("插入zhao6对象之后的集合:"+list);
        System.out.println("--------------------------------------------------------------------");
        removeWorkerList("wang5",list);
        System.out.println("删除wang5对象之后的集合:"+list);
        System.out.println("----------------------------------------------------------------------");
        showInfosWorker(list);
    }

    /**
     * 打印工人信息,并调用方法
     * @param list
     */
    public static void showInfosWorker(List list) {
        //1.先获取到迭代器对象
        Iterator iterator = list.iterator();
        while (iterator.hasNext()){ //判断是否还有下一个元素
            Worker w = ((Worker)iterator.next());// 取出迭代器中的对象
            System.out.println(w);
            w.doWork();
        }
    }

    /**
     * c. 删除wang5的信息
     * @param name 要删除对象的名称
     * @param list  要查找的集合
     */

    public static void removeWorkerList(String name, List list) {
        //使用增强for循环对集合元素进行删除凑操作时会出现 并发修改异常【ConcurrentModificationException】
        //原因在于增强for循环在底层的实现方式是以 迭代器的形式实现, 所以在增强for循环中使用集合删除方法是不可以操作删除对象的
//            for(Object obj : list){
//                //obj 相当于是获取到每一个worker对象
//                Worker other = (Worker)obj;
//                if(other.getName().equals(name)){
//                    list.remove(other);//删除集合中对象
//                }
//            }
            for(int i = 0; i<list.size();i++){
                //obj 相当于是获取到每一个worker对象
                Worker other = ((Worker)list.get(i));
                if(other.getName().equals(name)){
                    list.remove(other);//删除集合中对象
                }
            }

    }

    /**
     *  在li4之前插入一个工人 信息为: zhao6  24  3300
     * @param worker  zhao6对象
     * @param list    存工人对象的集合
     */
    public static void insertWorkerList(Worker worker, List list) {
        for (int i = 0; i <list.size() ; i++) {
            /*
                问题:集合在没有使用泛型之前,任何数据都可以存到集合中
                     原因在于集合中默认使用数据类型是Object即所有类的父类,除此之外集合只能存引用类型【这些引用类型是Object的子类】
                     以上两个条件都满足,所有集合中才可以存储任何数据类型
                     好处: 什么数据都能存储
                     坏处:  因为集合中默认数据类型是Object,所以所有存储的数据都会执行【对象向上转型】
                            ---》此时只能调用父类属性和方法,子类特殊属性和方法就无法调用
                            ---》所以就面临一个问题,频繁的【对象向下转型】 ---》即可以调用父类属性和方法也可以调用子类的属性和方法
             */
            Worker ohter = ((Worker)list.get(i));
            if(ohter.getName().equals("li4")){
                //li4的位置就找到了
                list.add(i,worker);
                return;
            }

        }
    }
}

1)封装一个新闻类,包含标题和内容属性,提供get、set方法,重写toString方法,打印对象时只打印标题;
2)只提供一个带参数的构造器,实例化对象时,只初始化标题;并且实例化两个对象:
新闻一:中国多地遭雾霾笼罩空气质量再成热议话题
新闻二:春节临近北京“卖房热”
3)将新闻对象添加到ArrayList集合中,并且使用ListIterator倒序遍历;
4)在遍历集合过程中,对新闻标题进行处理,超过15字的只保留前14个,然后在后边加“…”
5)在控制台打印遍历出经过处理的新闻标题

package com.qfedu.HomeWork;

/**
 * 新闻类
 */
public class News {
    private String title;
    private String content;
    public News(String title){
        this.title = title;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    @Override
    public String toString() {
        return "标题:"+title;
    }
}

package com.qfedu.HomeWork;

import jdk.nashorn.internal.ir.CallNode;

import java.util.ArrayList;
import java.util.Collections;
import java.util.ListIterator;

public class NewsTest {
    public static void main(String[] args) {
        /**
         * 2)只提供一个带参数的构造器,实例化对象时,只初始化标题;并且实例化两个对象:
         * 新闻一:中国多地遭雾霾笼罩空气质量再成热议话题
         * 新闻二:春节临近北京“卖房热”
         * 3)将新闻对象添加到ArrayList集合中,并且使用ListIterator倒序遍历;
         * 4)在遍历集合过程中,对新闻标题进行处理,超过15字的只保留前14个,然后在后边加“…”
         * 5)在控制台打印遍历出经过处理的新闻标题
         */
        ArrayList list = new ArrayList();
        Collections.addAll(list
                ,new News("中国多地遭雾霾笼罩空气质量再成热议话题")
                ,new News("春节临近北京“卖房热”"));
        //使用listIterator 是Iterator迭代器的增强版本, 这类提供出 hasNext next remove之外添加了一些新的方法
        ListIterator listIterator = list.listIterator();

        //1.将光标移动到最后一个位置
        while(listIterator.hasNext()){
            listIterator.next();
        }
        //2.反向向回移动
        while(listIterator.hasPrevious()){
            News news  = ((News)listIterator.previous());
            if(news.getTitle().length() >= 15){
                String substring = news.getTitle().substring(14);//从14位开始往后的字符串
                String replace = news.getTitle().replace(substring, "...");
                news.setTitle(replace);
            }
            System.out.println(news);
        }
    }
}

问题2:迭代器异常【并发修改异常】
    
1.快速失败
在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出Concurrent Modification Exception。
原理:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变modCount的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出异常,终止遍历。
注意:这里异常的抛出条件是检测到 modCount!=expectedmodCount 这个条件。如果集合发生变化时修改modCount值刚好又设置为了expectedmodCount值,则异常不会抛出。因此,不能依赖于这个异常是否抛出而进行并发操作的编程,这个异常只建议用于检测并发修改的bug。
 	 场景:java.util包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程中被修改)。

2.完全失败
采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。
原理:由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发Concurrent Modification Exception。
缺点:基于拷贝内容的优点是避免了Concurrent Modification Exception,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。
	 场景:java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改。
package com.qfedu.HomeWork;

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

//问题:
public class IteratorQuestion {
    public static void main(String[] args) {
        //官方中共有一个对迭代器的说明,不能迭代器中使用 集合的删除方法删除数据,否则会出现并发修改异常
        ArrayList list  = new ArrayList();
        Collections.addAll(list,1,2,3,4);
        Iterator iterator = list.iterator();
//        while (iterator.hasNext()){ //cursor != size while的循环判断是收到这个印象
//            if(iterator.next().equals(2)){//这个异常的抛出是当前 next中检查方法抛出checkForComodification();
//                //ConcurrentModificationException 并发修改迭代异常
//                //list.remove(new Integer(2));//调用时集合中删除方法,会修改 modCount值,会出现异常信息
//                //官方建议,使用迭代器自身的删除方法
//                //iterator.remove();
//            }
//        }
//        System.out.println(list);

        //问题: 为什么可以删除成功?
        /**
         * 在使用迭代器的时候用,使用集合删除方法,删除迭代器中倒数第二个元素对象,系统不会抛出异常【这个是Java自身代的Bug】
         * hasNext方法是判断cursor != size来进行判断有下一个元素 ,只要为true就执行 否则为false则停止
         * size是 集合的大小 即长度
         * cursor 是 next方法 一个自增变量 用于在hasNext方法中进行判断 只要一次next 这个值就+1
         * 之所以不会崩溃,问题也就出现在这个+1操作者身上
         *
         * 例如:
         * 集合有5个元素{1,2,3,4,5} 分别使用  集合中 remove方法删除  3 和 4 对象
         *
         * 假如删除3的时候, next 方法先先获取到这个值  修改   cursor = 2+1   cursor = 3
         * 在执行  集合中 remove方法删除操作的同时fastRemove 方法,这个方法中会修改两个值
         *   一个 modCount++   一个是 --size == 5-1 = 4
         * 此时删除成功, hasNext 会判断 cursor != size   3 != 4 为true,会再次执行next
         * 但是执行next方法之前会先执行 checkForComodification()
         * final void checkForComodification() {
         * expectedModCount 原始值是0,因为 modCount已经++ ,所以这个表达式的值是  1 != 0 表达式成立 会抛出异常
         *             if (modCount != expectedModCount) 判断 modeCount是否不等于 expectedModCount
         *                 throw new ConcurrentModificationException();
         *         }
         *
         * 加入删除4的时候, next方法先获取到这个值  cursor = 3+1   cursor = 4
         * 在执行  集合中 remove方法删除操作的同时fastRemove 方法,这个方法中会修改两个值
         *   一个 modCount++   一个是 --size == 5-1 = 4
         * 此时删除成功
         * 系统会在此回到 hasNext 会判断 cursor != size   4 != 4 为false,不会再执行while循环中删除了,所以不会崩溃
         *
         */
        while(iterator.hasNext()){
            if(iterator.next().equals(3)){
                list.remove(new Integer(3));
            }
        }
        System.out.println(list);

    }
}
    

泛型

PS:泛型就是约束集合中可以存储的数据类型,并且可以让类,方法,接口达到最大通用性

​ 泛型本身是没有任何意义的,它就是一个【占位符号】,等到传入的数据类型

为什么要使用泛型?

1.集合问题

在使用集合时,不使用泛型集合需要向下转型和判断数据类型,如果集合数据类型过【杂】,操作起来十分不方便

package com.qfedu.Genericity;

import java.util.ArrayList;

public class GenericityQuestion {
    public static void main(String[] args) {
        //1.集合问题
        //在没有使用泛型之前,集合存储的数据类型都是Object
        ArrayList list = new ArrayList();
        list.add(1);
        list.add('2');
        list.add("3");
        list.add(true);
        list.add(new Object());
        list.add(2);
        System.out.println(list);
        //求和,求出集合中数据的和
        int sum  = 0;
        for(Object obj : list){
            //我们每次获取到集合中对象,都需要向下转型
            //如果集合中存储的数据类型过【杂】,那就意味着转换会异常的麻烦
            Integer i = (Integer)obj;
            sum += i;
        }
        System.out.println(sum);
    }
}

2.通用性问题

DRY原则:不要重复自己(尽量不要写重复代码)

场景:产品经理提出需求【求出 点中x和y的值】

class Point{
    private Integer x;
    private Integer y;
    提供 getter和setter  有参无参  计算x和y的值
}
//计算Double类型x和y
class Point2{
    private Double x;
    private Double y;
    提供 getter和setter  有参无参  计算x和y的值
}
//计算 Long和String类 x和y
class Point3{
    private Long x;
    private Long y;
    提供 getter和setter  有参无参  计算x和y的值
}
class Point4{
    private String x;
    private String y;
    提供 getter和setter  有参无参  计算x和y的值
}
//已经违背了 DRY 原则,你为了达到最大通用性
class Point{
    private Object x;   //String
    private Object y;   //Integer
    //计算的时候需要向下转型
    提供 getter和setter  有参无参  计算x和y的值
    
}
//此时即想达到Point类的通用性也想达到x和y数据类型一致,此时就可以使用泛型了

泛型概念

PS: 泛型的本身没有任何含义,它就是一个占位符号,只有真正传入数据类型的时候才会有真正的意义

泛型是从Java5开始支持的一个语法,它可以通过传入数据类型【进行赋值和约束】,从而可以达到限制传入的数据和数据类型

语法:
    菱形语法  <占位符号>   
    
 PS:
		通过这样的语法就可以定义泛型,定义泛型需要使用占位符,这个占位符号是"26个英文字母大写",其中的某一个,如果同时声明多个泛型,泛型之间的字母不能重复   
     泛型只能"存活在编译时期,一旦运行时泛型会自动消除(泛型擦除)"
     一般存在泛型类一旦编译成.class文件时是看不到泛型的
     占位符号一般是在声明泛型的时候使用,如果需要给占位符"赋值",这个"值",只能是数据类型       

泛型的基本使用

1.作用在集合上

PS:集合类或接口,基本上都使用上了泛型,所以无需对泛型声明,只需要对泛型赋值即可【只需要写数据类型就可以】

package com.qfedu.Genericity;

import java.util.ArrayList;

/**
 * 在集合中使用泛型
 */
public class GenericityDemo {
    public static void main(String[] args) {
        //在创建集合时,需要使用如下语法
        // 集合类型<集合中存储元素的数据类型>  集合对象名 = new 集合实现类<>();
        // 这个语法其实类似于 创建数组
        //  数组中存储元素的数据类型[]  数组名 = new 数组中存储元素的数据类型[数组长度];
        //PS:集合一旦明确泛型,其他数据就无法在存到集合中
        //集合使用泛型的好处:不需要在进行向下转型,可以保证集合存储数据类型都一致
        //             坏处: 集合只能存单一数据类型【可以忽略不计】

        //例如:向集合存储1-5的整数
        //1.不使用泛型
        ArrayList list = new ArrayList();
        //无法限制存储到集合中数据
        list.add(1);
        list.add('2');
        list.add("3");
        list.add(new Integer(4));
        list.add(5);

        //计算1到5的和
        for(Object obj : list){
            //因为在不使用泛型之前,默认是Object,所以在使用数据时候需要对应转换
            //如果数据类型不一致,会面临强制转换异常,需要使用 instanceof进行类型检查
            if(obj instanceof Integer) {//解决了数据不一致强转问题
                //数据计算的时候回缺少数据 --> '2' 和"3"计算不了
                Integer i = (Integer) obj;
            }
        }


        //2.使用泛型
        //当前就是一个泛型集合,这里说明这个集合中只能存储Integer类型
        //当前泛型限制ArrayList这个集合中只能存储Integer
        ArrayList<Integer> list1 = new ArrayList<>();
        list1.add(1);
        //list1.add('2'); 这里就是一个典型限制,限制存其他数据类型
        list1.add(2);
        list1.add(3);
        list1.add(4);
        list1.add(5);

        
        //计算  因为现在的集合已经确定了泛型,获取集合中数据的时候就可以明确取出对应的数据类型
        //      无需在使用Object了
        int sum = 0;
        for (Integer i: list1) {
            //取出的值就是Integer类型,无需在进行类型转换了
            sum += i;

        }
    }
}


PS:从这一刻开始起,在创建集合的时候就需要写泛型,除非约定这个集合什么都存储,否则集合必须带泛型

2.泛型使用在类上

package com.qfedu.Genericity;


/**
 * 泛型类
 * 泛型类的产生是在一个抽象类或普通类的类型后面添加泛型语法定义,此时这个类就会变成泛型类
 * 语法:
 * 访问权限修饰符 [abstract] class 类名<占位符>{
 *
 *       此时这个占位符即泛型可以在类中方法和属性中使用这个泛型
 *
 * }
 * ps:如果有多个占位符,可以使用逗号隔开--》 <占位符1,占位符2.....>
 */
public class GenericityClassDemo<T> {
    //此时这个T就是泛型,但是需要注意其本质【它就是站位符,本身没有任何含义,只有当传入真正数据类型时才会有意义】

    //第一个定义属性是可以当做数据类型使用
    private T x;  // 此时这个x的数据类型是不确定,只有对 T 进行赋值 才能去顶x的数据类型
    private T y;

    //第二个定义成员方法中的参数类型和返回值类型
    public T show(T n){
        return  n;
    }
    //-------------------------------等价于------------------------------
//    private Object x;
//    private Object y;
//
//    public Object show(Object n){
//        return  n;
//    }
    //此时出现问题
    /**
     * 1.T 既然是占位符号 ,本身有没有任何意义, 为什么语法不报错
     *   其实泛型之所以占位不报错的原因,是在于Java默认这个为和这个占位符其实就是---》Object
     * 2.何时才能确定 T 是什么数据类型呢?
     * 2.1 创建对象的时候指定泛型是什么数据类型, 此时系统就不会是Object进行占位,而是使用你实际传递进来的数据类型
     * 2.2 发生了继承,子类决定了父类的泛型是什么数据类型,就可以确定数据类型了
     */
    public static void main(String[] args) {
        //在对 对象创建的时候,就可以对泛型赋值  此时这个Integer 就相当于是 对T进行了父类,在类中所有T的位置都是Integer
        GenericityClassDemo<Integer> gcd = new GenericityClassDemo<>();
        gcd.x = 1;
        Integer show = gcd.show(1);

        //如果在创建时不给泛型赋值,可以,但是需要注意就会激活默认数据类型  --> Object
        GenericityClassDemo gcd2 = new GenericityClassDemo();
        gcd2.x = "1";

    }
}
//使用一个类继承于另外一个泛型类的时候,可以对泛型类中泛型进行赋值
class A  extends GenericityClassDemo<Integer>{

    public void  display(){
        Integer show = show(1);
    }

}
class  B  extends  GenericityClassDemo{
    //这里并内有指定父类的泛型,所以,默认激活Object
    public  void display(){
        show("Object");
    }
}


PS:在给泛型赋值的时候【不存在继承】,不可能、不允许出现这种语法

   List<Object> list = new  ArrayList<String>;

泛型类型只能确定成一种,不允许出现在赋值时,前后泛型数据不一致,【不可能出现这种关系】
    

3.泛型方法中使用

泛型声明在方法上

1.泛型类中泛型只适用于非静态方法,如果需要给静态方法设置泛型,必须使用泛型方定义

2.泛型类中泛型只适用于大范围相同数据类型,如果某个方法需要使用自己的泛型,可以使用泛型方法定义

package com.qfedu.Genericity;


import java.util.Arrays;
import java.util.List;

/**
 * 泛型方法
 * 主要就是为了静态方法使用泛型和方法自身使用自定义泛型
 *
 * 访问权限修饰符 [static]<占位符> 返回值类型 方法名(形参定义的类型就可以使用自己泛型){
 *
 *                  方法体
 * }
 */
public class GenericityMethodDemo<T> {  //此时在这个类上定义了一个泛型

    public void  show(T t){  //这个方法使用泛型是受泛型类中定义泛型所约束
        System.out.println(t);
    }

    //定义方法的时候使用自己的泛型
    public<F>  void  display(F f){  //这个F泛型就是这个方法自身的
        System.out.println(f);
    }

    public static<E> E  showInfos(E e){//这个E泛型就是这个方法自身的
        return  e;
    }

    public static void main(String[] args) {
        //泛型方法中是如何确定泛型类型的

        //当调用方法传递参数时候就可以确定泛型数据类型
        String s = GenericityMethodDemo.showInfos("1");
        //典型 应用
        /*
         public static <T> List<T> asList(T... a) {
                      return new ArrayList<>(a);
        }
         */
        List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5);

    }

}


PS:泛型方法能确定数据类型的方式,只能是通过方法参数列表来去顶【参数列表使用泛型定义】

4.在接口中的使用

PS:泛型接口就完全参考泛型类即可

package com.qfedu.Genericity;

import java.util.Comparator;

/**
 * 泛型接口
 * 语法:
 *  public interface 接口名<占位符>{
 *     接口的内部定义就可以使用到这个泛型了
 *  }
 */
public interface GenericityInterfaceDemo<T> {
    void run(T t);
    //接口同样支持 泛型方法  谁调用这个方法,谁决定E的数据类型
    public abstract<E> void  show(E e);
}
//如何对泛型接口中泛型进行赋值

//1.使用普通类实现泛型接口并指定泛型的类型
class C implements  GenericityInterfaceDemo<Integer>{
   //在泛型接口中使用泛型作为方法参数类型的,只要指定接口的泛型,在实现方法的时候将使用指定泛型类型
    @Override
    public void run(Integer integer) {
        
    }

    @Override
    public <E> void show(E e) {

    }
}
//2.以一个泛型类实现泛型接口,可以使用泛型类的泛型对接口中泛型尽心二次定义
//  只要创建类的对象时,指定泛型类型,接口的泛型类型也将被指定
class D<P> implements GenericityInterfaceDemo<P>{

    @Override
    public void run(P p) {
        
    }

    @Override
    public <E> void show(E e) {

    }

    public static void main(String[] args) {
        D<String> d = new D<>();
        d.run("1");//这个方法的参数就是String类型,也就相当于间接的指定泛型接口中的泛型是String
        GenericityInterfaceDemo<Integer>  gif = new D<>();
          gif.run(1); //此时这个方法就是Integer类型
    }
}

class  E {
    public static void main(String[] args) {
//        new GenericityInterfaceDemo<Double>(){
//
//            @Override
//            public void run(Double aDouble) {
//                
//            }
//         
//            //如果是方法的自定义泛型,那么就不能再匿名内部类中使用
//            @Override
//            public  void show(Integer e) {
//                
//            }
//
//
//        };
        new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return 0;
            }
        };
        
    }
}


泛型限定和通配符

1.泛型通配符【?】

?表示是未知,可以作为通配符使用,进限于在方法形参定义的泛型中使用

package com.qfedu.Genericity;

import java.util.ArrayList;
import java.util.List;

/**
 * 在集合中使用泛型
 */
public class GenericityDemo2 {
    public static void main(String[] args) {
       //通配符是不可以这样使用
        //编译阶段是没有任何问题
        List<Integer> list = new ArrayList<>();
        //这个?只能出出现在形参参数类型定义的位置,表示任何数据都能接收
        //这个问号可以单独使用,也可以配合泛型限定使用
        //list.add("1");
        
        //泛型赋值时,数据类型需要是一直的,表面上Object是Integer的父类,但是在泛型中就会认为
        //Integer和Object数据类型不一致,无法尽传递
      //showInfosList(list);
        //虽然可以匹配任何的泛型类定的集合,但是在方法的内部是无法操作
        //showInfosList2(list);
        //对通配符进行泛型限定【泛型约束】

    }
    
    public static void showInfosList(List<Object> list){
        
    }
    public static void showInfosList2(List<?> list){
      

    }
    
}



2.<? extends 类名> 代表? 可以接受extends后面相同类型或其子类

3.<? super 类名> 代表? 可以接受super后面相同类型或其父类

PS:这个两个操作就是泛型限定

package com.qfedu.Genericity;

import java.util.ArrayList;
import java.util.List;

/**
 *通配符的限定
 */
public class GenericityDemo3 {
    public static void main(String[] args) {
        List<String> list1 = new ArrayList<>();
        List<Integer> list2 = new ArrayList<>();
        List<Number> list3 = new ArrayList<>();
        List<Object> list4 = new ArrayList<>();
        //泛型的上限
        //showInfosList(list1);//既不是Number的类型 也不是 Number的子类
        showInfosList(list2);
        showInfosList(list3);
        //showInfosList(list4);//既不是Number的类型 也不是 Number的子类

        //泛型的下限
       // showInfosList2(list1);//即不是Number的类型 也不是Number的父类
       // showInfosList2(list2);//即不是Number的类型 也不是Number的父类
        showInfosList2(list3);
        showInfosList2(list4);

    }
    //泛型的上限,此时的泛型?【通配符】,只能是Number 或 Number的子类
    public static void showInfosList(List<? extends  Number> list){


    }
    //泛型的下限,此时的泛型?【通配符】,只能是Number 或 Number的父类
    public static void showInfosList2(List<? super Number> list){


    }

}




扩展:泛型擦除

泛型在编译之后,泛型自动消失【自动擦除】

把不带泛型的集合赋值给带泛型集合【手动查出】

package com.qfedu.Genericity;

import java.util.ArrayList;
import java.util.List;

/**
 * 手动擦除
 */
public class GenericityDemo4 {
    public static void main(String[] args) {
        //创建一个带有泛型Integer集合
        List<Integer> list = new ArrayList<>();
        list.add(1);
       // list.add("1"); 此时泛型是生效的并且会验证传入数据是否争取

        //手动擦除泛型
        List list2 = null;
        list2 = list;//此时泛型就擦除
        list2.add("123");

        //再次对你提供和泛型的集合
        List<Integer> list3 = null;
        list3 = list2;
        list3.add(1);
        list3.forEach(System.out::println);
    }
    public static List showInfos(List<Integer> list){
        List list4 = null;
        list4 = list;
        return list4;
    }
}


Set集合

Set集合是Collection集合下的一个子集合即Set集合是继承于Collection,所以Set集合的实现类,也是Collection集合的实现类

Collection接口的实现类有:ArrayList,LinkedList,HashSet

List接口的实现类有: ArrayList,LinkedList

Set接口的实现类有: HashSet

HashSet是Set集合的实现类,这个集合的特点有:

1.无序【添加的顺序和底层存储的顺序是不一样(无序不代表随机)】

2.所有Set集合的实现类都具备【排重】作用 【Set集合中是不允许出现重复值】

3.HashSet的实现是依托于【Hash表(hash算法)】

PS:HashSet是Set集合的主要实现类即操作set集合就是在操作HashSet

HashSet集合API

package com.qfedu.Set;

import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/*
   HashSet API实现
 */
public class HashSetAPIDemo {
    public static void main(String[] args) {
         //1.创建HashSet实现了
        //此时创建好的hashSet集合默认大小是16,当达到默认大小0.75时即存储了12个元素
        //此时HashSet会自动括容
         HashSet<Integer> set = new HashSet<>();
         //2.可以使用Collection的集合实现类,初始化HashSet集合
         //将集合中数据赋值到Set集合中进行初始化
         HashSet<Integer> set1 = new HashSet<>(set);

         //常用API
         //Set集合是重写toString,所以可以直接打印,打印出Set集合中存储的元素
         //1.添加元素
        set.add(1);
        set.add(1);
        set.add(1);
        set.add(1);
        set.add(1);
        System.out.println(set);//所有set集合都是排重,排重依据【equals和hashcode】
        //2.向集合中添加另外集合的元素
        Set<Integer> set2 = new HashSet<>();
        Collections.addAll(set2,1,2,3,4,5,6,7);
        set.addAll(set2);
        System.out.println(set);

        //3.清空集合,集合的地址引用时存在,但是集合中没有数据

        // set.clear();

        //4.比较集合中是否存在某个元素[查找原则是根据返回的equals]
        boolean contains = set.contains(1);

        //5.比较集合是否存在另外一个集合中存储的元素【全体比较】
        boolean b = set.containsAll(set2);

        //6.判断集合中是否有元素【判断集合是否为空(有没有元素)】
        boolean empty = set.isEmpty();

        //7.HashSet集合没有下标,所以不嫩通过下标操作
        //所有不支持get方法 和set方法,还有普通for循环
        //遍历HashSet集合只能使用增强for和Iterator迭代器
        //这个迭代器还是最基础迭代器
        for(Integer i : set){
            System.out.println(i);
        }
       //Iterator也存在泛型,只要集合指定泛型,创建出来Iterator也会存在泛型
        Iterator<Integer> iterator = set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }

        //Java8中提供一个Collection集合通用遍历
        set.forEach(System.out::println);


        //删除,通过对象删除
        set.remove(1);

        //removeAll删除参数集合中所有提供的元素
        //retainAll保留参数集合中提供所有元素,删除其他元素

        //获取集合的长度
        System.out.println(set.size());


        //将set集合变成数组
        Object[] objects = set.toArray();


    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值