• jdk集合常用方法分析之HashSet和TreeSet


    HashSet常用方法介绍

    public boolean add(E e)
    public boolean isEmpty()
    void clear()
    public Iterator<E> iterator()
    int size()

    详细方法详见JDK帮助文档

    Object的equals方法和hashCode方法

    1、 Object类equals方法的特点:

      a) 自反性:x.equals(x)应该返回true

      b) 对称性:x.equels(y)为true,那么y.equals(x)也为true

      c) 传递性:x.equels(y)为true并且y.equels(z)为true,那么x.equels(z)也应该为true

      d) 一致性:x.equels(y)的第一次调用为true,那么x.equels(y)的第二次、第三次、第n次调用也应该为true,前提条件是在比较之间没有修改x也没有修改y

      e) 对于非空引用x,x.equels(null)返回false

    2、 Object类hashCode方法的特点:

      a) 在java应用的一次执行过程中,对于同一个对象的hashCode方法的多次调用,他们应该返回同样的值(前提是该对象的信息没有发生变化);

      b) 对于两个对象来说,如果使用equals方法比较返回true,那么这两个对象的hashCode值一定是相同的

      c) 对于两个对象来说,如果使用equals方法比较返回false,那么这两个对象的hashCode值不要求一定不同(可以相同,也可以不同),但是如果不同则可以提高应用的性能;

      d) 对于Object类来说,不同Object对象的hashCode的值是不同的(Object类的hashCode值表示的是对象的地址);Object的toString()方法默认调用的是:

    getClass().getName() + '@' + Integer.toHexString(hashCode())

    3、 如果我们重写equals方法,那么也要重写hashCode方法,反之亦然;

    HashSet常用方法源码分析

    构造方法

    public HashSet() {
        map = new HashMap<E,Object>();
    }
    
    private transient HashMap<E,Object> map;

    add()

    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
    
    private static final Object PRESENT = new Object(); //同一个set中的PRESENT对象一致

    remove()

    public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }

    size()

    public int size() {
        return map.size();
    }

    iterator ()

    public Iterator<E> iterator() {
        return map.keySet().iterator();
    }

    常用方法源码分析总结

    1、 set可以添加null;

    2、 在使用HashSet时,hashCode()方法会得到调用,判断已经存储在集合中的对象的hashCode值是否与添加的对象的hashCode值一致,如果不一致,直接添加进去;如果一致,再进行equals方法的比较,equals方法如果返回true,表示对象已经添加进去了,就不会再添加新的对象,否则就加进去;

    3、 HashSet底层是使用HashMap实现的,当调用add方法将对象添加到Set中时,实际上是将该对象作为底层所维护的Map对象的key,而value则都是同一个Object对象(该对象我们用不上)。在HashMap中,同一个key,后添加的value会覆盖前一个value;

    4、 当重写equals方法的时候必须要重写hashCode方法,如果一个类的两个对象的equals方法比较结果为true,那么该两个对象必须具有相同的hashCode值

    HashSet示例分析

    示例1:添加两个相同的对象

    public class HashSetDemo03 {
        public static void main(String[] args) {
            HashSet set = new HashSet();
            People p1 = new People("zhangsan");// p1存放的是zhangsan的引用地址
    
            set.add(p1);
            set.add(p1);
            System.err.println(set);
        }
    }
    
    public class People {
        String name;
        public People(String name) {
            this.name = name;
        }
        public String toString() {
            return this.name;
        }
    }

    分析:当往set中添加Person对象的时候,先添加进去p1,在第二次还是添加p1的时候,会先调用hashCode方法(同一对象的hashCode值当然相同),再去判断equals()方法,同一对象的equals()方法肯定是true的,所以加不进去。

    示例2:添加两个相同的字符串

    public class HashSetDemo04 {
        public static void main(String[] args) {
            HashSet set = new HashSet();
            String s1 = new String("a");
            String s2 = new String("a");
            set.add(s1);
            set.add(s2);
            System.out.println(set);
        }
    }

    分析:当往set中添加是s1的时候,先添加进去s1,在第二次还是添加s1的时候,会先调用hashCode方法,String对象已经重写了hashCode()方法(根据给定的字符串用一个公式运算),hashCode值是相同的,再去判断equals()方法,String的equals()方法比较的是字符串的内容,所以equals()为true,所以加不进去。

    示例3:重写equals和hashcode后对比

    public class HashSetDemo05 {
        public static void main(String[] args) {
            HashSet set = new HashSet();
            Student s1 = new Student("zhangsan");
            Student s2 = new Student("zhangsan");
            set.add(s1);
            set.add(s2);
            System.out.println(set);
        }
    }
    public class Student {
        String name;
        public Student(String name) {
            this.name = name;
        }
        public String toString() {
            return name;
        }
    }

    分析:

    未重写equals和hashcode方法之前:s1和s2是不同的两个对象,由于hashCode存放的是引用的地址,所以他们是不同的,就直接加进去了。

    重写equals()和hashCode()方法之后,hashCode就按照name进行取值,所以他们的hashCode相等,然后比较equals()方法,而我们自己也按照name重写了equals()方法。所以equals为true,就不会加进去。

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((name == null) ? 0 : name.hashCode());
            return result;
        }
    
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Student other = (Student) obj;
            if (name == null) {
                if (other.name != null)
                    return false;
            } else if (!name.equals(other.name))
                return false;
            return true;
        }

    示例3:HashSet常用迭代方法

    public class HashSetDemo06 {
        public static void main(String[] args) {
            HashSet set = new HashSet();
            set.add("a");
            set.add("b");
            set.add("c");
            set.add("d");
            set.add("e");
            
            Iterator iter = set.iterator();
            while (iter.hasNext()) {
                String value = (String) iter.next();
                System.out.println(value);
            }
            
            System.out.println("--------------");
        
            for (Iterator iterator = set.iterator(); iterator.hasNext();) {
                String s = (String) iterator.next();
                System.out.println(s);
            }
        }
    }

    TreeSet

    HashSet是在输出的时候是没有顺序的,而TreeSet是能够按照指定的顺序进行输出的。

    TreeSet的构造方法

    public TreeSet(Comparator<? super E> comparator)

    在构造方法中需要我们事先自定义好一个实现Comparator接口的方法,并重写它的int compare(T o1,T o2)方法。


    如何定义自己的Comparator

    public class TreeSetDemo02 {
        public static void main(String[] args) {
            TreeSet<Order> set = new TreeSet<Order>(new OrderComparator());
            set.add(new Order(10));
            set.add(new Order(20));
            set.add(new Order(30));
            set.add(new Order(40));
    
            for (Order order : set) {
                System.out.println(order);
            }
        }
    }
    
    class OrderComparator implements Comparator<Order> {
        @Override
        public int compare(Order o1, Order o2) {
            return -(o1.count - o2.count);
        }
    }
    
    class Order {
        int count;
        public Order(int count) {
            this.count = count;
        }
        public String toString() {
            return String.valueOf(count);
        }
    }

    我们可以在OrderComparator类中的compare方法中按照自己的意愿进行重写以实现升序或者降序排列。

    public class TreeSetDemo03 {
        public static void main(String[] args) {
            TreeSet set = new TreeSet(new MyComparator());
            set.add("A");
            set.add("F");
            set.add("E");
            set.add("D");
            set.add("a");
            set.add("C");
            set.add("B");
            for (Iterator iterator = set.iterator(); iterator.hasNext();) {
                String s = (String) iterator.next();
                System.out.println(s);
            }
        }
    }
    
    class MyComparator implements Comparator {
        public int compare(Object o1, Object o2) {
            String s1 = (String) o1;
            String s2 = (String) o2;
            return -s1.compareTo(s2);
        }
    }

    我们可以在MyComparator类中的compare方法中按照字母的降序排列。

  • 相关阅读:
    用python分析1225万条淘宝数据,终于搞清楚了我的交易行为
    Python 中 3 个不可思议的返回
    2020年最新的过某宝滑块验证技术,Python大牛轻松搞定技术难题
    改改Python代码,运行速度还能提升6万倍
    Python的10个神奇的技巧
    Python 在线免费批量美颜,妈妈再也不用担心我 P 图两小时啦
    全程干货,requests模块与selenium框架详解
    Python-选择器Xpath,Css,Re
    Python-Django 模型层-多表查询-2
    Python-Django 模型层-多表查询
  • 原文地址:https://www.cnblogs.com/luogankun/p/3976536.html
Copyright © 2020-2023  润新知