Java笔记_13(集合进阶)

一、集合的体系结构

在这里插入图片描述

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

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值