zoukankan      html  css  js  c++  java
  • HashSet、LinkedHashSet、SortedSet、TreeSet

    HashSet概况:

    public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable

    HashSet源码:

    常量:

    transient HashMap<E,Object> map;  // HashSet底层实现就是HashMap 
    private static final Object PRESENT = new Object(); // 这个PRESENT相当于HashMap中的value值

    构造方法:

    public HashSet() {
        map = new HashMap<>();
    }
        
    public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }
        
    public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }

     add(E e):通过 map.put() 方法来添加元素,该方法如果新插入的key不存在,则返回null,如果新插入的key存在,则返回原key对应的value值(注意新插入的value会覆盖原value值)。

         也就是说 HashSet 的 add(E e) 方法,会将 e 作为 key,PRESENT 作为 value 插入到 map 集合中,如果 e 不存在,则插入成功返回 true;如果存在,则返回false。

    public boolean add(E e) {            
        return map.put(e, PRESENT)==null;
    }                                    

    HashSet对象重复问题以及hashCode(),equal()方法:

    关于set对象里面的重复:

    class Person {
        private String name;
        private int age;
            ...... 
    }
    public class SetTest {
        public static void main(String[] args) {
            Set<Person> set = new HashSet<>();
            set.add(new Person("张三", 14));
            set.add(new Person("李四", 16));
            set.add(new Person("张三", 14));
            set.add(new Person("李四", 16));
            set.add(new Person("张三", 14));
            System.out.println(set);
        }
    }
    View Code

     明显6个元素都输出,因为每个对象的地址值都不一样嘛(每个对象的那个hashcode值不一样)

    class Person {
        private String name;
        private int age;
        ......
        @Override                                                
        public boolean equals(Object obj){                       
           Person p = (Person)obj;                              
           return this.name.equals(p.name) && this.age == p.age;
       }                                                        
    }
    View Code

    复写equals方法,一样没有用,因为调用equals方法之前,一定会调用hashcode方法,因为每个对象里面的hashcode值 是不一样的,所以当发现hashcode值不一样时,就不再调用equals方法了(无论这个时候equals方法后是否一样),只有当hashcode值相同的时候才去调用equals方法。但是,如果只重写了hashcode方法不重写equals方法,虽然hash值相同,但是因为每个对象的地址值不一样,所以还是输出6个元素的。所以一定要复写equals方法的同时,一定要复写hashcode方法

    /*                                                                                   
     * 每个新建对象的hashcode不一样,所以要复写hashcode,这样的话这四个对象的hashcode就一样了                           
     * 因为hashcode的底层是hash码的                                                              
     * 只有hashcode一样的时候,才会调用equals方法,这样的话就少调用了很多次equals方法                                 
     * 所以要尽量是hashcode不一样                                                                 
     */                                                                                  
    class Person {
        private String name;
        private int age;
        ...... 省略构造方法,set,get方法,toString方法......
        @Override                                                
        public boolean equals(Object obj){                       
           Person p = (Person)obj;                              
           return this.name.equals(p.name) && this.age == p.age;
       }
       @Override
       public int hashCode() {
          return 10;
       }                                                        
    }
    View Code

     只有hashcode方法一样时,才调用equals方法

    一定要记得,要同时复写两个方法hashcode以及equals方法,set里面的对象集合才不会重复

    !!!最新发现:因为hashset的底层是hashmap实现的,通过了解我们可以知道,hashmap的key值是先hashcode再取模,当这个下标一样时,才再计算有没有相等;

                     所以,以前死记的hashcode再equals,现在明白了吧

    LinkedHashSet:

    public class LinkedHashSet<E> extends HashSet<E> implements Set<E>, Cloneable, java.io.Serializable

    构造方法

    public LinkedHashSet(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor, true);
    }
        
    public LinkedHashSet(int initialCapacity) {
        super(initialCapacity, .75f, true);
    }
        
    public LinkedHashSet() {
        super(16, .75f, true);
    }

     关于add()方法

    HashSet<Integer> set = new LinkedHashSet<>();
    Set<Integer> set02 = new LinkedHashSet<>();
    set.add(1);
    set02.add(1);
    1,首先调用子类LinkedHashSet的add方法,发现没有
    2,然后调用上一层父类HashSet的add方法,发现有了
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;  // 注意:这里的map的实现类为LinkedHashMap
    }
    注意:
    1)无论接收者是什么,一定要是你老豆,或者老豆的老豆啊,或者更老豆
    2)但是接收者,也就是老豆,一定要定义这个方法才行啊:
    最典型:    LinkedList<String> list = new LinkedList<>();   list.addFirst("a");    
    不能  List<String> list = new LinkedList<>();     因为List接口没有定义addFirst这个方法()
    View Code

    其它方法remove,contains也类似

    SortedSet:

    public interface SortedSet<E> extends Set<E>

    As the name of the classsuggests, the sorting is accomplished by a tree data structure(The current implementation uses a red-black tree).

    Every time an element is added to a tree, it is placed into its proper sorting position. Therefore, the iterator always visits the elements in sorted order.

    Adding an element to a tree is slower than adding it to a hash table. But it is still much faster than checking for duplicates in an array or linked list.

    TreeSet:

    public class LinkedListTest {
        public static void main(String[] args) {
            Set<Person> ts = new TreeSet<Person>();
            ts.add(new Person("张三", 23));
            ts.add(new Person("李四", 13));
            ts.add(new Person("周七", 13));
            ts.add(new Person("王五", 43));
            ts.add(new Person("赵六", 33));
            System.out.println(ts);
        }
    }
    
    class Person implements Comparable<Person>{
        private String name;
        private int age;
        public Person(String name, int age) {
            this.age = age;
            this.name = name;
        }
        @Override
        public int compareTo(Person o) {
            return 0; // 返回值写死为0,元素值每次比较,都认为是相同的元素,这时就不再向TreeSet中插入除第一个外的新元素。所以TreeSet中就只存在插入的第一个元素。
            return -1; // 返回值写死为1,元素值每次比较,都认为新插入的元素比上一个元素大,于是二叉树存储时,会存在根的右侧,读取时就是正序排列的。
            return 1; // 返回值写死为-1,元素值每次比较,都认为新插入的元素比上一个元素小,于是二叉树存储时,会存在根的左侧,读取时就是倒序序排列的。
        }
        @Override
        public String toString() {
            return "Person [name=" + name + ", age=" + age + "]";
        }
    }
    View Code

    所以要定义implements Comparable<Person>

    class Person implements Comparable<Person>{
        private String name;
        private int age;
        public Person(String name, int age) {
            this.age = age;
            this.name = name;
        }
        @Override
        public int compareTo(Person o) {
            int num = this.age - o.age;                //年龄是比较的主要条件
            return num == 0 ? this.name.compareTo(o.name) : num;//姓名是比较的次要条件
        }
        @Override
        public String toString() {
            return "Person [name=" + name + ", age=" + age + "]";
        }
    }
    View Code

    或者三层比较:

    public int compareTo(Person o) {
            int length = this.name.length() - o.name.length();                //比较长度为主要条件
            int num = length == 0 ? this.name.compareTo(o.name) : length;    //比较内容为次要条件
            return num == 0 ? this.age - o.age : num;                        //比较年龄为次要条件
    }
    View Code

    NavigableSet:

    参考文档:

     1)JDK1.8源码(八)——java.util.HashSet 类

  • 相关阅读:
    ZCMU 暑期练习赛【识别】
    P1024 一元三次方程求解
    P1059 明明的随机数
    P2670 扫雷游戏
    ESP32手动搭建Arduino环境
    基于arduino的气象站
    序列归并
    结构体数组排序:1004 成绩排名 【pta】
    C语言:大数求和
    centos7 安装mongodb replica set 集群搭建
  • 原文地址:https://www.cnblogs.com/ericguoxiaofeng/p/8635632.html
Copyright © 2011-2022 走看看