首页 > 其他分享 >集合(进阶 set系列)

集合(进阶 set系列)

时间:2022-12-28 15:01:29浏览次数:36  
标签:set return 进阶 age String Student1 集合 public name

泛型


package com.an.a;

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

public class FanxingTest {
    public static void main(String[] args) {
        List list =new ArrayList();//不申明泛型,默认用Object接收
        list.add("hello");
        list.add(12);
        list.add(23.4);
       Iterator it = list.iterator();//获取迭代器
        while (it.hasNext()){//判断该指针所指的位置是否有元素
          String str=(String) it.next();//获取该位置的元素并往后移动指针
            System.out.println(str);
        }

    }
}
//ClassCastException: java.lang.Integer cannot be cast to java.lang.String
//	at com.an.a.FanxingTest.main(FanxingTest.java:15)




  • 当集合不声明泛型将会默认以Object进行接收
  • 这样将会导致集合可以接收任意类型的数据,这样将导致数据混乱,不利于使用
  • 这样接收将会导致无法使用子类特有的行为
  • 如果储存的数据类型和泛型的类型不同将会在编译期间报错
    想要使用子类特有的行为必须要向下转型,但是向下转型极有可能导致ClassCastException


java的伪泛型

泛型类

(以下所写的泛型类,实际上也是多数集合源码的体现)

java所采取的是伪泛型机制,在编译期间将会进行泛型擦除,即泛型类型作用是检测存进集合的数据的类型,而实际上在集中还是用Object储存,而在取出时将会进行强制类型转化
泛型方法


package com.an.a;
import java.util.ArrayList;
//定义工具类,里面定义静态方法addAll可以添加集合多种类型的元素
class ListUtil{
    //私有化构造方法
    private ListUtil(){};
    //addAlll:参数1:集合 参数2:要添加的元素
    public static <T> void addAll(ArrayList<T> list,T...t){//变长参数(t表示数组名)
        for (int i = 0; i < t.length; i++) {
            list.add(t[i]);
        }
    }
}
public class ListUtilTest {
    public static void main(String[] args) {
        ArrayList<Integer> list=new ArrayList<>();
        ListUtil.addAll(list,2,3,4,5,6,7,87,0);
        System.out.println(list);//[2, 3, 4, 5, 6, 7, 87, 0]
    }
}

泛型接口

泛型的继承和通配符

ArrayList,泛型类型为T的集合可以添加类型为T或者是T的子类的数据
泛型不具备继承性,但是数据具有继承性

package com.an.a;

import java.util.ArrayList;


class Person{

}
class Student extends Person{

}
public class test5 {
    public static void main(String[] args) {
        ArrayList<Person> list1 =new ArrayList<>();
        ArrayList<Student> list2 =new ArrayList<>();
                
        show(list1);
        show(list2);
    }
    public static void show(ArrayList<Person> list){
        
    }
}

此时进行引用传递,发现只有相同类型且相同泛型的对象才能通过引用传递,说明泛型不具备继承性

  • 应用场景
    如果我们在定义类 方法 接口 的时候,如果类型不确定,就可以定义成泛型类 泛型方法 泛型接口
    如果类型不知道,但是知道只能传递某个继承体系,就可以使用泛型通配符
    泛型通配符:可以限定类型的范围


  • 综合练习(以后完善)

public void show(ArrayList list)此时的T和?作用一样,表示传递任意类型都可以

Vector在java1.2的时候已经被淘汰

ArrayList: Array底层的数据结构:数组 List:属于List的一员



查找和储存时的规律一样,依次和根节点进行比较

  • 二叉树的遍历方式





二叉查找树的弊端

如果添加的数据使得根节点的左右子树极其不对称,将会导致查询效率太低

平衡二叉树


平衡二叉树保持平衡的旋转机制

Set系列集合

  • 回顾Collection的方法

    由于set系列的集合没有索引,所以set里面的方法和collection里面的方法基本相同
package com.an.a;

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

//测试set里面的一些方法的使用
public class SetTest {
    public static void main(String[] args) {
        /*
            利用Set系列的集合,添加字符串并使用多种方式遍历
            1.迭代器
            2.增强for

         */
        //1.创建一个Set的集合并添加元素:去重
        Set<String> s=new HashSet<>();//Set为接口,使用多态实例化
        //2.添加元素:当该元素为第一次添加将会添加成功 返回true
        //当该元素为第二次添加将会添加失败 返回false
        boolean a = s.add("hello");
        boolean b = s.add("hello");
        System.out.println(a);//true
        System.out.println(b);//false
        System.out.println("--------------------------------");
        //2.打印集合:无序
        Set<String>s2=new HashSet<>();
        s2.add("张三");
        s2.add("李四");
        s2.add("王五");
        System.out.println(s2);//[李四, 张三, 王五]:存和取的顺序不一样
        //3.没有索引
        //4.遍历

        //迭代器遍历
         Iterator<String> it = s2.iterator();//获取迭代器
        while(it.hasNext()){
            System.out.println(it.next());
        }
    //增强for遍历
        for (String s1 : s2) {
            System.out.println(s1);
        }


    }
}

HashSet

HashSet的方法和Collection的方法一样

HashSet的底层实现原理

哈希值:对象的整数表现形式





哈希值特点演示

package Test;

import java.util.Objects;

//Hash值是根据hashCode方法计算出来的整数
//hashCode是定义在Object中的方法,如果没有被从写,默认以对象的地址值进行计算
//哈希值是对象的整数表现形式
class Person{
    private String name;
    private int age;

    public Person() {
    }

    public Person(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;
    }

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

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

    public String toString() {
        return "Person{name = " + name + ", age = " + age + "}";
    }
}
public class HashTest {
    public static void main(String[] args) {
        //哈希值的特点

        /*
        1.如果没有重写hashCode,不同对象计算的哈希值是不同的
        2.如果重写了hashCode,不同对象只要属性相同,计算出的哈希值就是一样的
        3.在小部分情况下,不同属性或者不同地址计算出来的哈希值也有可能相同(哈希碰撞)
         */
        //创建对象
       Person pr1=new Person("张三",23);
       Person pr2=new Person("张三",23);
       //以地址值进行计算,相同的对象不同的地址哈希值不同
        System.out.println(pr1.hashCode());//460141958
        System.out.println(pr2.hashCode());//1163157884
        //当我们覆写hashCode方法使其按照属性值计算哈希值
        //覆写hashCode只要属性值相同即使地址不同哈希值也是相同的
        System.out.println(pr1.hashCode());//24022543
        System.out.println(pr2.hashCode());//24022543
        //3.在少部分的情况下属性值不同或者地址值不同哈希值有可能相同
        //字符串已经覆写了hashCode方法,使其按照字符串来计算哈希值
        System.out.println("abc".hashCode());//96354
        System.out.println("acD".hashCode());//96354
    }
}

HashSet类是根据对象的哈希值来判断,是否是同一个对象的

package Test;

import java.util.HashSet;
import java.util.Objects;

class Stduent{
    private String name;
    private int age;

    public Stduent() {
    }

    public Stduent(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;
    }

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

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

    public String toString() {
        return "Stduent{name = " + name + ", age = " + age + "}";
    }
}
public class HashSetTest {
    public static void main(String[] args) {
        Stduent st1=new Stduent("张三",23);
        Stduent st2=new Stduent("李四",24);
        Stduent st3=new Stduent("王五",25);
        Stduent st4=new Stduent("张三",23);
        HashSet<Stduent> ha=new HashSet<>();
        //添加元素到集合中
        //1.当没有重写hashCode,不同对象的哈希值都不同
        //2.当重写hashCode,通过属性决定哈希值
        System.out.println(ha.add(st1));//true
        System.out.println(ha.add(st2));//true
        System.out.println(ha.add(st3));//true
        System.out.println(ha.add(st4));//true   false(覆写hashCode将会通过属性确定哈希值,然后对对象去重)

    }
}

LinkedHashSet


LindedHashSet底层原理

treeSet


默认将集合内的元素按从小到大进行排序

package Test;

import java.util.*;

//利用hashTree储存整数并进行排序
public class HashTreeTest {
    public static void main(String[] args) {
       TreeSet<Integer> hs=new TreeSet<>();
        hs.add(3);
        hs.add(1);
        hs.add(2);
        hs.add(5);
        hs.add(4);
        System.out.println(hs);//[1, 2, 3, 4, 5]
        //迭代器遍历
        final Iterator<Integer> it = hs.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }
        //增强for遍历
        for (Integer h : hs) {
            System.out.println(h);
        }
    }
}


对于字符串的ascii码比较,第一个和第一个字符相比,第二个和第二个字符相比,直到比较出来了大小关系,然后后面的将不会再比较

treeSet的第一种排序规则

我们前面排序的,集合里面储存的数据所属的类,都是java官方写好的类,下面我们将使用自己写的类进行排序

按照之前一样的进行处理

package Test;

import java.util.*;

class Student1{
    private String name;
    private int age;

    public Student1() {
    }

    public Student1(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 "Student1{name = " + name + ", age = " + age + "}";
    }
}
public class HashTreeTest {
    public static void main(String[] args) {
     TreeSet<Student1> ts = new TreeSet<>();
     Student1 s1 = new Student1("zhangsan",23);
     Student1 s2 = new Student1("lisi",24);
     Student1 s3 = new Student1("wangwu",25);
     //添加数据
     ts.add(s1);
     ts.add(s2);
     ts.add(s3);
     System.out.println(ts);




    }
}


此代码将会出现异常ClassCastException,可以知道是因为我们没有实现Comprable接口(和使用sort方法的情况一样),

这是因为我们没有为我们的Student1类定义比较规则,而Integer String等类在内步已经实现了该接口,即已经指明了比较规则

package Test;

import java.util.*;

class Student1 implements Comparable<Student1>{
    private String name;
    private int age;

    public Student1() {
    }

    public Student1(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 "Student1{name = " + name + ", age = " + age + "}";
    }

    @Override
    public int compareTo(Student1 o) {
       return this.getAge()-o.getAge();
    }
}
public class HashTreeTest {
    public static void main(String[] args) {
     TreeSet<Student1> ts = new TreeSet<>();
     Student1 s1 = new Student1("zhangsan",23);
     Student1 s2 = new Student1("lisi",24);
     Student1 s3 = new Student1("wangwu",25);
     //添加数据
     ts.add(s1);
     ts.add(s2);
     ts.add(s3);
     System.out.println(ts);
//[Student1{name = zhangsan, age = 23}, Student1{name = lisi, age = 24}, Student1{name = wangwu, age = 25}]



    }
}

可以发现此时已经按照年龄进行了排序
结合红黑树的特性,然后调用compareTo进行具体的排序的具体过程以后具体整理

TreeSet的第二种比较方式


当默认比较规则不能满足我们的需求,就可以使用自定义的第二种排序规制

  • 综合练习


标签:set,return,进阶,age,String,Student1,集合,public,name
From: https://www.cnblogs.com/swtaa/p/17002904.html

相关文章

  • WinNTSetup V5.3.0 Bata5 单文件版
    前言WinNTSetup是一款Windows系统硬盘安装器,支持从PE和本地安装系统,支持支持NT内核的系统。WinNTSetup包括XP、Win7、Win8、Win8.1、Win10等这些系统。直接从硬盘安装......
  • setup语法糖
    Vue3.2版本开始才能使用语法糖!什么是setupscript它是Vue3的一个新语法糖,在setup函数中。所有ES模块导出都被认为是暴露给上下文的值,并包含在setup()返回对......
  • EL表达式取出Map集合中key为Integer类型的值,bug解决方案
    EL表达式取出Map集合中key为Integer类型的值,bug解决方案  今天,我在用EL表达式取Map集合中key为Integer类型的值时,发现无法取出。  问题 Demo如下:<b......
  • 公理集合论(三):基数理论
    请读者具备离散数学的基础三、基数可数序数(可数集)可数、不可数、有限、无限,在高中应该都有接触,但具体定义是什么,或许并不能很准确的说明。(1)可数无穷序数(简称可数序......
  • [AGC060D] Same Descent Set
    题解考虑给定一个由<和>组成的长度为\(n-1\)的字符串,第\(i\)位为<表示\(p_i<p_{i+1}\),否则表示\(p_i>p_{i+1}\)。假设有一个这样的字符串\(t\),那么设\(......
  • Java难点 | Collections集合工具类
    Collections集合工具类addAll和shuffle方法代码示例/*publicstatic<T>booleanaddAll(collection<T>c,T...elements):往集合添加多个元素publicstatic......
  • Collection集合常用方法
           ......
  • Collection集合概述和使用
          ......
  • Collection 集合
           ......
  • setjmp使用
    当 setjmp 和 longjmp 一起使用时,它们提供了一种执行非本地 goto 的方法。它们通常在C代码中用于将执行控制传递给先前调用的例程中的错误处理或恢复代码,而不使用......