Java基础(十)

前面的话
博客主页:hyhWTX的博客主页

欢迎关注🖱点赞🎀收藏⭐留言✒

本文由hyhWTX原创,csdn首发!

二十四.Set接口

一.接口

java.util.Set接口和java.util.List接口一样,同样继承自Collection接口,它与Collection接口中的方法基本一致,并没有对Collection接口进行功能上的补充,只是比Cooection接口更加严格了,与List接口不同的是,Set接口中元素无序,并且都会以某种规则保证存入的元素不出现重复。

Set集合有多个子类。

HashSet集合介绍:

java.util.HashSet是Set接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的(即存取顺序不一致—)。Java.util.HashSet底层其实是一个java.util,HashMap支持。

HashSet是根据对象的哈希值来确定元素在集合中的存储位置,因此就有良好的存取和查找性能,保证元素唯一性的方式依赖于:hashCode与equals方法。

源码示例:

Person.java

package Day12.Hashcode;

/**
 * @author hyhWTX
 * @version 1.0
 * @ClassName Person
 * @date 2022年07月09日 16:12
 * @Description: TODO (一句话描述以下该类的功能)
 */
public class Person extends Object{
    //重写HashCode方法

//    @Override
//    public int hashCode() {
//        return 1;
//    }
}

Demo01HashCode.java

package Day12.Hashcode;

/**
 * @author hyhWTX
 * @version 1.0
 * @ClassName Demo01HashCode
 * @date 2022年07月09日 16:06
 * @Description: hashCode案例示范
 */
/**
 * hashCode()方法的源码:
 *  public native int hashCode();
 *  native:代表该方法调用的是本地操作系统的方法。
 */
public class Demo01HashCode {
    public static void main(String[] args) {
        //Person类继承Object类,所以可以使用Object里面的hashcode方法
        Person p1 = new Person();
        int h1 = p1.hashCode();
        System.out.println(h1);//21685669

        Person p2 = new Person();
        int h2 = p2.hashCode();
        System.out.println(h2);//2133927002

        /**
         * toString方法的源码
         *  return getClass().getName() + "@" + Integer.toHexString(hashCode());
         *
         */
        System.out.println(p1);//Day12.Hashcode.Person@14ae5a5

        System.out.println(p2);//Day12.Hashcode.Person@7f31245a

        System.out.println(p1==p2);

        /**
         *String类的哈希值
         *  String类重写Object类的hashCode方法
         *
         */
        String s1 = new String("abc");
        String s2 = new String("abc");
        System.out.println(s1.hashCode());
        System.out.println(s2.hashCode());

        System.out.println("重地".hashCode());
        System.out.println("通话".hashCode() );

    }
}

二.结构——hashmap

哈希值:是一个十进制的整数,由系统随机给出(就是对象的地址值,是一个逻辑地址,是模拟出来得到地址,不是数据实际存储的物理地址),在Object类中有一个方法,可以获取对象的哈希值。int hashCode()返回该对象的哈希码值。

源码实例:

Person.java

package Day12.Hashcode;

/**
 * @author hyhWTX
 * @version 1.0
 * @ClassName Person
 * @date 2022年07月09日 16:12
 * @Description: TODO (一句话描述以下该类的功能)
 */
public class Person extends Object{
    //重写HashCode方法

//    @Override
//    public int hashCode() {
//        return 1;
//    }
}

Demo01HashCode.java

package Day12.Hashcode;

/**
 * @author hyhWTX
 * @version 1.0
 * @ClassName Demo01HashCode
 * @date 2022年07月09日 16:06
 * @Description: hashCode案例示范
 */
/**
 * hashCode()方法的源码:
 *  public native int hashCode();
 *  native:代表该方法调用的是本地操作系统的方法。
 */
public class Demo01HashCode {
    public static void main(String[] args) {
        //Person类继承Object类,所以可以使用Object里面的hashcode方法
        Person p1 = new Person();
        int h1 = p1.hashCode();
        System.out.println(h1);//21685669

        Person p2 = new Person();
        int h2 = p2.hashCode();
        System.out.println(h2);//2133927002

        /**
         * toString方法的源码
         *  return getClass().getName() + "@" + Integer.toHexString(hashCode());
         *
         */
        System.out.println(p1);//Day12.Hashcode.Person@14ae5a5

        System.out.println(p2);//Day12.Hashcode.Person@7f31245a

        System.out.println(p1==p2);

        /**
         *String类的哈希值
         *  String类重写Object类的hashCode方法
         *
         */
        String s1 = new String("abc");
        String s2 = new String("abc");
        System.out.println(s1.hashCode());
        System.out.println(s2.hashCode());

        System.out.println("重地".hashCode());
        System.out.println("通话".hashCode() );

    }
}

什么是哈希表呢?

在JDK1.8之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储

在一个链表里,但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低,而JDK1.8中,哈希表存储采用数组+链表+红黑数实现,当链表长度超过阙值(8)时,将链表转换为红黑树,这样就大大减少了查找时间。

简单而言,哈希表是由数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PAKMLKLx-1657717102995)(2022-07-09%201png.png)]

三.HashSet存储数据的结构

源码示例:

Demo02Set.java

package Day12.Set;

import java.util.HashSet;

/**
 * @author hyhWTX
 * @version 1.0
 * @ClassName Demo02Set
 * @date 2022年07月09日 16:47
 * @Description: HashSet存储自定义类型元素
 */

/**
 * set集合不允许存储重复元素的原理
 */
public class Demo02Set {
    public static void main(String[] args) {
        //创建HashSet集合对象
        HashSet<Object> set = new HashSet<>();
        String s1 = new String("abc");
        String s2 = new String("abc");
        set.add(s1);
        set.add(s2);
        set.add("重地");
        set.add("通话");
        set.add("abc");
        System.out.println(set);

    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yVFp7hjx-1657717102997)(2022-07-09%202.png)]

给HashSet中存放自定义类型元素时,需要重写对象中的HashCode和equals方法,建立自己的比较方式,才能保证HashSet集合中的对象唯一。

源码示例:

Person.java

package Day12.Set;

import java.util.Objects;

/**
 * @author hyhWTX
 * @version 1.0
 * @ClassName Person
 * @date 2022年07月09日 18:59
 * @Description: Demo03Set使用的Person类
 */
public class Person {
    private String name;
    private int age;

    public Person() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    //重写之后两者的equal值相同,存储一次
    @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);
    }
}

Demo03Set.java

package Day12.Set;

/**
 * @author hyhWTX
 * @version 1.0
 * @ClassName Demo03Set
 * @date 2022年07月09日 18:51
 * @Description: HashCode存储自定义类型元素
 */

import java.util.HashSet;

/**
 * set集合报错元素唯一:
 *      存储的元素(String,Integer,...Student,Person..)必须重写hashCode方法和equals方法
 *
 * 要求:
 *      同名同年龄的人,视为同一个人,只能存储一次
 */

public class Demo03Set {
    public static void main(String[] args) {
        //创建HashSet集合存储Person
        HashSet<Person> set = new HashSet<>();
        Person p1 = new Person("小美女", 12);
        Person p2 = new Person("小美女", 12);
        Person p3 = new Person("小美女", 12);

        System.out.println(p1.hashCode());
        System.out.println(p2.hashCode());
        System.out.println(p1==p2);//两者的hashcode值不同
        set.add(p1);
        set.add(p2);
        set.add(p3);
        System.out.println(set);
    }
}

四.LinkedHashSet

我们知道HashSet保证元素唯一,可是元素存放进去是没有顺序的,那么我们要保证有序,该如何实现呢?在Hashset下面有一个子类java.util.LinkedHashSet,它是链表和哈希表组合的一个数据存储结构。

具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。此实现与 HashSet 的不同之外在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,即按照将元素插入到 set 中的顺序(插入顺序)进行迭代。注意,插入顺序 受在 set 中重新插入的 元素的影响。(如果在 s.contains(e) 返回 true 后立即调用 s.add(e),则元素 e 会被重新插入到 set s 中。)

**注意,此实现不是同步的。**如果多个线程同时访问链接的哈希 set,而其中至少一个线程修改了该 set,则它必须 保持外部同步。这一般通过对自然封装该 set 的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedSet 方法来“包装”该 set。最好在创建时完成这一操作,以防止意外的非同步访问:

     Set s = Collections.synchronizedSet(new LinkedHashSet(...));
 

此类的 iterator 方法返回的迭代器是快速失败 的:在迭代器创建之后,如果对 set 进行修改,除非通过迭代器自身的 remove 方法,其他任何时间任何方式的修改,迭代器都将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不冒将来不确定的时间任意发生不确定行为的风险。

注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何强有力的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。

此类是 Java Collections Framework 的成员。

源码示例:

package Day12.Set;

/**
 * @author hyhWTX
 * @version 1.0
 * @ClassName Demo04LinkedHashSet
 * @date 2022年07月09日 19:37
 * @Description: LinkedHashSet示例示范
 */

import java.util.HashSet;
import java.util.LinkedHashSet;

/**
 * LinkedHashSet集合特点:
 *      底层是一个哈希表(数组+链表/红黑树)+链表,多了一条链表(记录元素的存储顺序),保证元素有序
 */

public class Demo04LinkedHashSet {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<>();
        set.add("www");
        set.add("abc");
        set.add("abc");
        ;
        set.add("itcast");
        System.out.println(set);//无序的,不允许重复

        LinkedHashSet<String> linked = new LinkedHashSet<>();
        linked.add("www");
        linked.add("abc");
        linked.add("abc");
        linked.add("itcast");
        System.out.println(linked);//有序,且不允许重复
    }
}

五.可变参数

在JDK1.5之后,如果我们定义一个方法需要接受多个参数,并且多个参数类型一致,我们可以对其简化成如下格式:

修饰符 返回值类型 方法名(参数类型...形参名){}

书写等价于:

修饰符 返回值类型 方法名(参数类型...形参名){}

只是后面的这种定义,在调用时必须传递数组,而前者可以直接传递数据即可。

JDK1.5以后,出现了简化操作。…用于参数上,称之为可变参数。

同样是代表数组,但是在调用这个带有可变参数的方法时,不用创建数组(这就是简单之处),直接将数组中的元素作为实际参数进行传递,其实编译撑的class文件,将这些元素先封装到一个数组中,在进行传递,这些动作都在编译.class文件时,自动完成了。

注意事项:

  1. 一个方法的参数列表,只能有一个可变参数
  2. 如果方法的参数有多个,那么可变参数必须写在参数列表的末尾。

可变参数特殊写法

public static void method(Object ...object)//可接受任意类型的参数

源码示例:

package Day12.VarArgs;

/**
 * @author hyhWTX
 * @version 1.0
 * @ClassName Demo01Args
 * @date 2022年07月10日 13:18
 * @Description: 可变参数示例
 */
public class Demo01Args {
    public static void main(String[] args) {
        int i = add(10,20,30,40,50,60,70,80,90,100);
        System.out.println(i);
    }

    /**
     *定义计算(0~n)整数和的方法
     * 已知:计算整数的和,数据类型已经确定为int ,参数数目不确定,使用可变参数
     * 调用add方法,就会创建一个长度为0的数组 new int[0]
     * 在add中添加几个数字,,里面就会创建长度为几的数组
     *
     */
    public  static int add(int...arr){
        System.out.println(arr);//底层是一个数组



        //定义一个初始化的变量,记录累加求和
        int sum = 0;
        //遍历数组,获取数组中的每一个元素
        for (int i : arr) {
            //累加求和
            sum +=i;

        }
        //将求和结果返回
        return sum;
    }

//    //定义一个方法,计算三个int类型整数的和
//    public static  int add(int a,int b,int c){
//        return a+b+c;
//    }
//    //定义一个方法,计算两个int类型的整数的和
//    public static  int add(int a,int b){
//        return a+b;
//    }

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值