IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Day3.Set&Map&euqals()&hashCode()&Comparable&Comparator -Java集合 -> 正文阅读

[Java知识库]Day3.Set&Map&euqals()&hashCode()&Comparable&Comparator -Java集合

Set

? Set集合与Collection集合基本相同,没有提供额外的方法。只是行为上略有不同,Set集合不允许添加重复的元素(add()方法会返回false,且元素不会被加入)

HashSet

HashSet使用Hash算法来存储集合中的元素,因此有良好的存取和查找性能。
HashSet的特点:

  1. HashSet不是线程安全的
  2. HashSet中元素存储的顺序可能与元素添加的顺序不同;
  3. 集合的元素值可以为null

?除此之外,HashSet判断元素是否相等的标准也是其一大特点:HashSet集合判断两个元素相等的标准是hashcode()方法的返回值相等且equals()方法返回true。

equals()

? equals()方法定义在Object类中,其作用是:通过判断两个对象的地址是否相等来判断两个对象是否相等
equals()源码如下:
在这里插入图片描述
?既然equals()方法定义在Object类中,那么意味着Java中所有的类都实现了equals()方法,所有的类都可以使用equals()方法来判断两个对象是否相等。但是默认的equals()等价于"=="
?我们也可以重写子类的equals()方法,定义满足自己需求的逻辑。下面我们以Person类为例子,假设身高和姓名相等的两个人相等。

代码如下:

public class Person {
    public int age;
    public int height;

	//重写Object类的equals(),使其满足自己逻辑
    @Override
    public boolean equals(Object obj) {
    	//如果地址相等,那肯定就相等
        if (this == obj)
            return true;
        //如果obj==null,那必定不等
        if (obj == null)
            return false;
        //如果不是同一个类的对象 那也不等
        if (getClass() != obj.getClass())
            return false;
        //向下转型
        Person other = (Person) obj;
        if (age != other.age)
            return false;
        if (height != other.height)
            return false;
        return true;
    }
}

?我们可以将"类是否重写equals()方法",将equals()的作用分为两类:

  1. 没有重写equals():这时候使用equals()方法去比较两个对象是否相等,实际上比较的是这两个对象的内存地址是否相等(等价于"=="),也就是这两个对象是不是一个对象
  2. 重写了equals():重写了equals()方法的情况通常是:如果比较的这两个对象的内容相等,那么这两个对象相等

hashCode()

? hashCode()的作用是获取哈希值\散列码;hashCode()返回一个int型整数,这个hash值的作用是确定元素在散列表中的索引位置。
?hashCode()方法定义在Object类中,这意味着Java中所有的类中都有hashCode()方法。但是仅仅在创建某个"类"的散列表时,这个类的hashCode()方法才会发挥作用。更通俗的讲,只有创建包含该类的Hashset,Hashtable,HashMap集合时,hashCode()才有用。HashMap,HashSet,Hashtable就是散列表集合。

类似于equals()方法,hashCode()也分两种情况:

  1. 没有重写hashCode():若一个类没有重写hashCode(),那么当它通过hashCode()方法比较是否相等的时候,等价于使用"=="比较两个对象是否相等;
  2. 重写了hashCode():通常的做法是 如果两个对象的内容相等,那么其hashCode()的返回值相等;

HashSet判断集合元素是否相等

?HashSet中不能存储重复元素,当调用add(Object)方法的时候,首先会调用Object的hashCode()方法判断hash值是否已经存在,如果不存在那么就直接插入元素;如果存在那么就调用equals()方法来判断,如果返回false,那么就插入元素;返回true则插入失败。
?HashSet判断两个元素相等的标准:hashCode()返回值相等且equals()返回true。
注意: HashSet是根据元素的hash值来快速定位的,如果出现hash值相等但是equals()返回false的情况,HashSet会把它们存储在相同的位置上,在这个位置上用链表式结构存储多个对象,这样会导致性能下降。
所以重写方法时,尽量保证:hash值相等的情况下,equals()返回true。否则如果将该元素插入到Hash表中,哈希表的性能会降低。

补充:HashSet的实质是一个HashMap。HashSet的所有集合元素构成了HashMap的key,其value是一个静态的Object对象。因此HashSet的所有性质,HashMap的key全部具备。

LinkedHashSet

LinkedHashSet是HashSet的子类,也是根据hash值来确定元素在hash表中存储的位置,同时,LinkedHashSet使用链表来维护元素的次序,使得元素是按添加的顺序来保存的。 当遍历LinkedHashSet中的元素时,LinkedHashSet会以元素的添加顺序来访问集合中的元素。但是由于维护了元素的插入顺序,在性能上略低于HashSet,但是在迭代访问Set集合中的元素时有良好的性能。
注意: LinkedHashSet判断元素是否重复的标准与HashSet一致;

TreeSet

TreeSet是SortedSet接口的实现类,正如SortedSet所暗示的,TreeSet中的元素都处于有序状态。
TreeSet中的方法:

comparator():返回对此 set 中的元素进行排序的比较器;如果此 set 使用其元素的自然顺序(类实现了Comparable接口),则返回nullfirst():返回此 set 中当前第一个(最低)元素。

last(): 返回此 set 中当前最后一个(最高)元素。

lower(E e):返回此 set 中严格小于给定元素的最大元素;如果不存在这样的元素,则返回 nullhigher(E e):返回此 set 中严格大于给定元素的最小元素;如果不存在这样的元素,则返回 nullsubSet(E fromElement, E toElement):返回此 set 的部分视图,其元素从 fromElement(包括)到 toElement(不包括)。

headSet(E toElement):返回此 set 的部分视图,其元素小于toElement。

tailSet(E fromElement):返回此 set 的部分视图,其元素大于等于 fromElement。

TreeSet的排序方式

自然排序 Comparable接口

public interface Comparable<T> {
 
    public int compareTo(T o);
}

Java中提供了一个Comparable接口,如果试图将一个对象添加到TreeSet集合中,那么就必须实现Comparable接口。
TreeSet会调用集合中元素的compare()方法,然后将元素按照升序排序,即 通过compare()方法比较后比较大的元素放在后面。

定制排序 Comparator比较器

@FunctionalInterface
public interface Comparator<T> {

    int compare(T o1, T o2)
    
}

要实现定制排序,需要在创建TreeSet时,调用一个带参构造器,传入Comparator对象。并由该Comparator对象负责集合元素的排序逻辑,集合元素可以不必实现Comparable接口。

注意:如果向TreeSet中添加了一个可变对象后,在后面程序中修改了该对象,这将导致它与其他对象的大小顺序发生了改变,但TreeSet并不会再次调整它们。
例如:


    @Test
    public void test1(){
        TreeSet<Person> set = new TreeSet<>((o1, o2) -> o1.age-o2.age);

        Person p1 = new Person();
        p1.setAge(10);
        Person p2 =new Person();
        p2.setAge(30);
        Person p3 =new Person();
        p3.setAge(40);

        set.add(p1);
        set.add(p2);
        set.add(p3);

        
        System.out.println("初始年龄排序");
        System.out.println(set);
        //p1的年龄修改成50 最大
        p1.age = 60;
        System.out.println("修改p1年龄后集合排序");
        System.out.println(set);
        p2.age = 40;
        System.out.println("修改p2年龄后集合排序");
        System.out.println(set);
    }

在这里插入图片描述
可以看到元素的位置并没有发生变化,而且修改后元素的删除操作也可能不成功。
总之,尽量不要修改放入TreeSet集合中元素的关键实例变量。TreeSet的底层是TreeMap,所以说TreeSet的底层也是红黑树
另外,TreeSet也是非线程安全的。

TreeSet如何判断集合元素相等

TreeSet集合判断元素是否相等的唯一标准是:两个对象通过compare()方法是否返回0。如果返回0,那么TreeSet就认为这两个元素相等,不予加入集合内。

Set的性能

HashSet以Hash算法进行位置存储,特别适合添加 查询操作;LinkedHashSet要用链表来维护元素插入顺序,性能稍差,LinkedHashSet特别适合用于插入 删除 以及遍历操作。而TreeSet需要使用红黑树来维护元素的位置,性能最差。


Map

在这里插入图片描述

Map与Set 和 List的关系

与Set集合的关系

如果把Map里面的所有key放在一起看,它们就组成了一个Set集合(所有的key没有顺序,且不可重复)。事实上,Map中确实有一个keySet()方法,返回Map中所有的key组成的Set集合。

与List集合的关系

如果把Map里面的所有value放在一起看,它们又很像一个List(元素与元素可以重复,可以根据索引找到每个值),只是Map中不是使用整数而是使用另一个对象作为索引。

Map中的方法

在这里插入图片描述
由于Map中存放的元素都是键值对,所以每一个键值对必然存在一个映射关系。Map中采用Entry内部类来表示一个映射项,映射项包含key和value(我们总说键值对键值对 每一个键值对实际上就是一个Entry)

Entry中的方法:
在这里插入图片描述

Map的四种遍历方式

    @Test
    public void test1(){
        Map<String, String> map = new HashMap<String, String>();
        map.put("1", "value1");
        map.put("2", "value2");
        map.put("3", "value3");

        //第一种 获取其entry的Set集合,再使用迭代器遍历这个Set集合
        Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()){
            Map.Entry<String, String> next = iterator.next();
            System.out.println("key: "+next.getKey()+"  value: "+next.getValue());
        }


        //第二种 获取其所有键值对的Set集合,增强For循环遍历
        for (Map.Entry<String, String> entry : map.entrySet()) {
            System.out.println("key: "+entry.getKey()+"  value: "+entry.getValue());
        }

        //第三种 获取Map种key的集合 根据key去获得value
        for (String key : map.keySet()) {
            System.out.println("key: "+key+"  value: "+map.get(key));
        }

        //第四种 获取Map的所有键值对 将遍历Map的问题转换成遍历Set的问题
        Set<Map.Entry<String, String>> entrySet = map.entrySet();
        entrySet.forEach(System.out::println);
        entrySet.stream().map(Map.Entry::getKey).forEach(System.out::println);

    }


HashMap

HashMap判断key与value相等的标准

  • key的判断标准:与HashSet相同。(HashSet其实就是一个特殊的HashMap);
  • value的判断标准:两个元素equals()返回true就认为相等;

注意: 如果加入到HashMap中的key是一个可变对象,在加入到集合后又修改key的成员变量的值,可能导致hashCode()和equals()的比较结果发生变化,无法访问到该key。一般情况下不要修改。


    @Test
    public void test2(){
        Map<Person, String> map = new HashMap<>();
        Person lzh1 = new Person("lzh1", 20);
        Person lzh2 = new Person("lzh2", 20);
        map.put(lzh1, "lzh1");
        map.put(lzh2, "lzh2");

        map.entrySet().forEach(System.out::println);

        //改变lzh2变量
        lzh2.setName("lzh1");

        System.out.println();
        map.entrySet().forEach(System.out::println);

        //已经访问不到key为lzh2的元素
        String s = map.get(lzh2);
        System.out.println();
        System.out.println(s);

        String s1 = map.get(lzh1);
        System.out.println(s1);

        lzh2.setName("lzh2");
        String s2 = map.get(lzh2);
        System.out.println();
        System.out.println(s2);
    }


执行结果:
在这里插入图片描述

HashMap的本质

HashMap的构造函数

// 默认构造函数。
HashMap()

// 指定“容量大小”的构造函数
HashMap(int capacity)

// 指定“容量大小”和“加载因子”的构造函数
HashMap(int capacity, float loadFactor)

// 包含“子Map”的构造函数
HashMap(Map<? extends K, ? extends V> map)

  1. 容量(默认为16)即hash表的长度;
  2. 装载因子(默认为0.75)是hash表在自动扩容之前容量可以达到多满的一种尺度。

当hash表中的条目数大于hash表容量乘以装在因子时,hash表会进行resize操作(重建内部的数据结构),进而hash表将具有大约两倍的桶数;

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-08-23 16:31:00  更:2021-08-23 16:32:53 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 9:46:52-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码