Java集合和常见数据结构以及泛型

news2024/11/20 3:32:56

Java集合和常见数据结构以及泛型

  • 集合概述
  • Collection集合的体系特点
  • Collection集合常用API
  • Collection集合的遍历方式
    • 方法一:迭代器
    • 方法二:foreach/增强for循环
    • 方法三:lambda表达式
  • Collection集合存储自定义类型的对象
  • 常见数据结构
    • 数据结构概述、栈、队列
    • 数组
    • 链表
    • 二叉树、二叉查找树
    • 平衡二叉树
    • 红黑树
  • List系列集合
  • 补充知识:集合的并发修改异常问题
    • List集合特点、特有API
    • List集合的遍历方式
    • ArrayList集合的底层原理
    • LinkedList集合的底层原理
  • 补充知识:集合的并发修改异常问题
  • 补充知识:泛型深入
    • 泛型的概述和优势
    • 自定义泛型类
    • 自定义泛型方法
    • 自定义泛型接口
    • 泛型通配符、上下限

集合概述

集合和数组都是容器。
数组的特点:
1.数组定义完成并启动后,类型确定、长度固定。
2.在进行增删数据操作的时候,数组是不太合适的,增删数据都需要放弃原有数组或者移位。
数组适合的场景
当业务数据的个数是固定的,且都是同一批数据类型的时候,可以采取定义数组存储。

在这里插入图片描述
集合是Java中存储对象数据的一种容器。
集合的特点:
1.集合的大小不固定,启动后可以动态变化,类型也可以选择不固定。集合更像气球。
2.集合非常适合做元素的增删操作。
注意:集合中只能存储引用类型数据,如果要存储基本类型数据可以选用包装类。
集合适合的场景
数据的个数不确定,需要进行增删元素的时候。

Collection集合的体系特点

集合类体系结构
在这里插入图片描述
1.Collection单列集合,每个元素(数据)只包含一个值。
2。Map双列集合,每个元素包含两个值(键值对)。
注意:前期先掌握Collection集合体系的使用。
Collection集合体系:

在这里插入图片描述
Collection集合特点:
1.List系列集合:添加的元素是有序、可重复、有索引。

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

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

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

package d1_collcction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;

public class CollectionDemo {
    public static void main(String[] args) {
        // 有序 可重复 有索引
        Collection list = new ArrayList();
        list.add("Java");
        list.add("Java");
        list.add("Mybatis");
        list.add(23);
        list.add(23);
        list.add(false);
        list.add(false);
        System.out.println(list);
        //
        Collection list1 = new HashSet();
        list1.add("Java");
        list1.add("Java");
        list1.add("Mybatis");
        list1.add(23);
        list1.add(23);
        list1.add(false);
        list1.add(false);
        System.out.println(list1);
    }
}

集合对于泛型的支持
1.集合都是支持泛型的,可以在编译阶段约束集合只能操作某种数据类型。

Collection<String> lists = new ArrayList<String>();
Collection<String> lists = new ArrayList<String>(); // JDK1.7开始后面的泛型类型申明可以省略不写

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

错误的写法:
Collection<int> lists = new ArrayList<>();

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

// 存储基本类型使用包装类
Collection<Integer> lists = new ArrayList<>();
Collection<Double> lists1 = new ArrayList<>();

Collection集合常用API

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

在这里插入图片描述

package d2_collection_api;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;

public class CollectionDemo {
    public static void main(String[] args) {
        // HoshSet:添加的元素是无序,不重复,无索引。
        Collection<String> c = new ArrayList <>();

        // 添加元素,添加成功返回true
        c.add("MYSQL");
        c.add("HTML");
        c.add("MYSQL");
        c.add("java");
        System.out.println(c.add("java")); // true
        System.out.println(c); // [MYSQL, HTML, MYSQL, java, java]

        // 清空集合的元素
//        c.clear();
//        System.out.println(c); // []

        // 判断集合是否为空 是空返回true,反之
        System.out.println(c.isEmpty()); // false

        // 获取集合的大小。
        System.out.println(c.size());

        // 判断集合中形容集合包含某个元素。
        System.out.println(c.contains("java")); //true

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

        // 把集合转换成数组
        Object[] arrs = c.toArray();
        System.out.println("数组:"+ Arrays.toString(arrs));

        System.out.println("-------------扩展------------");
        Collection<String> c1 = new ArrayList <>();
        c1.add("snow");
        c1.add("dream");
        Collection<String> c2 = new ArrayList <>();
        c2.add("瑞瑞");
        c2.add("云云");
        // addAll把c2集合的元素全部倒入到c1中去。
        c1.addAll(c2);
        System.out.println(c1);
        System.out.println(c2);
    }
}

Collection集合的遍历方式

方法一:迭代器

迭代器遍历概述
1.遍历就是一个一个的把容器中的元素访问一遍。
2.迭代器在Java中的代表是Iterator,迭代器是集合的专用遍历方式。
Collection集合获取迭代器

在这里插入图片描述
Iterator中的常用方法
在这里插入图片描述

package d2_collection_api;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class CollectionDemo1 {
    public static void main(String[] args) {
        Collection<String> c = new ArrayList <>();
        c.add("a");
        c.add("b");
        c.add("d");
        c.add("e");
        System.out.println(c);

        // 得到当前集合的迭代器对象。
        Iterator<String> it = c.iterator();
//        System.out.println(it.next());
//        System.out.println(it.next());
        // 定义循环
        while (it.hasNext()){
            String ele = it.next();
            System.out.println(ele);
        }
    }
}

方法二:foreach/增强for循环

增强for循环
1.增强for循环:既可以遍历集合也可以遍历数组。
2,它是JDK5之后出现的,其内部原理是一个Iterator迭代器,遍历集合相当于是迭代器的简化写法。
实现Iterable接口的类才可以使用迭代器和增强for,Collection接口已经实现了Iterable接口。
格式:

for(元素数据类型 变量名:数组或者Collection集合){
	// 在此处使用变量即可,改变量就是元素
}

Collection<String> t = new ArrayList <>();
for(String e:t){
	System.out.println(e);
}

在这里插入图片描述

package d2_collection_api;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;

public class CollectionDemo2 {
    public static void main(String[] args) {
        Collection <String> c = new ArrayList <>();
        c.add("A");
        c.add("B");
        c.add("C");
        c.add("D");
        System.out.println(c);
        // 增强for
        for(String e : c){
            System.out.println(e);
        }
        System.out.println("----------------");
        int[] age = {11,22,33,44};
        System.out.println(Arrays.toString(age));
        for(int a : age){
            System.out.println(a);
        }
    }
}

方法三:lambda表达式

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

Collection结合Lambda遍历的API
在这里插入图片描述

package d2_collection_api;
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Consumer;

public class CollectionDemo3 {
    public static void main(String[] args) {
        Collection<String> c = new ArrayList <>();
        c.add("A");
        c.add("B");
        c.add("C");
        c.add("D");
        System.out.println(c);

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

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

		// 进一步简化
        c.forEach(System.out::println);
    }
}

Collection集合存储自定义类型的对象

**案例:影片信息在程序中的表示
需求:某影院系统需要在后台存储上述三部电影,然后依次展示出来。
分析:
1.定义一个电影类,定义一个集合存储电影对象。
2.创建3个电影对象,封装相关数据,把3个对象存入到集合中去。
3.遍历集合中的3个对象,输出相关信息。
**
Movie.java电影类:

package d3_collection_object;

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

TestDemo.java实现类:

package d3_collection_object;
import java.util.ArrayList;
import java.util.Collection;

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

内存运行逻辑图:
在这里插入图片描述
首先main方法会加载到栈内存中运行,然后执行第一行代码创建movices集合对象,在栈内存中开辟空间存储堆内存中集合的内存地址,然后在堆内存中创建电影对象,把对应添加到堆内存的集合中去。
在这里插入图片描述
然后通过foreach循环遍历出集合中的每个对象,再根据每个对象的get方法取出对象的内容。

常见数据结构

数据结构概述、栈、队列

数据结构概述:
1.数据结构是计算机底层存储、组织数据的方式。是指数据相互之间是以什么方式排列在一起的。

2.通常情况下,精心选择的数据 结构可以带来更高的运行或者存储效率。
常见的数据结构:
1.栈
2.队列
3.数组
4.链表
5.二叉树
6.二叉查找树
7.平衡二叉树
8.红黑树、等

栈数据结构的执行特点:
后进先出,先进后出

在这里插入图片描述
数据进入栈模型的过程称为:压/进栈
数据离开栈模型的过程称为:弹/出栈

队列的特点:
先进先出,后进后出

在这里插入图片描述
数据从后端进入队列模型的过程称为:入队列
数据从前端离开队列模型的过程称为:出队列

数组

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

链表

链表的特点:
1.链表中的元素是在内存中不连续存储的,每个元素节点包含数据值如下一个元素的地址。
2.链表查询慢。无论查询那个数据都要从头开始找。
3.链表增删相对快。

在这里插入图片描述
在这里插入图片描述

链表的种类:
1.单向链表:

在这里插入图片描述
2.双向链表:
在这里插入图片描述

二叉树、二叉查找树

二叉树的概述:
在这里插入图片描述
二叉树的特点:
1.只能有一个根节点,每个节点最多支持2个直接子节点。
2.节点的度:节点的度:节点拥有的子树的个数,二叉树的度不大于2叶子节点度为0的节点,也称为终端结点。
3.高度:叶子节点的高度为1,叶子结点的父节点高度为2,以此类推,根节点的高度最高。
4.层:根节点在 第一层,以此类推。
5.兄弟节点:拥有共同父节点的节点互称为兄弟节点。

在这里插入图片描述
二叉查找树又称二叉排序树或者二叉搜索树。
在这里插入图片描述
特点:
1.每一个节点上最多有两个子节点。
2.左子树上 所有节点的值都小于根节点的值。
3.右子树上所有节点的值都大于根节点的值。
目的:提高检索数据的性能。
二叉查找树添节点:

在这里插入图片描述
将上面的节点按照二叉查找树的规则存入
规则:小的存左边、大的存右边、一样的不存。

在这里插入图片描述

平衡二叉树

二叉树查找存在的问题:
在这里插入图片描述
将上面的节点按照二叉查找树的规则存入
在这里插入图片描述
问题:出现瘸子现象,导致查询的性能与单链表一样,查询速度变慢!
平衡二叉树是在满足查找二叉树的大小规则下 ,让树尽可能矮小,以此提高查数据的性能。

在这里插入图片描述
平衡二叉树的要求:
任意节点的左右子树的高度差不超过1,任意节点的左右两个子树都是一颗平衡二叉树。
平衡二叉树在添加元素后可能导致不平衡:基本策略是进行左旋,或者右旋保证平衡,
1.平衡二叉树-左左
当根节点树的左子树有节点插入,导致二叉树不平衡。

在这里插入图片描述

在不添加元素这个二叉树是平衡二叉树,但是加上一个元素 如下:
在这里插入图片描述
这是左树高,导致不平衡,然后通过右旋从新得到平衡树。
在这里插入图片描述
2.平衡二叉树 -左右
当根节点左子树的右子树有节点插入,导致二叉树不平衡。

在这里插入图片描述
当前是平衡二叉树,当在左子树的右子树节点插入元素,如下:
在这里插入图片描述
这时左子树的右子树子节点导致树不平衡,为了达到平衡,先把出问题的节点向左旋。
在这里插入图片描述
然后再整体右旋这时就会形成新的平衡二叉树。
在这里插入图片描述

3.平衡二叉树-右右
当根节点右子树的右子树有节点插入,导致二叉树不平衡。

在这里插入图片描述
当前是平衡二叉树,当在右子树的右子树节点插入元素,如下:
在这里插入图片描述
这时右子树的右子树子节点导致树不平衡,为了达到平衡进行右旋,就可以从新达到平衡。
在这里插入图片描述
4.平衡二叉树-右左
当根节点右子树的左子树有节点插入,导致二叉树不平衡。

在这里插入图片描述
当前是平衡二叉树,当在右子树的左子树节点插入元素,如下:
在这里插入图片描述
这时右子树的左子树子节点导致树不平衡,为了达到平衡进行右旋,先把出问题的节点向右旋。

在这里插入图片描述
然后再整体左旋这时就会形成新的平衡二叉树。
在这里插入图片描述

红黑树

红黑树概述:
1.红黑树是一种自平衡的二叉查找树,是计算机科学中用到的一种数据结构。
2.每一个节点 可以是红或者黑;红黑树不是通过高度平衡的,它的平衡是通过”红黑规则“进行实现的。

在这里插入图片描述
红黑树规则:
1.每一个节点或是红色的,或者是黑色的,根节点必须是黑色。
2.如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,叶节点是黑色的。
3.如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)。
4.对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点。

在这里插入图片描述
添加节点
1.添加的节点的颜色,可以是红色的,也可以是黑色的。
2,默认用红色效率高。
默认用黑添加三个元素一共需要调整两次。

在这里插入图片描述
默认用红添加三个元素一共需要调整一次。
在这里插入图片描述

List系列集合

补充知识:集合的并发修改异常问题

List集合特点、特有API

List系列集合特点:
1.ArrayList、LinekdList:有序,可重复,有索引。
2.有序:存储和取出的元素顺序一致。
3.有索引:可以通过索引操作元素。
4.可重复:存储的元素可以重复。
List集合特有方法:
1.List集合因为支持索引,所以多了很多索引操作的独特api,其他Collection的功能List也都继承了。

在这里插入图片描述

package d5_collection_list;

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

public class ListDemo {
    public static void main(String[] args) {
        // 创建一个ArrayList集合对象 List:有序,可重复,有索引
        List<String> l = new ArrayList <>();
        l.add("java");
        l.add("java");
        l.add("MYSQL");
        l.add("MYSQL");
        // 在某个索引位置插入元素。
        l.add(2, "HTML");
        System.out.println(l); //[java, java, HTML, MYSQL, MYSQL]
        // 根据索引删除元素,返回删除元素
        System.out.println(l.remove(2)); //HTML
        System.out.println(l); //[java, java, MYSQL, MYSQL]
        // 根据索引获取元素
        System.out.println(l.get(2)); //MYSQL
        // 修改索引位置元素,返回修改前的数据
        System.out.println(l.set(1, "高斯林")); //java
        System.out.println(l); //[java, 高斯林, MYSQL, MYSQL]
    }
}

List集合的遍历方式

List集合的遍历方式有几种?
1.迭代器
2.增强for循环
3.Lambda表达式
4.for循环(因为List集合存在索引)

package d5_collection_list;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;

public class ListDemo1 {
    public static void main(String[] args) {
        // 创建一个ArrayList集合对象 List:有序,可重复,有索引
        List <String> l = new ArrayList <>();
        l.add("java1");
        l.add("java2");
        l.add("java3");

        // for循环
        for (int i = 0; i < l.size(); i++) {
            String  e = l.get(i);
            System.out.println(e);
        }

        // 迭代器
        Iterator<String> it = l.iterator();
        while (it.hasNext()){
            String e =  it.next();
            System.out.println(e);
        }

        // foreach
        for (String s : l) {
            System.out.println(s);
        }

        // Lambda表达式
        l.forEach(new Consumer <String>() {
            @Override
            public void accept(String s) {
                
            }
        });
        // 简化
        l.forEach(s-> System.out.println(s));
        //最终简化
        l.forEach(System.out::println);
    }
}

ArrayList集合的底层原理

1.ArrayList底层是基于数组实现的:根据索引定位元素快,增删需要做元素的位移操作。
2.第一次创建集合并添加第一个元素的时候,在底层创建一个默认长度为10的数组。

在这里插入图片描述
List集合存储的元素要超过容量怎么办?
在这里插入图片描述
ArrayList底层在新创建的集合中插入第一个元素的时候会默认创建长度为10的数组,当插入的元素长度为10的时候,ArrayList底层就会调用grow()进行扩容,然后长度扩到原来的1.5倍,也就是扩容后的长度为15,然后把原有的元素迁移到扩容后的新数组中继续进行后期操作。

LinkedList集合的底层原理

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

在这里插入图片描述

package d5_collection_list;

import java.util.LinkedList;

public class ListDemo2 {
    public static void main(String[] args) {
        // linkedList可以完成队列结构和栈结构(双链表)
        // 栈
        LinkedList<String> l = new LinkedList <>();
        // 入栈
        l.push("第1颗子弹");
        l.push("第2颗子弹");
        l.addFirst("第3颗子弹");
        l.addFirst("第4颗子弹");
        System.out.println(l);
        // 获取栈顶元素
        System.out.println(l.getFirst());
        System.out.println(l);
        // 出栈 弹栈
        System.out.println(l.pop());
        System.out.println(l.pop());
        System.out.println(l.removeFirst());
        System.out.println(l);

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

链表的种类:
在这里插入图片描述

补充知识:集合的并发修改异常问题

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

异常一:

package d6_collection_update;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        // 准备数据
        List<String> list = new ArrayList <>();
        list.add("snow");
        list.add("java");
        list.add("java");
        list.add("dream");
        list.add("A");
        list.add("B");
        System.out.println(list); // [snow, java, java, dream, A, B]
        // 删除全部的Java信息
        // 迭代器遍历对象
        Iterator <String> iterator = list.iterator();
        while (iterator.hasNext()){
            String e = iterator.next();
            if("java".equals(e)){
                list.remove(e);
            }
        }
        System.out.println(list);
    }
}

在这里插入图片描述
由于上面使用迭代器遍历删除的时候,在删除当前元素后,迭代器对象发生后移,然后导致并发异常,这样删除有风险。解决方法使用迭代器对象的remove方法即可,因为迭代器对象内部计数器也会随着remove元素的删除进行–操作 ,这样一来不会导致并发异常。

package d6_collection_update;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        // 准备数据
        List<String> list = new ArrayList <>();
        list.add("snow");
        list.add("java");
        list.add("java");
        list.add("dream");
        list.add("A");
        list.add("B");
        System.out.println(list); // [snow, java, java, dream, A, B]
        // 删除全部的Java信息
        // 迭代器遍历对象
        Iterator <String> iterator = list.iterator();
        while (iterator.hasNext()){
            String e = iterator.next();
            if("java".equals(e)){
                iterator.remove();
            }
        }
        System.out.println(list); //[snow, dream, A, B]
    }
}

异常一:

package d6_collection_update;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        // 准备数据
        List<String> list = new ArrayList <>();
        list.add("snow");
        list.add("java");
        list.add("java");
        list.add("dream");
        list.add("A");
        list.add("B");
        System.out.println(list); // [snow, java, java, dream, A, B]
        // 删除全部的Java信息
        // foreach遍历删除
        for (String s : list) {
            if("java".equals(s)) {
                list.remove(s);
            }
        }
    }
}

同样这样用foreach遍历删除也会发生并发异常,并且删除元素后计数器还是会后移,内部没有 提供相应的相对移动措施,所以不能用该方法。
在这里插入图片描述
同样Lambda表达式也会出现该问题,因为底层用的就是foreach,也无法避免。

package d6_collection_update;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;

public class Test {
    public static void main(String[] args) {
        // 准备数据
        List<String> list = new ArrayList <>();
        list.add("snow");
        list.add("java");
        list.add("java");
        list.add("dream");
        list.add("A");
        list.add("B");
        System.out.println(list); // [snow, java, java, dream, A, B]
        // 删除全部的Java信息
        // Lambda表达式
        list.forEach(s-> {
                if("java".equals(s)){
                    list.remove(s);
                }
        });
    }
}

接下来用for循环删除,我们发现不报错但是还会因为移位问题发生漏删。

package d6_collection_update;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;

public class Test {
    public static void main(String[] args) {
        // 准备数据
        List<String> list = new ArrayList <>();
        list.add("snow");
        list.add("java");
        list.add("java");
        list.add("dream");
        list.add("A");
        list.add("B");
        System.out.println(list); // [snow, java, java, dream, A, B]
        // 删除全部的Java信息
        // for循环删除
        for (int i = 0; i < list.size(); i++) {
            String e = list.get(i);
            if("java".equals(e)){
                list.remove(e);
            }
        }
        System.out.println(list);
    }
}

为了解决这个漏删的问题,选择通过for循环从后面对集合进行遍历删除,因为后面的都扫描完成当元素删除后,后面的元素补上来并不影响指针倒着往前扫描。

package d6_collection_update;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;

public class Test {
    public static void main(String[] args) {
        // 准备数据
        List<String> list = new ArrayList <>();
        list.add("snow");
        list.add("java");
        list.add("java");
        list.add("dream");
        list.add("A");
        list.add("B");
        System.out.println(list); // [snow, java, java, dream, A, B]
        // 删除全部的Java信息
        // for循环
        for (int i = list.size()-1; i >= 0; i--) {
            String e = list.get(i);
            if("java".equals(e)){
                list.remove(e);
            }
        }
        System.out.println(list);
    }
}

这样就可以解决漏删问题。解决本问题还有一种方法就是,在for删除元素的时候,让指针也随着删除的元素回退一下,也可以解决该问题,如下所示:

package d6_collection_update;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;

public class Test {
    public static void main(String[] args) {
        // 准备数据
        List<String> list = new ArrayList <>();
        list.add("snow");
        list.add("java");
        list.add("java");
        list.add("dream");
        list.add("A");
        list.add("B");
        System.out.println(list); // [snow, java, java, dream, A, B]
        // 删除全部的Java信息
        // for循环
        for (int i = 0; i < list.size(); i++) {
            String e = list.get(i);
            if("java".equals(e)){
                list.remove(e);
                i--;
            }
        }
        System.out.println(list);
    }
}

补充知识:泛型深入

泛型的概述和优势

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

泛型的好处:
1.统一数据类型。
2.把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为编译阶段类型就能确定下来。

泛型可以在很多地方进行定义:
类后面 ——> 泛型类
方法申明上 ——> 泛型方法
接口后面 ——> 泛型接口

自定义泛型类

1.定义类时同时定义了泛型的类就是泛型类。
2.泛型类的格式:

修饰符 class 类名<泛型变量>{}
public class MyArrayList<T>{}

3.此处泛型变量T可以随便写为任意标识,常见的如E、T、K、V等
4.作用:编译阶段可以指定数据类型,类似于集合的作用 。
例:模拟ArrayList集合定义一个集合MyArrayList集合,完成添加和删除功能的泛型设计即可。
MyArrayList.java泛型类:

package d8_genricity;
import java.util.ArrayList;

public class MyArrayList <E>{
    private ArrayList list = new ArrayList();
    public void add(E e){
        list.add(e);
    }
    public void remove(E e){
        list.remove(e);
    }
    @Override
    public String toString() {
        return list.toString();
    }
}

Test.java测试类:

package d8_genricity;

public class Test {
    public static void main(String[] args) {
        MyArrayList<String> m = new MyArrayList <>();
        m.add("java1");
        m.add("java2");
        m.add("java3");
        m.remove("java1");
        System.out.println(m);
    }
}

泛型类的原理:
把出现泛型变量的地方全部替换成传输的真实数据类型。

自定义泛型方法

泛型方法的概述:
1.定义方法时同时定义了泛型的方法就是泛型方法。
2.泛型方法的格式:

修饰符 <泛型变量> 方法返回值 方法名称(形参列表){}
public <T> void show(T t){}

3.作用:方法中可以使用泛型接受一切实际类型的参数,方法更具有通用性。
例:给你任何一个类型的数组,都能返回它的内容。也就是实现Arrays.toString(数组)的功能!

package d9_genericity_method;

public class GenericDemo {
    public static void main(String[] args) {
        String [] name = {"A","C","B"};
        printArray(name);
        Integer [] ages = {10,20,30};
        printArray(ages);
    }
    protected 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);
        }
    }
}

泛型方法的原理:
把出现泛型变量的地方全部替换成传输的真实数据类型。

自定义泛型接口

泛型接口的概述
1.使用了泛型定义的接口就是泛型接口。
2.泛型接口的格式:

修饰符 interface 接口名称<泛型变量>{}
public interface Data <E> {}

3.作用:泛型接口可以让实现类选择当前功能需要操作的数据类型
例:教务系统,提供一个接口可约束一定要完成数据(学生,老师)的增删改查操作。

泛型接口的原理:
实现类可以在实现接口的时候传入自己操作的数据类型,这样重写的方法都将是针对于该类型的操作。

在这里插入图片描述

泛型通配符、上下限

通配符:?
1.?可以在"使用泛型"的时候代表一切类型。
2.E T K V是定义泛型的时候使用的。
案例:开发一个极品飞车的游戏,所有的汽车都能一起参与比赛。

package d11_genercity_limit;
import java.util.ArrayList;

public class GenericDemo {
    public static void main(String[] args) {
        ArrayList<BENZ> bmws = new ArrayList <>();
        bmws.add(new BENZ());
        bmws.add(new BENZ());
        bmws.add(new BENZ());
        go(bmws);
        
        ArrayList<BMW> bmns = new ArrayList <>();
        bmns.add(new BMW());
        bmns.add(new BMW());
        bmns.add(new BMW());
        go(bmns);
    }
    // 所有车比赛
    public static void go(ArrayList<Car> cars){

    }
}

class BENZ extends Car{

}

class BMW extends Car{

}

class Car{

}

注意:通过上面发现BMW和BENZ都继承了Car但是ArrayList和ArrayList与ArrayList没有关系的!为了解决这个问题可以选用通配符。

package d11_genercity_limit;
import java.util.ArrayList;

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

        ArrayList<BMW> bmns = new ArrayList <>();
        bmns.add(new BMW());
        bmns.add(new BMW());
        bmns.add(new BMW());
        go(bmns);

        ArrayList<Dog> dogs = new ArrayList <>();
        dogs.add(new Dog());
        dogs.add(new Dog());
        dogs.add(new Dog());
        go(dogs);
    }
    // 所有车比赛
    public static void go(ArrayList<?> cars){

    }
}
class Dog{

}

class BENZ extends Car{

}

class BMW extends Car{

}

class Car{

}

当选用通配符可以把Car的子类BENZ和子类BMW可以进行比赛,但是我们发现不是Car的子类也能进入比赛,为了解决这个问题,引入了泛型的上下限:
?extends Car: ?必须是Car或者其子类 泛型上限
?super Car: ?必须是Car或者其父类 泛型下限

package d11_genercity_limit;
import java.util.ArrayList;

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

        ArrayList<BMW> bmns = new ArrayList <>();
        bmns.add(new BMW());
        bmns.add(new BMW());
        bmns.add(new BMW());
        go(bmns);

        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{

}

这是通过泛型的上下限可以完美解决这个问题,让Car的子类进行比赛。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/81969.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PDF格式如何转成Excel?这篇文章教会你如何转换

不知道在日常的办公中&#xff0c;大家有没有碰到过这样一个问题&#xff1a;当领导给你发个PDF文件后&#xff0c;要求你尽快的将PDF文件中的表格数据进行修改&#xff0c;但当你把PDF转换成Excel格式后&#xff0c;发现转换后的文件排版格式错乱的。可是截止时间的压迫和乱七…

[附源码]Python计算机毕业设计大学生日常行为评分管理系统Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

Python输出文字改变颜色

# Python输出文字改变颜色的方法 一、使用注释性输出 &#xff08;一&#xff09;、基本语法 \033[显示方式; 前景色; 背景色m******\033[0m 显示方式、前景色、背景色都为可选参数&#xff0c;选择自己需要的即可&#xff0c;而且顺序可变非固定&#xff0c;但建议按照默认顺…

Openlayers 自定义投影坐标系数据转换以及在线转换工具

Openlayers 自定义投影坐标系数据转换以及在线转换工具OpenLayers 教程查看 EPSG 码和定义Openlayers 自定义投影坐标系数据转换以及在线转换工具在线示例OpenLayers 教程 工作中经常会遇到转换坐标的情况&#xff0c;一般都会写代码搞定&#xff0c;但是有时候只需要查看一下…

web前端期末大作业:基于HTML+CSS+JavaScript汽车租赁网站(47页)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

ELK 企业级日志分析系统

一、ELK 概述 1、ELK 简介 ELK平台是一套完整的日志集中处理解决方案&#xff0c;将 ElasticSearch、Logstash 和 Kiabana 三个开源工具配合使用&#xff0c; 完成更强大的用户对日志的查询、排序、统计需求。 ●ElasticSearch&#xff1a;是基于Lucene&#xff08;一个全文检…

[附源码]Python计算机毕业设计Django安防管理平台

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

CleanMyMacX软件怎么样?实际使用效果功能讲解

如果你也对MAC系统的优化和文件管理摸不清头脑&#xff0c;不知道该如何清理垃圾和缓存文件。那你可以试试我最近发现的这款软件——CleanMyMac X。 这是一款MAC OS的老牌清理软件&#xff0c;集系统的清理、提速、保护和软件的卸载于一体,由知名软件开发商MACPaw开发&#xf…

未来五年,人类和数字化劳动力混合的员工队伍将变得非常普遍

从机器管家安德鲁&#xff0c;到钢铁侠的贾维斯&#xff0c;无论是实体智能机器人&#xff0c;还是人工智能系统&#xff0c;人们对于机器人助手的想象从未停止过。 虽然&#xff0c;人类和机器人助手一起工作&#xff0c;听上去很科幻&#xff0c;但这一情况如今已比想象的更…

SpringBoot整合邮件服务(QQ邮箱)

文章目录SpringBoot整合邮件服务配置选择账户点击开启SMTP服务&#xff1a;发送短信&#xff1a;发送完&#xff0c;点击我已发送&#xff0c;然后得到密码&#xff1a;POM依赖&#xff1a;application.ymlJava集成EmailService在controller里定义接口&#xff1a;在业务实现层…

继承——C++第二大特性

目录 一、概念及定义 1、概念 2、定义 &#xff08;2&#xff09;方式 &#xff08;3&#xff09;继承基类成员访问方式的变化 二、父类子类赋值转换 三、继承中的作用域 四、派生类的默认成员函数 六、继承与静态成员 七、复杂的菱形继承及菱形虚拟继承 八、归纳 一、…

C语言——每周刷题题集(第一周)

前言 做题可以更好地巩固所学知识&#xff0c;并加深对于知识点的理解。下面题目均来自牛客网入门编程练习题。 第一题: BC8 十六进制转十进制 描述&#xff1a; BoBo写了一个十六进制整数ABCDEF&#xff0c;他问KiKi对应的十进制整数是多少。 输入描述&#xff1a; 无 …

基于自注意力的生成对抗归因网络的交通流缺失数据修复

文章信息《Missing Data Repairs for Traffic Flow With Self-Attention Generative Adversarial Imputation Net》是2022年7月发表在期刊IEEE TRANSACTIONS ON INTELLIGENT TRANSPORTA TION SYSTEMS上的一篇文章。摘要随着传感器技术的快速发展&#xff0c;由多个空间分布的传…

2023我为什么建议你学Python?

前言 最近有一位读者准备实习开始找工作&#xff0c;来跟我交流他目前找工作遇到的一些情况&#xff1a; “我被找工作弄得满头包&#xff0c;觉得好像自己学的专业很没有市场&#xff0c;也没有很好的大公司背书&#xff0c;根本没有和 HR 谈薪资的底气。 他最近撒简历发现…

Java IO流

一、IO的概念 Java IO&#xff1a;Java IO即Java 输入输出系统。不管我们编写何种应用&#xff0c;都难免和各种输入输出相关的媒介打交道&#xff0c;其实和媒介进行IO的过程是十分复杂的&#xff0c;这要考虑的因素特别多&#xff0c;比如我们要考虑和哪种媒介进行IO&#x…

react进阶用法完全指南

React调用回调函数&#xff0c;正确设置this指向的三种方法 通过bind this.increment this.increment.bind(this);通过箭头函数 <button onClick{this.multi}>点我*10</button> multi () > {this.setState({count: this.state.count * 10}) }箭头函数包裹 …

[附源码]Python计算机毕业设计大学生学科竞赛管理系统Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

【特征选择】时变正弦和 V 形传递函数 (BMPA-TVSinV) 的新型二元海洋捕食者算法附matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

CSC7136B

CSC7136B是一款高效率低待机功耗原边反馈小功率 电源 AC/DC 驱动 电路&#xff0c;无 需光耦、TL431 及相关器件。CSC7136B采用开关频率调制和初级电流峰值振幅&#xff08; FM 和 AM &#xff09;多模式工作技术&#xff0c;保证了全负载和线性范围内的较高的转换效率。恒压模…

无法安装人脸检测dlib库的解决方法

1. 引言 dlib 库是一个用来人脸关键点检测的 Python 库&#xff0c;但因为其是 C 编写&#xff08;或需要 C编译&#xff1f;&#xff09;&#xff0c;使得在安装时可能会遇到各种各样问题。笔者在安装时遇到问题后&#xff0c;搜索了一些博客&#xff0c;看到了一些解决方法&…