JavaSE基础笔记——集合

1、集合概述

Java集合框架(JCF)

J——java

C——Collection

F——frameWork

什么是集合?

把一堆数据集中合并在一起,进行统一的管理和操作。

我们可以把集合看成是一个容器,在里面装一堆数据。

数组其实是集合当中的一种,只是说它是最简单也是最原始的一种,所以大部分编程语言自带这种类型的集合。

数组有三大特征(缺点):

1、只能存放同一数据类型元素;——解决方案:多态,用父类引用指向子类对象的特性可以解决。Object[]可以放入所有的对象,再加上包装类,基本数据类型的问题也可以解决。

2、长度不可变——解决方案:封装,提供一个新的类型在其内部封装一个数组对象,然后让外部只能通过方法去操作元素,而不能直接操作这个数组。

3、连续内存地址空间存放——解决方案:这个需要《数据结构》的内容。然后再通过封装,继承,多态的语法进行实现设计。

JDK里,先人已经把这些设计完成了,并且提供了几十种方案,无需我们再来实现,我们只需要用就可以了。而这些设计好的容器类就是所谓的集合类。我们只需要在使用的时候,根据场景选择某一个集合对象来用即可。

什么是框架?

所谓框架,就是先人为了实现某一个目的,而预先设计的一系列具有继承或实现关系的接口和类。当程序员要完成这个功能目的的时候,只需要借助这些预先设计好的接口和类,按照先人设计的方案进行操作即可。

集合框架里的集合类的层次结构

Collection接口是集合框架的核心接口,所有的集合类都是通过这个接口衍生而来的

集合与数组类似,也是一种容器,用于装数据的。

数组和集合的元素存储的个数问题。

  • 数组定义后类型确定,长度固定
  • 集合类型可以不固定,大小是可变的。

数组和集合存储元素的类型问题。

  • 数组可以存储基本类型和引用类型的数据。
  • 集合只能存储引用数据类型的数据。

数组和集合适合的场景

  • 数组适合做数据个数和类型确定的场景。
  • 集合适合做数据个数不确定,且要做增删元素的场景,集合种类更多,功能更强大。

2、Collection集合的体系特点

集合的代表是Collection接口

集合类体系结构

集合体系分为

Collection单列集合体系,每个元素(数据)只包含一个值。

Map双列集合体系,每个元素包含两个值(键值对)。

Collection集合体系

List系列集合:添加的元素是有序、可重复、有索引。

  • ArrayList、LinekdList :有序、可重复、有索引。

Set系列集合:添加的元素是无序、不重复、无索引。

  • HashSet: 无序、不重复、无索引;
  • LinkedHashSet: 有序、不重复、无索引。
  • TreeSet:按照大小默认升序排序、不重复、无索引。

集合对于泛型的支持(如何约定集合存储数据的类型?)

集合都是泛型的形式,可以在编译阶段约束集合只能操作某种数据类型

注意:集合和泛型都只能支持引用数据类型,不支持基本数据类型,所以集合中存储的元素都认为是对象。

如果集合中要存储基本类型的数据怎么办?

Collection<Integer> list = new ArrayList<>();

Collection<Double> list1 = new ArrayList<>();

3、Collection常用API

Collection集合

  • Collection是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的。

Collection的常用API:

方法名称

说明

public boolean add(E e)

把给定的对象添加到当前集合中 

public void clear() 

清空集合中所有的元素

public boolean remove(E e)

把给定的对象在当前集合中删除

public boolean contains(Object obj)

判断当前集合中是否包含给定的对象

public boolean isEmpty()

判断当前集合是否为空

public int size()

返回集合中元素的个数。

public Object[] toArray()

把集合中的元素,存储到数组中

代码演示:
/**
 Collection集合:是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的
 API: 1、public boolean add(E e)
      2、public void clear()
      3、public boolean isEmpty()
      4、public int size()
      5、public boolean contains(Object obj)
      6、public boolean remove(E e)
      7、public Object [] toArray()
 */

public class CollectionDemo {
    public static void main(String[] args) {
        //HashSet:添加的元素是无序,不重复,无索引。
        Collection<String> list = new ArrayList<>();
        //1、添加元素,添加成功返回true。
        list.add("Java");
        list.add("HTML");
        System.out.println(list.add("HTML"));
        list.add("MySQL");
        list.add("Java");
        System.out.println(list.add("黑马"));
        System.out.println(list);

        //2、清空集合的元素。
        //list.clear();
        //System.out.println(list);

        //3、判断集合是否为空 是空返回true,反之。
        //System.out.println(list.isEmpty());

        //4、获取集合的大小。(元素的个数)
        System.out.println(list.size());

        //5、判断集合中是否包含了某个元素。
        System.out.println(list.contains("Java")); // trye
        System.out.println(list.contains("java")); // false
        System.out.println(list.contains("黑马")); // true

        //6、删除某个元素:如果有多个重复元素默认删除前面的第一个!
        System.out.println(list.remove("java")); //false
        System.out.println(list);
        System.out.println(list.remove("Java")); //true
        System.out.println(list);

        //7、把集合转换成数组 [HTML, HTML, MySQL, Java, 黑马]。
        Object[] arrs = list.toArray();
        System.out.println("数组:" + Arrays.toString(arrs));

        System.out.println("----------扩展----------");
        Collection<String> c1 = new ArrayList<>();
        c1.add("java1");
        c1.add("java2");
        Collection<String> c2 = new ArrayList<>();
        c2.add("赵敏");
        c2.add("殷素素");
        // addALL把c2集合的元素全部倒入c1中去。
        c1.addAll(c2);
        System.out.println(c1);
        System.out.println(c2);
    }
}

4、Collection遍历方式

方式一:迭代器

迭代器遍历概述

  • 遍历就是一个一个的把容器中的元素访问一遍。
  • 迭代器在Java中的代表是Iterator,迭代器是集合的专用的遍历方式。

步骤:

1、Collection集合获取迭代器

方法名称

说明

Iterator<E> iterator()

返回集合中的迭代器对象,该迭代器对象默认指向当前集合的0索引

2、Iterator中的常用方法

方法名称

说明

boolean hasNext()

询问当前位置是否有元素存在,存在返回true ,不存在返回false

E next()

获取当前位置的元素,并同时将迭代器对象移向下一个位置,注意防止取出越界。

public class CollectionDemo01 {
    public static void main(String[] args) {
        Collection<String> lists = new ArrayList<>();
        lists.add("张三");
        lists.add("李四");
        lists.add("王五");
        lists.add("赵六");
        System.out.println(lists);
        //[张三, 李四, 王五, 赵六]
        // it

        //1、得到当前集合的迭代器对象
        Iterator<String> it = lists.iterator();
//        String ele = it.next();
//        System.out.println(ele);
//        System.out.println(it.next());
//        System.out.println(it.next());
//        System.out.println(it.next());
        //System.out.println(it.next());//NoSuchElementException 出现无此元素异常错误

        //2、定义while循环
        while (it.hasNext()){
            String ele = it.next();
            System.out.println(ele);
        }
        System.out.println("----------------------");
    }
}

 方式二:foreach/增强for循环

  • 增强for循环:既可以遍历集合也可以遍历数组。
  • 它在JDK 5 之后出现的,其内部原理是一个iterator迭代器,遍历集合相当于是迭代器的简化写法。

  • 实现iterable接口的类才可以使用迭代器和增强for,Collection接口已经实现了iterable接口。

格式:

for(元素数据类型 变量名 : 数组或者Collection集合) {          

//在此处使用变量即可,该变量就是元素 }

代码演示:

public class CollectionDemo02 {
    public static void main(String[] args) {
        Collection<String> lists = new ArrayList<>();
        lists.add("张三");
        lists.add("李四");
        lists.add("王五");
        lists.add("赵六");
        System.out.println(lists);
        //[张三, 李四, 王五, 赵六]
        // ele

        for (String ele : lists){
            System.out.println(ele);
        }

        System.out.println("--------------------");
        double[] scores = {100 , 99.5 , 59.5};
        for (double score : scores) {
            System.out.println(score);
        }
    }
}

增强for修改变量,集合的元素不会动的。修改第三方变量的值不会影响到集合中的元素

方式三:lambda表达式遍历集合

  • 得益于JDK 8开始的新技术Lambda表达式,提供了一种更简单、更直接的遍历集合的方式。

Collection结合Lambda遍历的API

方法名称

说明

default void forEach(Consumer<? super T> action): 

结合lambda遍历集合

public class CollectionDemo03 {
    public static void main(String[] args) {
        Collection<String> lists = new ArrayList<>();
        lists.add("张三");
        lists.add("李四");
        lists.add("王五");
        lists.add("赵六");
        System.out.println(lists);
        //[张三, 李四, 王五, 赵六]
        //  s

//        lists.forEach(new Consumer<String>() {
//            @Override
//            public void accept(String s) {
//                System.out.println(s);
//            }
//        });

        lists.forEach(s -> System.out.println(s) ); //简化
    }
}

5、Collection存储自定义类型的对象

需求:某影院系统需要在后台存储上述三部电影,然后依次展示出来。

1、定义一个电影类、定义一个集合存储电影对象。

2、创建三个电影对象,封装相关数据,把三个对象存入到集合里去。

3、遍历集合里的三个对象,输出相关信息

电影类:

public class Movie {
    private String name;//名字
    private double score;//得分
    private String actor;//演员

    public Movie() {
    }

    public Movie(String name, double score, String actor) {
        this.name = name;
        this.score = score;
        this.actor = actor;
    }

    public String getName() {
        return name;
    }

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

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }

    public String getActor() {
        return actor;
    }

    public void setActor(String actor) {
        this.actor = actor;
    }
}

测试类:

public class TestDemo {
    public static void main(String[] args) {
        //1、定义一个电影类
        //2、定义一个集合对象存储3部电影对象
        Collection<Movie> movies = new ArrayList<>();
        movies.add(new Movie("《你好,李焕英》", 9.5 , "张小斐,贾玲,沈腾,陈赫"));
        movies.add(new Movie("《唐人街探案》", 8.5 , "王宝强,刘昊然,美女"));
        movies.add(new Movie("《刺杀小说家》", 8.6 , "雷佳音,杨幂"));
        
        //3、遍历集合容器中的每个电影对象
        for (Movie movie : movies) {
            System.out.println("片名: " + movie.getName());
            System.out.println("得分: " + movie.getScore());
            System.out.println("主演: " + movie.getActor());
        }
    }
}

6、常见数据结构简介

数据结构概述

  • 数据结构是计算机底层存储、组织数据的方式。是指数据相互之间是以什么方式排列在一起的。
  • 通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率

常见的数据结构:

栈 队列 数组 链表 二叉树 二叉查找树 平衡二叉树 红黑树。。。

栈数据结构的执行特点:

  • 后进先出,先进后出
  • 数据进入栈模型的过程称为:压栈/进栈
  • 数据离开栈模型的过程称为:弹栈/出栈

常见数据结构之队列:

  • 先进先出,后进后出
  • 数据从后端进入队列模型的过程称为:入队列
  • 数据从前端离开队列模型的过程称为:出队列

常见数据结构之数组:

数组是一种查询快,增删慢的模型

  • 查询速度快:查询数据通过地址值和索引定位,查询任意数据耗时相同。(元素在内存中是连续存储的)
  • 删除效率低:要将原始数据删除,同时后面每个数据前移。
  • 添加效率极低:添加位置后的每个数据后移,再添加元素。

链表的特点

  • 链表中的元素是在内存中不连续存储的,每个元素节点包含数据值和下一个元素的地址。
  • 链表查询慢。无论查询哪个数据都要从头开始找。
  • 链表增删相对快。
  • 分为单向链表和双向链表。

二叉树概述

  • 只能有一个根节点,每个节点最多支持2个直接子节点。
  • 节点的度: 节点拥有的子树的个数,二叉树的度不大于2 叶子节点 度为0的节点,也称之为终端结点。
  • 高度:叶子结点的高度为1,叶子结点的父节点高度为2,以此类推,根节点的高度最高。
  • 层:根节点在第一层,以此类推
  • 兄弟节点 :拥有共同父节点的节点互称为兄弟节点

二叉查找树(又称二叉排序树和二叉搜索树)

特点:

1,每一个节点上最多有两个子节点

2,左子树上所有节点的值都小于根节点的值

3,右子树上所有节点的值都大于根节点的值

目的:提高检索数据的性能。

二叉树查找树添节点:规则:小的存左边,大的存右边,一样的不存

平衡二叉树

  • 二叉查找树的问题:出现瘸子现象,导致查询的性能与单链表一样,查询速度变慢!
  • 平衡二叉树是在满足查找二叉树的大小规则下,让树尽可能矮小,以此提高查数据的性能。
  • 要求:任意节点的左右两个子树的高度差不超过1,任意节点的左右两个子树都是一颗平衡二叉树。

平衡二叉树在添加元素后可能导致不平衡:基本策略是进行左旋,或者右旋保证平衡。

平衡二叉树-旋转的四种情况

左左:当根节点左子树的左子树有节点插入,导致二叉树不平衡

左右:当根节点左子树的右子树有节点插入,导致二叉树不平衡

右右:当根节点右子树的右子树有节点插入,导致二叉树不平衡

右左:当根节点右子树的左子树有节点插入,导致二叉树不平衡

红黑树

  • 红黑树是一种自平衡的二叉查找树,是计算机科学中用到的一种数据结构。
  • 1972年出现,当时被称之为平衡二叉B树。1978年被修改为如今的"红黑树"。
  • 每一个节点可以是红或者黑;红黑树不是通过高度平衡的,它的平衡是通过“红黑规则”进行实现的。

红黑规则

  • 每一个节点或是红色的,或者是黑色的,根节点必须是黑色。
  • 如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,叶节点也是黑色的。
  • 如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)。
  • 对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点。
  • 红黑树增删改查的性能都很好

添加节点

  • 添加的节点的颜色,可以是红色的,也可以是黑色的。
  • 默认用红色效率高。

7、List系列集合

List集合特点、特有API

List系列集合特点  

  • ArrayList、LinekdList :有序,可重复,有索引。
  • 有序:存储和取出的元素顺序一致
  • 有索引:可以通过索引操作元素
  • 可重复:存储的元素可以重复

List集合特有方法

  • List集合因为支持索引,所以多了很多索引操作的独特api,其他Collection的功能List也都继承了。

方法名称

说明

void add(int index,E element)

在此集合中的指定位置插入指定的元素

E remove(int index)

删除指定索引处的元素,返回被删除的元素

E set(int index,E element)

修改指定索引处的元素,返回被修改的元素

E get(int index)

返回指定索引处的元素 

public class ListDemo01 {
    public static void main(String[] args) {
        //1、创建一个ArrayList集合对象:
        //List:有序,可重复,有索引的。
        List<String> list = new ArrayList<>(); //一行经典代码(多态)
        list.add("Java");
        list.add("Java");
        list.add("MySQL");
        list.add("MySQL");

        //2、在某个索引位置插入元素
        list.add(2,"HTML");
        System.out.println(list);

        //3、根据索引删除元素,返回被删除元素
        System.out.println(list.remove(2));
        System.out.println(list);

        //4、根据索引获取元素:public E get(int index):返回集合中指定位置的元素。
        System.out.println(list.get(2));

        //5、修改索引位置处的元素:public E remove(int index , E element)
        //返回修改前的数据
        System.out.println(list.set(1, "高斯林"));
        System.out.println(list);
    }
}

List实现类的底层原理:

  • ArrayList底层是基于数组实现的,根据查询元素快,增删相对慢。
  • 第一次创建集合并添加第一个元素的时候,在底层创建一个默认长度为10的数组。
  • LinkedList底层基于双链表实现的,查询元素慢,增删首尾元素是非常快的。

List集合的遍历方式小结

  • 迭代器
  • 增强for循环
  • Lambda表达式
  • for循环(因为List集合存在索引)

数组的for-each和集合类的for-each的区别:

1、数组的for-each在经过编译以后的class文件中,是被
   翻译成了普通for循环;
2、集合类的for-each在经过翻译以后的class文件中,是被
   翻译成了迭代器语法。

迭代器Iterator是专门给集合类提供的一种遍历手段。因为除了List,
其它的Set和Map集合是没有线性特性的,即没有下标,所以无法使用使用
普通for。

迭代器的特点:

1、只能从头到尾遍历一次;
2、在遍历的过程中,不能给lst增加删除元素,有可能发生异常。
由于迭代器的语法比较繁琐,所以JDK1.5提供了for-each循环。
但是由于本质是一样的,所以for-each也不能在遍历过程中改变
元素个数,也只能从头到尾访问一遍。

public class ListDemo02 {
    public static void main(String[] args) {
        List<String> lists = new ArrayList<>();
        lists.add("java1");
        lists.add("java2");
        lists.add("java3");

        /**1、for循环。*/
        System.out.println("-----------------------");
        for (int i = 0; i < lists.size(); i++) {
            String ele = lists.get(i);
            System.out.println(ele);
        }

        /**2、迭代器。*/
        System.out.println("-----------------------");
        Iterator<String> it = lists.iterator();
        while (it.hasNext()){
            String ele = it.next();
            System.out.println(ele);
        }

        /**3、foreach。*/
        System.out.println("-----------------------");
        for (String ele : lists){
            System.out.println(ele);
        }

        /**4、Lambda。*/
        System.out.println("-----------------------");
        lists.forEach(s -> {
            System.out.println(s);
        });
    }
}

ArrayList集合的底层原理

  •  ArrayList底层是基于数组实现的:根据索引定位元素快,增删需要做元素的移位操作。
  •  第一次创建集合并添加第一个元素的时候,在底层创建一个默认长度为10的数组。
  • 当List集合存储元素超过容量后,会对集合进行扩容1.5倍。且把旧集合复制过来。

LinkedList集合的底层原理

  • 底层数据结构是双链表,查询慢,首尾操作的速度是极快的,所以多了很多首尾操作的特有API。

LinkedList集合的特有功能

方法名称

说明

public void addFirst​(E e)

在该列表开头插入指定的元素

public void addLast​(E e)

将指定的元素追加到此列表的末尾

public E getFirst​()

返回此列表中的第一个元素

public E getLast​()

返回此列表中的最后一个元素

public E removeFirst​()

从此列表中删除并返回第一个元素

public E removeLast​()

从此列表中删除并返回最后一个元素

public class ListDemo03 {
    public static void main(String[] args) {
        // LinkedList 可以完成队列结构,和栈结构(双链表)
        //栈
        LinkedList<String> stack = new LinkedList<>();
        //压栈,入栈
        /*stack.addFirst("第1颗子弹");
        stack.addFirst("第2颗子弹");
        stack.addFirst("第3颗子弹");
        stack.addFirst("第4颗子弹");
        stack.addFirst("第5颗子弹");*/
        //push的底层就是addFirst(),jdk1.6开始加进来
        stack.push("第1颗子弹");
        stack.push("第2颗子弹");
        stack.push("第3颗子弹");
        stack.push("第4颗子弹");
        stack.push("第5颗子弹");

        System.out.println(stack);
        //出栈 弹栈
        //System.out.println(stack.removeFirst());
        //pop的底层就是removeFirst
        System.out.println(stack.pop());
        System.out.println(stack.removeFirst());
        System.out.println(stack.removeFirst());
        System.out.println(stack);

        //队列
        LinkedList<String> queue = new LinkedList<>();
        //入队
        queue.addLast("1号");
        queue.addLast("2号");
        queue.addLast("3号");
        queue.addLast("4号");
        System.out.println(queue);
        //出队
        System.out.println(queue.removeFirst());
        System.out.println(queue.removeFirst());
        System.out.println(queue.removeFirst());
        System.out.println(queue);
    }
}

ArrayList和Vector:面试常遇到

Vector这个类虽然名字里没有List,但是也是List接口下的实现类。且它的底层数据结构也是一个可变长的数组。

无论在API,还是在结构上这两个类是一样的,他们的区别是:Vector是线程安全的,ArrayList是线程不安全的。

8、集合的并发修改异常问题

问题引出

  • 当我们从集合中找出某个元素并删除的时候可能出现一种并发修改异常问题。

哪些遍历存在问题?

  • 迭代器遍历集合且直接用集合删除元素的时候可能出现。
  • 增强for循环遍历集合且直接用集合删除元素的时候可能出现。

哪种遍历且删除元素不出问题

  • 迭代器遍历集合但是用迭代器自己的删除方法操作可以解决。
  • 使用for循环遍历并删除元素不会存在这个问题。
/**
 目标:研究集合遍历并删除元素可能出现的:并发修改异常问题
 */
public class Test {
    public static void main(String[] args) {
        //1、准备数据
        List<String> list = new ArrayList<>();
        list.add("黑马");
        list.add("Java");
        list.add("Java");
        list.add("赵敏");
        list.add("赵敏");
        list.add("素素");
        System.out.println(list);
        //[黑马, Java, Java, 赵敏, 赵敏, 素素]
        // it

        //需求:删除全部的Java信息。
        //a、迭代器遍历删除
/*        Iterator<String> it = list.iterator();
        while (it.hasNext()){
            String ele = it.next();
            if ("Java".equals(ele)){
                //list.remove("Java");
                it.remove();//删除当前所在元素,并且不会后移;使用迭代器删除当前位置的元素,保证不后移,能够成功遍历到全部元素!
            }
        }
        System.out.println(list);*/

        //b、foreach遍历删除(会出现bug)ConcurrentModificationException并发修改异常
/*        for (String s : list) {
            if ("Java".equals(s)) {
//                list.remove("Java");
            }
        }*/

        //c、Lambda表达式(会出现bug)
/*        list.forEach(s -> {
            if ("Java".equals(s)) {
                list.remove("Java");
            }
        });*/

        //d、for循环(不会出现异常错误,但是数据删除出现了问题 :会漏掉元素)
/*        for (int i = 0; i < list.size(); i++) {
            String ele = list.get(i);
            if ("Java".equals(ele)) {
                list.remove("Java");
            }
        }
        System.out.println(list);*/

        //解决方案1
        //[黑马, Java, Java, 赵敏, 赵敏, 素素]
        //                                 i
        //倒着删
/*        for (int i = list.size()-1; i >= 0 ; i--) {
            String ele = list.get(i);
            if ("Java".equals(ele)) {
                list.remove("Java");
            }
        }
        System.out.println(list);*/

        //解决方案2
        //[黑马, Java, Java, 赵敏, 赵敏, 素素]
        //                                 i
/*        for (int i = 0; i < list.size(); i++) {
            String ele = list.get(i);
            if ("Java".equals(ele)) {
                list.remove("Java");
                i -- ;
            }
        }
        System.out.println(list);*/
    }
}

9、泛型深入

1、泛型不是Java所特有的,c++早有了

2、Java当中的泛型经常被称之为“假泛型”;因为,它只是在编译器对泛型进行检查;

3、泛型的地位很高,它代表了一种传参方式——类型参数化

我们之前学到的函数的参数,叫做数据参数化,它控制的是把一个模块当中的数据传递到另一个模块当中去,这时的参数关注的是“数据值”。

泛型是指在一个模块当中要使用哪种数据类型需要另一个模块传递给它,相对于把类型做了参数。

泛型的概述和优势

泛型的概述:

泛型就是一个标签

注意:JDK1.7开始之后后面的泛型申明可以省略不写

  • 泛型:是JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查。
  • 泛型的格式:<数据类型>; 注意:泛型只能支持引用数据类型。
  • 集合体系的全部接口和实现类都是支持泛型的使用的。

泛型的好处:

  • 统一数据类型。
  • 把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为编译阶段类型就能确定下来。
public class GenericityDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Java");
        list.add("Java1");
        //list.add(23);

        List list1 = new ArrayList();
        list1.add("Java");
//        list1.add(23.3);
//        list1.add(false);
        list1.add("Spring");

//        for (Object o : list1) {
//            String ele = (String) o;
//            System.out.println(ele);
//        }

        for (Object s : list1) {
            System.out.println(s);
        }

        System.out.println("-------------------------");
        //存储任意类型的元素
        List<Object> list2 = new ArrayList<>();
        list2.add(23);
        list2.add(23.3);
        list2.add("Java");
    }
}

泛型可以在 很多地方进行定义:

自定义泛型类

泛型类概述:

  • 定义类时同时定义了泛型的类就是泛型类
  • 格式:修饰符 class 类名 <泛型变量> {}
  • 范例:public class MyArrayList<T>{}
  • 此处泛型变量T可以随便写成任意标识,常见的如E,T,K,V等。
  • 作用:编译阶段可以指定数据类型,类似于集合的作用。
  • 泛型类的原理(核心思想):把出现泛型变量的地方全部替换成传输的真实数据类型。

课程案例导学:

需求:模拟ArrayList集合自定义一个集合MyArrayList集合,完成添加和删除功能的泛型设计即可。关注泛型设计。

public class MyArrayList<E> {
    private ArrayList lists = new ArrayList();

    public void add(E e){
        lists.add(e);
    }

    public void remove(E e){
        lists.remove(e);
    }

    @Override
    public String toString() {
        return lists.toString();
    }
}
public class Test {
    public static void main(String[] args) {
        //需求:模拟ArrayList定义一个MyArrayList,关注泛型设计
        MyArrayList<String> list = new MyArrayList<>();
        list.add("Java");
        list.add("Java");
        list.add("MySQL");
        list.remove("MySQL");
        System.out.println(list);

        MyArrayList<Integer> list2 = new MyArrayList<>();
        list2.add(23);
        list2.add(24);
        list2.add(25);
        list2.remove(25);
        System.out.println(list2);
    }
}

自定义泛型方法

泛型方法的概述:

  • 定义方法时同时定义了泛型的方法就是泛型方法。
  • 泛型方法格式:修饰符<泛型变量>方法返回值  方法名称(形参列表){}
  • 范例:public <T> void show (T t){}
  • 作用方法中可以使用泛型接收一切实际类型的参数,方法更具备通用性。
  • 核心思想:是把出现泛型变量的地方全部替换成传输的真实数据类型。
  • 原理:把出现泛型变量的地方全部替换成传输的真实数据类型。
  • 小结:泛型方法可以让方法更灵活的接收数据,可以做通用技术!

课程案例导学:

给你一个任意类型的数组,都能返回它的内容。也就是Arrays.toString(数组)的功能。

public class GenericDemo {
    public static void main(String[] args) {
        String [] names = {"张三","李四","王五"};
        printArray(names);

        Integer[] ages = {10, 20, 30};
        printArray(ages);
    }

    public static <T> void printArray(T[] arr){
        if (arr != null){
            StringBuilder sb = new StringBuilder("[");
            for (int i = 0;i < arr.length; i++) {
                sb.append(arr[i]).append(i == arr.length - 1 ? "" : ",");
            }
            sb.append("]");
            System.out.println(sb);
        }else {
            System.out.println(arr);
        }
    }
} 

自定义泛型接口

泛型接口的概述

  • 使用了泛型定义的接口就是泛型接口。
  • 泛型接口的格式:修饰符 interface 接口名称<泛型变量> {}
  • 范例: public interface Data<E>{}
  • 作用泛型接口可以让实现类选择当前功能需要操作的数据类型
  • 小结:泛型接口的原理:泛型接口可以约束实现类,实现类可以在实现接口的时候传入自己操作的数据类型这样重写的方法都将是针对于该类型的操作。

课程案例导学:

教务系统,提供一个接口可约束一定要完成数据(学生,老师)的增删改查操作

接口:

public interface Data<E> {
    void add(E s);//增加
    void delete(int id);//删除
    void update(E e);//修改
    E queryByID(int id);//查询
}

学生类:

public class Student {
}

学生数据:

public class StudentData implements Data<Student> {
    @Override
    public void add(Student s) {

    }

    @Override
    public void delete(int id) {

    }

    @Override
    public void update(Student student) {

    }

    @Override
    public Student queryByID(int id) {
        return null;
    }
}

老师类:

public class Teacher {
}

老师数据:

public class TeacherData implements Data<Teacher> {
    @Override
    public void add(Teacher s) {

    }

    @Override
    public void delete(int id) {

    }

    @Override
    public void update(Teacher teacher) {

    }

    @Override
    public Teacher queryByID(int id) {
        return null;
    }
}

泛型通配符

泛型通配符: ?

  • ?可以在使用泛型的时候代表一切类型
  • ETKV是在定义泛型的时候使用的。

泛型的上下限:

?extends Car:?必须是Car或者其子类  泛型上限
?super Car:?必须是Car或者其父类  泛型下限(用的不多)

案例导学:

开发一个极品飞车的游戏,所有车都能一起参与比赛。

注意:虽然BMW和BENZ都继承了Car,但是ArrayList<BMW>和ArrayList<BENZ>是没有关系的。

public class GenericityDemo {
    public static void main(String[] args) {
        ArrayList<BMW> bmws = new ArrayList<>();
        bmws.add(new BMW());
        bmws.add(new BMW());
        bmws.add(new BMW());
        go(bmws);

        ArrayList<BENZ> benzs = new ArrayList<>();
        benzs.add(new BENZ());
        benzs.add(new BENZ());
        benzs.add(new BENZ());
        go(benzs);

        ArrayList<Dog> dogs = new ArrayList<>();
        dogs.add(new Dog());
        dogs.add(new Dog());
        dogs.add(new Dog());
        //go(dogs);
    }

    /**
     所有车比赛
     */
    public static void go(ArrayList<? extends Car>cars){
    }
}


class Dog{
}


class BENZ extends Car{
}

class BMW extends Car{
}

//父类
class Car{
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值