hashCode、hashTable、HashSet、HashMap的用法与区别

hashCode

hashcode是一种编码方式,在Java中,每个对象都会有一个hashcode,Java可以通过这个hashcode来识别一个对象。 至于hashcode的具体编码方式,比较复杂(事实上这个编码是可以由程序员通过继承和接口的实现重写的),实际上就是是一个十进制的整数,由系统随机给出(就是对象的地址值,是一个逻辑地址,是模拟出来的地址,不是数据实际存储的地址)
在Object类有一个方法,可以获取对象的哈希值
public native int hashCode();
native: 代表该方法调用的是本地操作系统的方法

   public static void main(String[] args) {
        Student one = new Student();
        //获取对象的hashCode
        int hashCode = Objects.hashCode(one);
        System.out.println(hashCode);//961
        //打印对象的地址值
        System.out.println(one);//cn.itcat.test.MapDemo.Student@3c1
    }
    ==================================================================
    运行结果:
     961
    cn.itcat.test.MapDemo.Student@3c1

根据结果可以知道:
对象的地址值 = 类的路径 + @ + 哈希值的十六进制数

—————————————————————————————————

hashTable

维基百科的解释:

散列表(Hashtable,也叫哈希表),是根据键(Key)而直接访问在内存储存位置的数据结构。也就是说,它通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度。这个映射函数称做散列函数,存放记录的数组称做散列表。

简而言之,哈希表实际上是一种存储数据的数据结构。在JDK1.8之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。 但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,哈 希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找 时间。 简单的来说,哈希表是由数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的,如下图所示。
hashCode、hashTable、HashSet、HashMap的用法与区别_第1张图片
—————————————————————————————————

HashSet集合

HashSet 是 Set 接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的(即存取顺序 不一致)。 HashSet 底层的实现其实是一个 java.util.HashMap 支持。HashSet 是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。保证元素唯一性 的方式依赖于: hashCode 与 equals 方法。通过下面这流程图,可以清楚的知道HashSet是如何存储数据,并实现数据的唯一性和无序性。
hashCode、hashTable、HashSet、HashMap的用法与区别_第2张图片
可以归纳出HashSet集合有以下几个特点
1、不允许存储重复的元素
2、没有索引,没有带索引的方法,也不能使用普通的for循环遍历
3、是一个无序的集合,存储元素和取出元素的顺序可能不一致
4、底层是一个哈希表结构(查询的速度非常快)
JDK1.8引入红黑树大程度优化了HashMap的性能,那么对于我们来讲保证HashSet集合元素的唯一, 其实就是根据对象的hashCode和equals方法来决定的。

HashSet存储自定义类型元素

package cn.itcat.test.Set;
import java.util.HashSet;
//利用HashSet集合存储自定义类型元素
//姓名和年龄一样的对象就是重复元素,就要重写hashCode方法和equals方法
public class demo01 {
    public static void main(String[] args) {
        HashSet<Person> set = new HashSet<>();
        Person one = new Person("张三",24);
        Person two = new Person("赵四",25);
        Person three = new Person("张三",24);
        Person four = new Person("张三",24);
        set.add(one);
        set.add(two);
        set.add(three);
        set.add(four);
        System.out.println(set);
    }
}
运行结果:
[Person{name='赵四', age=25}, Person{name='张三', age=24}]

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

@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);
        //如果name和age内容一样的话,就有相同的哈希地址
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

—————————————————————————————————

LinkedHashSet集合

HashSet保证元素唯一,可是元素存放进去是没有顺序的,那么我们要保证有序,怎么办呢? 在HashSet下面有一个子类 java.util.LinkedHashSet ,底层是一个哈希表(数组 + 链表/红黑树) + 链表的数据结构;多了一条链表,记录元素的存储顺序,保证元素有序
演示代码如下:

public class LinkedHashSetDemo {
    public static void main(String[] args) {
        HashSet<String> hs = new HashSet<>();
        hs.add("tom");
        hs.add("ojk");
        hs.add("cxvb");
        hs.add("feel");
        hs.add("tom");
        System.out.println(hs);//[ojk, tom, feel, cxvb] 无序,不允许重复

        LinkedHashSet<String> link = new LinkedHashSet<>();
        link.add("tom");
        link.add("ojk");
        link.add("cxvb");
        link.add("feel");
        link.add("tom");
        System.out.println(link);//[tom, ojk, cxvb, feel] 有序,不允许重复
    }
}

—————————————————————————————————

Map集合

现实生活中,我们常会看到这样的一种集合:IP地址与主机名,身份证号与个人,系统用户名与系统用户对象等, 这种一一对应的关系,就叫做映射。Java提供了专门的集合类用来存放这种对象关系的对象,即 java.util.Map 接 口。我们通过查看 Map 接口描述,发现 Map 接口下的集合与 Collection 接口下的集合,它们存储数据的形式不同,如下图。hashCode、hashTable、HashSet、HashMap的用法与区别_第3张图片
Collection集合与Map集合的区别
Collection 中的集合,元素是孤立存在的(理解为单身),向集合中存储元素采用一个个元素的方式存储。
Map 中的集合,元素是成对存在的(理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的 值。
Collection 中的集合称为单列集合, Map 中的集合称为双列集合。 需要注意的是, Map 中的集合不能包含重复的键,值可以重复;每个键只能对应一个值。
Map接口常用方法
public V put(K key, V value) : 把指定的键与指定的值添加到Map集合中。 public V remove(Object key) : 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的 值。
public V get(Object key) 根据指定的键,在Map集合中获取对应的值。 public Set keySet() : 获取Map集合中所有的键,存储到Set集合中。
public Set> entrySet() : 获取到Map集合中所有的键值对对象的集合(Set集合)
—————————————————————————————————

HashMap集合

HashMap:存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需 要重写键的hashCode()方法、equals()方法。

HashMap存储自定义类型键值的演示:

package cn.itcat.test.MapDemo;
import java.util.HashMap;
import java.util.Set;
public class HashMapDemo {

    public static void main(String[] args) {
        HashMap<Student,String> hashMap = new HashMap<>();
        hashMap.put(new Student("詹姆斯",34), "湖人");
        hashMap.put(new Student("库里",31), "勇士");
        hashMap.put(new Student("伦纳德",31), "快船");
        hashMap.put(new Student("洛瑞",30), "猛龙");

        Set<Student> keySet = hashMap.keySet();
        for (Student stu: keySet
             ) {
            String str = hashMap.get(stu);
            System.out.println(stu + "效力于" + str);
        }

    }
}

运行结果:

31岁的库里效力于勇士
31岁的伦纳德效力于快船
30岁的洛瑞效力于猛龙
34岁的詹姆斯效力于湖人

—————————————————————————————————

LinkedHashMap集合

LinkedHashMap:HashMap下有个子类LinkedHashMap,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;通过哈希表结构可以保证的键的唯一、不重复,需要重写键的 hashCode()方法、equals()方法。

public class LinkedHashMapDemo {
    public static void main(String[] args) {
        public static void main(String[] args) {
        LinkedHashMap<Student,String> hashMap = new LinkedHashMap<>();
        hashMap.put(new Student("詹姆斯",34), "湖人");
        hashMap.put(new Student("库里",31), "勇士");
        hashMap.put(new Student("伦纳德",31), "快船");
        hashMap.put(new Student("洛瑞",30), "猛龙");

        Set<Student> keySet = hashMap.keySet();
        for (Student stu: keySet
             ) {
            String str = hashMap.get(stu);
            System.out.println(stu + "效力于" + str);
        }
    }
}

运行结果:

34岁的詹姆斯效力于湖人
31岁的库里效力于勇士
31岁的伦纳德效力于快船
30岁的洛瑞效力于猛龙

可以观察到读取的数据与存入到集合中的顺序一致。

你可能感兴趣的:(Java基础,java,哈希算法,链表)