Java笔记_12(集合进阶)

news2024/12/24 12:00:29

Java笔记_12

  • 一、集合的体系结构
  • 二、Collection
    • 2.1、迭代器遍历
    • 2.2、增强for遍历
    • 2.3、Lambda表达式遍历
  • 三、list集合
    • 3.1、List集合的特有方法
    • 3.2、 List集合的遍历方式
  • 四、数据结构
    • 4.1、数据结构概述
    • 4.2、栈
    • 4.3、队列
    • 4.4、数组
    • 4.5、链表
    • 4.6、树
      • 二叉查找树
      • 平衡二叉树
    • 4.7、红黑树
  • 五、底层原理
    • 5.1、ArrayList集合底层原理
    • 5.2、LinkedList底层源码
    • 5.3、迭代器的底层源码
  • 六、泛型
    • 6.1、泛型深入
    • 6.2、泛型方法
    • 6.3、泛型接口
    • 6.4、泛型的继承和通配符
  • 七、Set集合
    • 7.1、HashSet
    • 7.2、HashSet案例
    • 7.3、LinkedHashSet
    • 7.4、TreeSet

一、集合的体系结构

在这里插入图片描述

二、Collection

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

Collection

  • Collection是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的。
方法名称说明
public boolean add(E e)把给定的对象添加到当前集合中
public void clear()清空集合中所有的元素
public boolean remove(E e)把给定的对象在当前集合中删除
public boolean contains(Object obj)判断当前集合中是否包含给定的对象
public boolean isEmpty()判断当前集合是否为空
public int size()返回集合中元素的个数/集合的长度

注意点:
Collection是一个接口,我们不能直接创建他的对象。所以,现在我们学习他的方法时,只能创建他实现类的对象。
实现类: ArrayList

  • add添加元素

    • 细节1:如果我们要往List系列集合中添加数据,那么方法永远返回true,因为List系列的是允许元素重复的。
    • 细节2:如果我们要往Set系列集合中添加数据,如果当前要添加元素不存在,方法返回true,表示添加成功。
      如果当前要添加的元素已经存在,方法返回false,表示添加失败。(Set系列集合不允许重复)
  • remove删除

    • 细节1:因为Collection里面定义的是共性的方法,所以此时不能通过索引进行删除。只能通过元素的对象进行别除。
    • 细节2:方法会有一个布尔类型的返回值,删除成功返回true,删除失败返回false
    • 如果要删除的元素不存在,就会删除失败。
  • contains判断包含

    • 细节:底层是依赖equals方法进行判断是否存在的。
    • 所以,如果集合中存储的是自定义对象,也想通过contains方法来判断是否包含,那么在javabean类中,一定要重写equals方法。

2.1、迭代器遍历

迭代器遍历
迭代器在Java中的类是lterator,迭代器是集合专用的遍历方式。

Collection集合获取迭代器

方法名称说明
Iterator<E> iterator()返回迭代器对象,默认指向当前集合的0索引

lterator中的常用方法

方法名称说明
boolean hasNext()判断当前位置是否有元素,有元素返回true ,没有元素返回false
E next()获取当前位置的元素,并将迭代器对象移向下一个位置。

在这里插入图片描述

在这里插入图片描述

package CollectionDome;

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

public class lteratorDome1 {
    public static void main(String[] args) {
        //1.创建集合对象
        Collection<String> coll = new ArrayList<>();
        //2.添加集合成员
        coll.add("asd");
        coll.add("qwe");
        coll.add("aaa");

        //3.创建迭代器对象
        Iterator it = coll.iterator();
        //利用循环获取里面的数据
        while (it.hasNext()){
            //next的两个作用:
            // 1.获取元素
            // 2.指针到下一个元素
            Object next = it.next();
            System.out.println(next);
        }
    }
}

细节注意点:

  1. 报错NoSuchElementException
  2. 迭代器遍历完毕,指针不会复位
  3. 循环中只能用一次next方法
  4. 迭代器遍历时,不能用集合的方法进行增加或者删除,使用迭代器名.remove()的方法移除

2.2、增强for遍历

  • 增强for的底层就是迭代器,为了简化迭代器的代码书写的
  • 它是JDK5之后出现的,其内部原理就是一个lterator迭代器
  • 所有的单列集合数组才能用增强for进行遍历。

格式

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

}
//例:
for(String s : list){
	System.out.println(s);
}
package CollectionDome;

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

public class Dome3 {
    public static void main(String[] args) {
        Collection<String> coll = new ArrayList<>();

        coll.add("asd");
        coll.add("aaa");
        coll.add("qweqe");
        coll.add("ssss");
        coll.add("dddd");
        //s为一个第三方变量,不改变集合的值
        for (String s:coll){
            System.out.println(s);
        }
    }
}

2.3、Lambda表达式遍历

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

方法名说明
default void forEach(Consumer<? super T> action):结合lambda遍历集合
package CollectionDome;

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

public class Dome3 {
    public static void main(String[] args) {
        Collection<String> coll = new ArrayList<>();

        coll.add("asd");
        coll.add("aaa");
        coll.add("qweqe");
        coll.add("ssss");
        coll.add("dddd");
        //s为一个第三方变量
        //匿名内部类
        coll.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
        //lambda表达式
        coll.forEach(s->System.out.println(s));
    }
}
  • 三种通用的遍历方式:
    • 迭代器:在遍历的过程中需要删除元素,请使用迭代器。
    • 增强for、Lambda:
      仅仅想遍历,那么使用增强for或Lambda表达式。

三、list集合

在这里插入图片描述
List集合特点

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

3.1、List集合的特有方法

  • Collection的方法List都继承了
  • List集合因为有索引,所以多了很多索引操作的方法
方法名称说明
void add(int index,E element)在此集合中的指定位置插入指定的元素
E remove(int index)删除指定索引处的元素,返回被删除的元素
E set(int index,E element)修改指定索引处的元素,返回被修改的元素
E get(int index)返回指定索引处的元素
  • add添加
    • 细节: 原来索引上的元素会依次往后移
  • remove删除
    • 细节:当集合元素和下标相同时,会优先删除下标表示的成员。因为在调用方法的时候,如果方法出现了重载现象优先调用,实参跟形参类型一致的那个方法。
package CollectionDome;

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

public class Dome4 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        //添加元素
        list.add("aaa");
        list.add("bbb");
        list.add("ddd");
        list.add("sss");
        //list系列中的add方法
        list.add(1,"qqq");
        System.out.println(list);
        //删除元素(使用元素值)
        list.remove("aaa");
        System.out.println(list);
        //删除元素(使用下标)
        list.remove(1);
        System.out.println(list);
        //创建一个新的元素
        list.set(1,"asd");
        System.out.println(list);
        //得到该索引的元素
        System.out.println(list.get(1));
    }
}

3.2、 List集合的遍历方式

五种迭代方式:

  1. 迭代器遍历
  2. 列表迭代器遍历
  3. 增强for遍历
  4. Lambda表达式遍历
  5. 不同for循环(因为List集合存在索引)
  • 列表迭代器遍历
package CollectionDome;

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

public class Dome5 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        //添加元素
        list.add("aaa");
        list.add("bbb");
        list.add("ddd");
        list.add("sss");
        //列表迭代器
        //获取一个列表迭代器的对象,里面的指针默认也是指向0索引的

        //额外添加了一个方法:在遍历的过程中,可以添加元素
        ListIterator<String> it = list.listIterator();
        while (it.hasNext()){
            String str = it.next();
            if ("bbb".equals(str)){
                it.add("qqq");
            }
        }
        System.out.println(list);
    }
}

四、数据结构

4.1、数据结构概述

数据结构是计算机底层存储、组织数据的方式。
是指数据相互之间是以什么方式排列在一起的。
数据结构是为了更加方便的管理和使用数据,需要结合具体的业务场景来进行选择。
一般情况下,精心选择的数据结构可以带来更高的运行或者存储效率。

4.2、栈

栈的特点:后进先出,先进后出

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

4.3、队列

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

数据从端进入队列模型的过程称为:入队列
数据从端离开队列模型的过程称为:出队列

在这里插入图片描述

4.4、数组

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

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

4.5、链表

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

  • 链表中的结点是独立的对象,在内存中是不连续的,每个结点包含数据值和下一个结点的地址。
  • 链表查询慢,无论查询哪个数据都要从头开始找
  • 链表增删相对快

在这里插入图片描述
各种数据结构的特点和作用是什么样的

  • :后进先出,先进后出。
  • 队列:先进先出,后进后出。
  • 数组:内存连续区域,查询快,增删慢。
  • 链表:元素是游离的,查询慢,首尾操作极快。

4.6、树

  • 度:每个节点的子节点数量
    • 二叉树中,任意节点的度<=2
  • 树高:树的总层数
  • 根节点:最顶层的节点
  • 左子节点:左下方的节点
  • 右子节点:右下方的节点
  • 根节点的左子树:蓝色虚线
  • 根节点的右子树:绿色虚线

在这里插入图片描述

一个节点上的结构
在这里插入图片描述

二叉查找树

二叉查找树,又称二叉排序树或者二叉搜索树

特点:

  • 每一个节点上最多有两个子节点
  • 任意节点左子树上的值都小于当前节点
  • 任意节点右子树上的值都大于当前节点

添加节点

  1. 小的存左边
  2. 大的存右边
  3. 一样的不存
    在这里插入图片描述
    遍历方式
  • 前序遍历:当前节点,左子节点,右子结点
  • 中序遍历:左子节点,当前节点,右子结点
  • 后序遍历:左子节点,右子结点,当前节点
  • 层序遍历:一层一层的去遍历

平衡二叉树

规则:任意节点左右子树高度差不超过1

平衡二叉树的旋转机制
规则1:左旋
规则2:右旋
触发时机:当添加一个节点之后,该树不再是一颗平衡二叉树

  • 左(右)旋步骤:

    1. 确定支点:从添加的节点开始,不断的往父节点找不平衡的节点
    2. 以不平衡点作为支点
    3. 将跟节点的右侧往左(右)拉
    4. 原先的右(左)子节点变成新的父节点,并把多余的左(右)子节点出让,给已经降级的根节点当右(左)子节点
  • 数据结构(平衡二叉树)需要旋转的四种情况

    1. 左左:当根节点左子树的左子树有节点插入,导致二叉树不平衡
      • 一次右旋
    2. 左右:当根节点左子树的右子树有节点插入,导致二叉树不平衡
      • 先局部左旋,再整体右旋(先变成左左型,再变成平衡二叉树)
    3. 右右:当根节点右子树的右子树有节点插入,导致二叉树不平衡
      • 一次左旋
    4. 右左:当根节点右子树的左子树有节点插入,导致二叉树不平衡
      • 先局部右旋,再整体左旋(先变成右右型,再变成平衡二叉树)

4.7、红黑树

  • 红黑树是一种自平衡的二叉查找树,是计算机科学中用到的一种数据结构。
  • 1972年出现,当时被称之为平衡二叉B树。后来,1978年被修改为如今的"红黑树"。
  • 它是一种特殊的二叉查找树,红黑树的每一个节点上都有存储位表示节点的颜色,
  • 每一个节点可以是红或者黑,红黑树不是高度平衡的,它的平衡是通过"红黑规则"进行实现的
    在这里插入图片描述
  • 平衡二叉树
    • 高度平衡
    • 当左右子树高度差超过1时,通过旋转保持平衡
  • 红黑树
    • 是一个二叉查找树
    • 但是不是高度平衡的
    • 条件 :特有的红黑规则

数据结构(红黑树)红黑规则

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

在这里插入图片描述
添加节点的规则

  • 红黑树在添加节点的时候,添加的节点默认是红色的
    在这里插入图片描述

五、底层原理

5.1、ArrayList集合底层原理

  1. 利用空参创建的集合,在底层创建一个默认长度为0的数组
  2. 添加第一个元素时,底层会创建一个新的长度为10的数组
  3. 存满时,会扩容1.5倍
  4. 如果一次添加多个元素,1.5倍还放不下,则新创建数组的长度以实际为准
    在这里插入图片描述
    在这里插入图片描述

5.2、LinkedList底层源码

  • 底层数据结构是双链表,查询慢,增删快,但是如果操作的是首尾元素,速度也是极快的。
  • LinkedList本身多了很多直接操作首尾元素的特有API。

在这里插入图片描述

特有方法说明
public void addFirst(E e)在该列表开头插入指定的元素
public void addLlst(E e)将指定的元素追加到此列表的末尾
public E getFirst()返回此列表中的第一个元素
public E getLast()返回此列表中的最后一个元素
public E removeFirst()从此列表中删除并返回第一个元素
public E removeLast()从此列表中删除并返回最后一个元素

添加第一个链表元素时:
在这里插入图片描述
添加多个链表元素时:
在这里插入图片描述

5.3、迭代器的底层源码

在这里插入图片描述

六、泛型

6.1、泛型深入

  • 泛型:是JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查。
  • 泛型的格式:<数据类型>
  • 注意:泛型只能支持引用数据类型。

如果我们没有给集合指定类型,默认认为所有的数据类型都是object类型
此时可以往集合添加任意的数据类型。
带来一个坏处:我们在获取数据的时候,无法使用他的特有行为。

泛型的好处

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

扩展知识点:Java中的泛型是伪泛型

泛型的细节:

  • 泛型中不能写基本数据类型
  • 指定泛型的具体类型后,传递数据时,可以传入该类类型或者其子类类型
  • 如果不写泛型,类型默认事Object

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

使用场景:当一个类中,某个变量的数据类型不确定时,就可以定义带有泛型的类

在这里插入图片描述

此处E可以理解为变量,但是不是用来记录数据的,而是记录数据的类型,可以写成:T、E、K、V等

package CollectionDome.Generics_Dome;

import java.util.Arrays;
//编写一个类的时候,如果不确定类型,那么这个类就可以定义为E
public class MyArrayList<E> {
    int size;
    Object[] obj = new Object[10];

    /*
    * E:表示不确定的类型,该类型在类名后面已经定义过了
    * e:形参的名字,变量名
    * */

    public boolean add(E e){
        obj[size] = e;
        size++;
        return true;
    }

    public E get(int index){return (E)obj[index];}

    @Override
    public String toString(){
        return Arrays.toString(obj);
    }
}

package CollectionDome.Generics_Dome;

import java.sql.SQLOutput;

public class Test {
    public static void main(String[] args) {
        MyArrayList<String> m1 = new MyArrayList<>();

        m1.add("aaa");
        m1.add("sss");
        m1.add("ddd");

        System.out.println(m1.get(1));

    }
}

6.2、泛型方法

方法中形参类型不确定时

  1. 使用类名后面定义的泛型——所有方法都能使用
  2. 在方法申明上定义自己的泛型——只有本方法能用

在这里插入图片描述

此处T可以理解为变量,但是不是用来记录数据的,而是记录类型的,可以写成:T、E、K、V等

package CollectionDome.Generics_Dome2;

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

public class ListUtil {
    private ListUtil(){};

    public static<E> void addAll(ArrayList<E> list,E e1, E e2,E e3,E e4,E e5){
        list.add(e1);
        list.add(e2);
        list.add(e3);
        list.add(e4);
    }
    public static<E> void addAll2(ArrayList<E> list,E ...e){
        for (E element:e){
            list.add(element);
        }
    }

}

package CollectionDome.Generics_Dome2;

import java.util.ArrayList;

public class Test {
    public static void main(String[] args) {
        ArrayList<String> a1 = new ArrayList<>();
        ListUtil.addAll(a1,"aaa","sss","cccc","ass","ccsc");
        System.out.println(a1);

        ArrayList<Integer> a2 = new ArrayList<>();
        ListUtil.addAll(a2,111,222,12321,232,545);
        System.out.println(a2);


        ArrayList<Integer> a3 = new ArrayList<>();
        ListUtil.addAll2(a3,111,222,12321,232,545,1121,3232,12321321);
        System.out.println(a3);
    }
}

6.3、泛型接口

在这里插入图片描述
重点:如何使用一个带泛型的接口

方式1:实现类给出具体类型

方式2:实现类延续泛型,创建对象时再确定

方式一:

package CollectionDome.Generics_Dome3;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

public class MyArraysList1 implements List<String> {

    @Override
    public int size() {
        return 0;
    }

    @Override
    public boolean isEmpty() {
        return false;
    }

    @Override
    public boolean contains(Object o) {
        return false;
    }

    @Override
    public Iterator<String> iterator() {
        return null;
    }

    @Override
    public Object[] toArray() {
        return new Object[0];
    }

    @Override
    public <T> T[] toArray(T[] a) {
        return null;
    }

    @Override
    public boolean add(String s) {
        return false;
    }

    @Override
    public boolean remove(Object o) {
        return false;
    }

    @Override
    public boolean containsAll(Collection<?> c) {
        return false;
    }

    @Override
    public boolean addAll(Collection<? extends String> c) {
        return false;
    }

    @Override
    public boolean addAll(int index, Collection<? extends String> c) {
        return false;
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        return false;
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        return false;
    }

    @Override
    public void clear() {

    }

    @Override
    public String get(int index) {
        return null;
    }

    @Override
    public String set(int index, String element) {
        return null;
    }

    @Override
    public void add(int index, String element) {

    }

    @Override
    public String remove(int index) {
        return null;
    }

    @Override
    public int indexOf(Object o) {
        return 0;
    }

    @Override
    public int lastIndexOf(Object o) {
        return 0;
    }

    @Override
    public ListIterator<String> listIterator() {
        return null;
    }

    @Override
    public ListIterator<String> listIterator(int index) {
        return null;
    }

    @Override
    public List<String> subList(int fromIndex, int toIndex) {
        return null;
    }
}

方式二:

package CollectionDome.Generics_Dome3;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

public class MyArrayList<E> implements List<E> {

    @Override
    public int size() {
        return 0;
    }

    @Override
    public boolean isEmpty() {
        return false;
    }

    @Override
    public boolean contains(Object o) {
        return false;
    }

    @Override
    public Iterator<E> iterator() {
        return null;
    }

    @Override
    public Object[] toArray() {
        return new Object[0];
    }

    @Override
    public <T> T[] toArray(T[] a) {
        return null;
    }

    @Override
    public boolean add(E e) {
        return false;
    }

    @Override
    public boolean remove(Object o) {
        return false;
    }

    @Override
    public boolean containsAll(Collection<?> c) {
        return false;
    }

    @Override
    public boolean addAll(Collection<? extends E> c) {
        return false;
    }

    @Override
    public boolean addAll(int index, Collection<? extends E> c) {
        return false;
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        return false;
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        return false;
    }

    @Override
    public void clear() {

    }

    @Override
    public E get(int index) {
        return null;
    }

    @Override
    public E set(int index, E element) {
        return null;
    }

    @Override
    public void add(int index, E element) {

    }

    @Override
    public E remove(int index) {
        return null;
    }

    @Override
    public int indexOf(Object o) {
        return 0;
    }

    @Override
    public int lastIndexOf(Object o) {
        return 0;
    }

    @Override
    public ListIterator<E> listIterator() {
        return null;
    }

    @Override
    public ListIterator<E> listIterator(int index) {
        return null;
    }

    @Override
    public List<E> subList(int fromIndex, int toIndex) {
        return null;
    }
}

6.4、泛型的继承和通配符

泛型的通配符:?

  • ?也表示不确定的类型他可以进行类型的限定
  • ? extends E:表示可以传递E或者E所有的子类类型
  • ? super E:表示可以传递E或者E所有的父类类型
package CollectionDome.Generics_Dome4;

import java.util.ArrayList;

public class test {
    public static void main(String[] args) {
        ArrayList<ye> l1 = new ArrayList<>();
        ArrayList<fu> l2 = new ArrayList<>();
        ArrayList<zi> l3 = new ArrayList<>();
        ArrayList<student> l4 = new ArrayList<>();
        
        method(l1);
        method(l2);
        method(l3);
        //由于student不是继承于ye类所以不能使用该方法
        //method(l4);
        
    }
    /*
    * 此时,泛型里面写的是什么类型,那么只能传递什么类型的数据。
    * 弊端:
        利用泛型方法有一个小弊端,此时他可以接受任意的数据类型
    *      Ye Fu zi student
       希望:本方法虽然不确定类型,但是以后我希望只能传递Ye Fu Zi
    此时我们就可以使用泛型的通配符:?
        ?也表示不确定的类型
        他可以进行类型的限定
        ? extends E:表示可以传递E或者E所有的子类类型
        ? super E:表示可以传递E或者E所有的父类类型
    应用场景:
        1.如果我们在定义类、方法、接口的时候,如果类型不确定,就可以定义泛型类、泛型方法、泛型接口。
        2.如果类型不确定,但是能知道以后只能传递某个继承体系中的,就可以泛型的通配符
    泛型的通配符:
        关键点:可以限定类型的范围

    * */
    public static void method(ArrayList<? extends ye> list){

    }
    static class ye{ }
    static class fu extends ye{}
    static class zi extends fu{}
    
    static class student{}
}

在这里插入图片描述

package CollectionDome.Generics_Dome5;

import java.beans.beancontext.BeanContextServiceProviderBeanInfo;
import java.util.ArrayList;

public class test {
    public static void main(String[] args) {
        ArrayList<Dog_taidi> taidis = new ArrayList<>();
        ArrayList<Dog_hashiqi> hashiqis = new ArrayList<>();
        ArrayList<Cat_bosi> bosi = new ArrayList<>();
        ArrayList<Cat_lihua> lihua = new ArrayList<>();

        Dog_taidi t1 = new Dog_taidi("泰泰",1);
        Dog_taidi t2 = new Dog_taidi("迪迪",2);

        Dog_hashiqi h1 = new Dog_hashiqi("哈哈",1);
        Dog_hashiqi h2 = new Dog_hashiqi("时时",2);

        Cat_bosi b1 = new Cat_bosi("波波",1);
        Cat_bosi b2 = new Cat_bosi("思思",2);

        Cat_lihua l1 = new Cat_lihua("梨梨",1);
        Cat_lihua l2 = new Cat_lihua("花花",2);



        taidis.add(t1);
        taidis.add(t2);

        hashiqis.add(h1);
        hashiqis.add(h2);

        bosi.add(b1);
        bosi.add(b2);

        lihua.add(l1);
        lihua.add(l2);

        KeepPet.keepPetDog(taidis);
        KeepPet.keepPetDog(hashiqis);
        KeepPet.keepPetAnimal(taidis);
        KeepPet.keepPetCat(bosi);
        KeepPet.keepPetCat(lihua);


    }
}
package CollectionDome.Generics_Dome5;

import java.util.ArrayList;

public class KeepPet {
    private KeepPet(){};

    public static void keepPetAnimal(ArrayList<? extends Animal> list){
        for (Animal a:list){
            a.eat();
        }
    }
    public static void keepPetDog(ArrayList<? extends Dog> list){
        for (Dog d :list){
            d.eat();
        }
    }
    public static void keepPetCat(ArrayList<? extends Cat> list){
        for (Cat c:list){
            c.eat();
        }
    }
}
package CollectionDome.Generics_Dome5;

public abstract class Animal {
    private String name;
    private int age;

    public Animal() {
    }

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

    abstract void eat();

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Animal{name = " + name + ", age = " + age + "}";
    }
}
package CollectionDome.Generics_Dome5;

public abstract class Cat extends Animal {
    public Cat(String name,int age ){
        super(name, age);
    }


}
package CollectionDome.Generics_Dome5;


public class Cat_bosi extends Cat{
    public Cat_bosi(String name, int age) {
        super(name, age);
    }

    @Override
    void eat() {
        System.out.println("一只叫"+Cat_bosi.super.getName() +"的,"+ Cat_bosi.super.getAge()+"岁的波斯猫,正在吃小鱼干");
    }
}
package CollectionDome.Generics_Dome5;

public class Cat_lihua extends Cat{

    public Cat_lihua(String name, int age) {
        super(name, age);
    }

    @Override
    void eat() {
        System.out.println("一只叫"+Cat_lihua.super.getName() +"的,"+ Cat_lihua.super.getAge()+"岁的狸花猫,正在吃小鱼干");
    }
}
package CollectionDome.Generics_Dome5;

public abstract class Dog extends Animal{
    public Dog(String name,int age ){
        super(name, age);
    }
}
package CollectionDome.Generics_Dome5;

public class Dog_hashiqi extends Dog{
    public Dog_hashiqi(String name, int age) {
        super(name, age);
    }

    @Override
    void eat() {
        System.out.println("一只叫做"+Dog_hashiqi.super.getName() +"的,"+Dog_hashiqi.super.getAge() +"岁的哈士奇,正在吃骨头,边吃边蹭");
    }
}
package CollectionDome.Generics_Dome5;

public class Dog_taidi extends Dog{
    public Dog_taidi(String name, int age) {
        super(name, age);
    }
    @Override
    void eat() {
        System.out.println("一只叫做"+Dog_taidi.super.getName() +"的,"+Dog_taidi.super.getAge() +"岁的泰迪,正在吃骨头,边吃边蹭");
    }
}

哪里定义泛型?

  • 泛型类:在类名后面定义泛型,创建该类对象的时候,确定类型
  • 泛型方法:在修饰符后面定义方法,调用该方法的时候,确定类型
  • 泛型接口:在接口名后面定义泛型,实现类确定类型,实现类延续泛型
  • 泛型不具备继承性,但是数据具备继承性

七、Set集合

Set系列集合

  • 无序:存取顺序不一致
  • 不重复:可以去除重复
  • 无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素

Set集合的实现类

  • HashSet:无序、不重复、无索引
  • LinkedHashSet:有序、不重复、无索引
  • Treeset:可排序、不重复、无索引

Set接口中的方法上基本上与Collection的API一致。

7.1、HashSet

HashSet底层原理

  • HashSet集合底层采取哈希表存储数据
  • 哈希表是一种对于增删改查数据性能都较好的结构

哈希表组成

  • JDK8之前:数组+链表
  • JDK8开始:数组+链表+红黑树

哈希值

  • 根据hashcode方法算出来的int类型的整数
  • 该方法定义在Object类中,所有对象都可以调用,默认使用地址值进行计算
  • 一般情况下,会重写hashCode方法,利用对象内部的属性值计算哈希值

对象的哈希值特点

  • 如果没有重写hashcode方法,不同对象计算出的哈希值是不同的
  • 如果已经重写hashcode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的
  • 在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样。(哈希碰撞)

HashSet底层原理

  1. 创建一个默认长度16,默认加载因子0.75的数组,数组名table
  2. 根据元素的哈希值跟数组的长度计算出应存入的位置
  3. 判断当前位置是否为null,如果是null直接存入
  4. 如果位置不为null,表示有元素,则调用equals方法比较属性值
    • 一样:不存
    • 不一样:存入数组,形成链表
  • JDK8以前:新元素存入数组,老元素挂在新元素下面
  • JDK8以后:新元素直接挂在老元素下面
  • JDK8以后,当链表长度超过8,而且数组长度大于等于64时,自动转换为红黑树
  • 如果集合中存储的是自定义对象,必须要重写hashCodeequals方法

小结:

  1. Hashset集合的底层数据结构是什么样的?
    链表和红黑树
  2. HashSet添加元素的过程?
    根据元素的哈希值跟数组的长度计算出应存入的位置,然后存入,如果有数据了就判断两者是否相同,相同则去除,不相同则挂到当前链表下方
  3. Hashset为什么存和取的顺序不一样?
    存储的位置是随机的,不确定从哪个位置开始取出来
  4. HashSet为什么没有索引?
    一个索引下可能有多个数据,使用索引查找没办法取到准确的数据
  5. Hashset是利用什么机制保证去重的?
    利用HashCode方法和equals方法

7.2、HashSet案例

在这里插入图片描述

package CollectionDome.SetDome;

import java.util.HashSet;
import java.util.Set;

public class Dome2 {
    public static void main(String[] args) {

        Set<Student> s = new HashSet<>();

        Student s1 = new Student("zhangsan",18);
        Student s2 = new Student("xiaowang",18);
        Student s3 = new Student("laowang",19);
        Student s4 = new Student("xiaowang",18);

        s.add(s1);
        s.add(s2);
        s.add(s3);
        s.add(s4);

        System.out.println(s);

    }
}
package CollectionDome.SetDome;

import javax.sql.rowset.spi.SyncResolver;
import java.util.Objects;

public class Student {
    private String name;
    private int age;


    public Student() {
    }

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

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

7.3、LinkedHashSet

  • 底层原理
    • 有序、不重复、无索引。
    • 这里的有序指的是保证存储和取出的元素顺序一致
    • 原理:底层数据结构是依然哈希费,只是每个元素又额外的多了一个双链表的机制记录存储的顺序。

在这里插入图片描述

7.4、TreeSet

特点:

  • 不重复、无索引、可排序
  • 可排序:按照元素的默认规则(有小到大)排序。
  • TreeSet集合底层是基升红黑树的数据结构实现排序的,增删改查性能都较好。
package CollectionDome.SetDome;

import java.util.Iterator;
import java.util.TreeSet;
import java.util.function.Consumer;

public class TreeSetDome1 {
    public static void main(String[] args) {
        TreeSet<Integer> t = new TreeSet<>();

        t.add(1);
        t.add(9);
        t.add(8);
        t.add(6);
        t.add(8);
        t.add(4);
        t.add(2);
        t.add(7);

        System.out.println(t);
        System.out.println("------------------");

        Iterator it = t.iterator();
        while (it.hasNext()){
            Object next = it.next();
            System.out.println(next);
        }

        for (Integer i:t){
            System.out.println(i);
        }

        t.forEach(integer->System.out.println(integer));
    }
}

TreeSet集合默认的规则

  • 对于数值类型: Integer , Double,默认按照从小到大的顺序进行排序。
  • 对于字符、字符串类型:按照字符在ASCII码表中的数字升序进行排序。

TreeSet的两种比较方式:

  1. 方式一:

默认排序/自然排序:Javabean类实现Comparable接口指定比较规则

在这里插入图片描述

package CollectionDome.SetDome;

import java.util.TreeSet;

public class TreeSetDome2 {
    public static void main(String[] args) {
        TreeSet<Student> ts = new TreeSet<>();

        Student s4 = new Student("xiaoli",23);
        Student s5 = new Student("xiaowang",24);
        Student s6 = new Student("xiaozhang",25);
        Student s1 = new Student("xiaoming",20);
        Student s2 = new Student("xiaohong",22);
        Student s3 = new Student("xiaolu",21);

        ts.add(s4);
        ts.add(s5);
        ts.add(s6);
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);

        System.out.println(ts);
    }
}
package CollectionDome.SetDome;

import javax.sql.rowset.spi.SyncResolver;
import java.util.Comparator;
import java.util.Objects;

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

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

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


    @Override
    public int compareTo(Student o) {
        System.out.println("this:"+this);
        System.out.println("o:"+o);
        return this.getAge()-o.getAge();
    }
}

  1. 方式二:

比较器排序:创建TreeSet对象时候,传递比较器Comparator指定规则

使用原则:默认使用第一种,如果第一种不能满足当前需求,就使用第二种
在这里插入图片描述

package CollectionDome.SetDome;

import java.util.Comparator;
import java.util.TreeSet;

public class TreeSetDome3 {
    public static void main(String[] args) {
        TreeSet<String> ts = new TreeSet<>((String o1, String o2)->{
                //比较长度来排序
                int i = o1.length()-o2.length();
                i =  i==0?o1.compareTo(o2):i;
                return i;
            }
        );

        ts.add("asd");
        ts.add("qwe");
        ts.add("zzza");
        ts.add("aaaz");
        ts.add("sdsasdasd");
        ts.add("jkl");
        ts.add("tyh");
        
        System.out.println(ts);

    }
}

在这里插入图片描述

package CollectionDome.SetDome;

import java.util.TreeSet;

public class stu1_Dome4 {
    public static void main(String[] args) {
        TreeSet<student1> ts = new TreeSet<>();

        student1 s1 = new student1("xiaoming",18,89,99,78);
        student1 s2 = new student1("xiaowang",19,87,99,80);
        student1 s3 = new student1("xiaoli",18,86,100,80);
        student1 s4 = new student1("xiaozhang",17,95,90,80);

        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);

        System.out.println(ts);
     }
}
package CollectionDome.SetDome;

public class student1 implements Comparable<student1>{
    private String name;
    private int age;
    private int Chinese;
    private int Math;
    private int English;


    public student1() {
    }

    public student1(String name, int age, int Chinese, int Math, int English) {
        this.name = name;
        this.age = age;
        this.Chinese = Chinese;
        this.Math = Math;
        this.English = English;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    /**
     * 获取
     * @return Chinese
     */
    public int getChinese() {
        return Chinese;
    }

    /**
     * 设置
     * @param Chinese
     */
    public void setChinese(int Chinese) {
        this.Chinese = Chinese;
    }

    /**
     * 获取
     * @return Math
     */
    public int getMath() {
        return Math;
    }

    /**
     * 设置
     * @param Math
     */
    public void setMath(int Math) {
        this.Math = Math;
    }

    /**
     * 获取
     * @return English
     */
    public int getEnglish() {
        return English;
    }

    /**
     * 设置
     * @param English
     */
    public void setEnglish(int English) {
        this.English = English;
    }

    public String toString() {
        return "student1{name = " + name + ", age = " + age + ", Chinese = " + Chinese + ", Math = " + Math + ", English = " + English + "}";
    }

    @Override
    public int compareTo(student1 o) {
        int sum1 = this.getChinese()+this.getEnglish()+this.getMath();
        int sum2 = o.getChinese()+o.getEnglish()+o.getMath();

        int i= sum1-sum2;
        i = i==0?this.getChinese()-o.getChinese():i;
        i = i==0?this.getMath()-o.getMath():i;
        i = i==0?this.getEnglish()-o.getEnglish():i;
        i = i==0?this.getName().compareTo(o.getName()):i;
        return i;
    }

}

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

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

相关文章

Linux系列讲解 —— SSH登录

讲解一下ssh远程登陆的基础知识。 目录 0. 基本原理1. 安装ssh程序&#xff1a;1.1 windows平台(Win10)1.2 Linux平台(Ubuntu18.04) 2. 密码方式远程登录3. 密钥方式远程登录3.1 生成私钥公钥对3.2 将公钥复制到远程机器3.3 尝试ssh远程登录 4. 常见问题4.1 sun192.168.1.21: P…

Pycharm卡顿、反应慢、CPU占用高

环境&#xff1a; Windows10 22H2 pycharm 2020.1.5专业版 pytorch1.10.1 这是第二次遇到这个问题了&#xff0c;之前尝试过更换pycharm版本&#xff0c;问题是解决了&#xff0c;但是又出现了其他bug。今天研究了半天&#xff0c;使用排除法确定了问题所在。 网上的解决方案…

opencv之图像遍历方法详解

1.IplImage IplImage是OpenCV2、3 中CxCore部分基础的数据结构&#xff0c;用来表示图像。IplImage结构体如下所示&#xff1a; typedef struct _IplImage { int nSize; /* IplImage大小 */ int ID; /* 版本 (0)*/ int nChannels; /* 大多数OPE…

LeetCode DFS算法求解联通分量数——省份数量

省份数量 题目要求 有 n 个城市&#xff0c;其中一些彼此相连&#xff0c;另一些没有相连。如果城市 a 与城市 b 直接相连&#xff0c;且城市 b 与城市 c 直接相连&#xff0c;那么城市 a 与城市 c 间接相连。 省份 是一组直接或间接相连的城市&#xff0c;组内不含其他没有相连…

编译时不好的注释会让代码丢失并产生问题

写在之前 这篇文章是上一篇文章的后续事件&#xff0c;记录的事情也挺有意思。想看事情如何开始的点击链接 频繁GC引起卡顿问题排查与解决 进入正题 不知道有没有人遇到过编译后部分代码缺失呢&#xff1f;反正我遇到了&#xff0c; 上一篇文章提到了因为开发人员写了死循环…

Docker安装mysql8.0文档

第一步需要安装Docker基础环境&#xff0c;具体可以看看这篇 docker基础篇 第二步&#xff0c;拉取mysql8.0的镜像 docker pull mysql:8.0 第三步&#xff0c;镜像启动和文件挂载 复制下面命令执行&#xff0c;33006是对外访问暴露的端口&#xff0c;当然你也可以设置为3306…

【Hello Network】HTTP协议

作者&#xff1a;小萌新 专栏&#xff1a;网络 作者简介&#xff1a;大二学生 希望能和大家一起进步 本篇博客简介&#xff1a;较为详细的介绍HTTP协议 HTTP协议 HTTP协议HTTP协议概念URL概念urlencode和urldecodeHTTP协议格式HTTP请求协议格式HTTP响应格式 HTTP的方法HTTP状态…

04-26 每日一题 1031. 两个非重叠子数组的最大和 学习反思

1031. 两个非重叠子数组的最大和 类似问题转换 考虑一个问题&#xff0c;如何求得数组中两个数的最大和。 可以固定一个数&#xff0c;然后向右遍历如下&#xff0c;可以求得目标数组中两个数的最大和为 15 把思路实现为代码 实现过程&#xff0c;如上图所示过程&#xff0…

【汽车品牌案例02-设置右侧索引 Objective-C语言】

一、刚才我们说了一下,如何把那个汽车品牌加载起来,我们使用了一个模型的嵌套,以及我们在创建单元格的时候,是不是指定了一个,单元格的可重用ID吧, 1.根据重用ID来创建单元格,那么我们运行的时候,已经能把这个大致的效果做出来了, 大致就是这么一个效果, 接下来,还…

SPI机制源码解析

概念 SPI&#xff08;service provider interface&#xff09;&#xff0c;是JDK内置的一种服务提供发现机制。是一种动态替换发现机制&#xff0c;比如有个接口&#xff0c;想在运行时动态地给它添件实现&#xff0c;只需要添加一个实现。 然后在META-INF/services目录创建一…

树莓派+摄像头:mjpg-streamer实现监控功能的配置及调试

目录 一 树莓派摄像头安装 二 配置mjpg-streamer ① 说明 ② 配置 <1> 配置前需要安装的工具包 <2> 下载安装mjpg-streamer源码到树莓 <3> 进入下载目录的路径 <4> 输入指令编译&#xff1a;make all <5> 安装指令&#xff1a;…

任务调度原理 通俗详解(FreeRTOS)

寄存器说明 以cortex-M3&#xff0c;首先先要了解比较特别的几个寄存器&#xff1a; r15 PC程序计数器&#xff08;Program Counter&#xff09;,存储下一条要执行的指令的地址。 r14 LR连接寄存器&#xff08;Link Register &#xff09;&#xff0c;保存函数返回地址&#x…

【操作系统】第一章

文章目录 &#x1f337; 一、操作系统的概念1、定义2、功能 和 目标 &#x1f337; 二、操作系统的特征1、**并发**2、 **共享**3、 **虚拟**4、 **不确定性** &#x1f337; 三、操作系统的发展与分类1、 手工操作阶段2、 批处理阶段3、 分时操作系统4、 实时操作系统5、 网络…

servlet技术

功能 对客户端发送的数据进行读取和拦截读取客户端请求的隐含数据运行结果或者生成结果发送响应的数据 Servlet技术特点 高效率 Servlet本身就是一个Java类&#xff0c;在运行的时候位于同一个Java虚拟机中&#xff0c;可以快速地响应客户 端的请求并生成结果。在Wb服务器…

停用词(stop words)+TF-IDF实现

一、什么是停用词&#xff1f; 在汉语中&#xff0c;有一类没有实际意义的词语&#xff0c;比如组词“的”&#xff0c;连词“以及”&#xff0c;副词“甚至”&#xff0c;语气词“吧”&#xff0c;被称为停用词。一个句子去掉这些停用词&#xff0c;并不影响理解。所以&#…

资产管理系统

目录 1、资产管理模块 资产入库 ​编辑 闲置资产分配 资产调配 资产回收 资产报废 车辆维修 2、资产设置模块 资产标准规格 资产分类 3、资产报表模块 全部资产报表 已分配资产报表 资产分类报表 到期资产报表 机构资产报表 资产折旧报表 机构分类报表 资产…

〖ChatGPT实践指南 - 零基础扫盲篇⑤〗- OpenAI API 演示 Demo 之宠物名字生成器

文章目录 ⭐ 运行 Demo应用 - 宠物名字生成器&#x1f31f; 安装 - node.js&#x1f31f; 利用 git 下载 Demo应用 - 宠物名字成器&#x1f31f; 添加 API 秘钥&#x1f31f; 安装依赖并运行Demo应用 - 宠物名字成器 ⭐ 访问并测试 Demo应用 - 宠物名字成器 在上一章节&#xf…

最新版TensorFlow的GPU版本不支持原生Windows系统(大坑预警)

一、前言 首先需要说明&#xff0c;按照官方中文文档安装是无法正常检测到GPU的。因为TensorFlow 2.10是支持原生Windows系统GPU环境的最后版本&#xff0c;默认安装的版本都比较高。 中文文档没有说明&#xff0c;英文文档是有提到的&#xff1a; &#xff08;我在GitHub上找…

PostgreSQL-布尔类型

布尔类型 boolean的值要么是true&#xff0c;要么是false&#xff0c;如果是unknown状态&#xff0c;用NULL表示。 boolean在SQL中可以用不带引号的TRUE或FALSE表示&#xff0c;也可以用其他表示“真”和“假”的带引号字符表示&#xff0c;如true、false、yes、no等等。 cr…

操作系统之进程同步和互斥

目录 什么是进程同步和进程互斥 进程互斥的软件实现方法 进程互斥的硬件实现方法 互斥锁 信号量机制 用信号量实现进程互斥和同步 生产者消费者问题 多生产者多消费者问题 吸烟者问题 读者写者问题 哲学家进餐问题 管程 死锁 什么是进程同步和进程互斥 进程同步 进…