• java 基础 --Collection(Set)


    注意:

    如果hashSet存储自定义对象,一定要重写hashCode()&&equals()

    如果TreeSet存储自定义对象,让元素所属的类实现自然排序接口Comparable,并重写CompareTo()/让集合的构造方法接收一个比较器接口的子类对象Comparator

    结构:
    Set:
    |--HashSet(底层结构是Hash表(Hash表:元素是链表的数组,Hash表依赖于Hash值存储))
    |--LinkedHashSet(底层机构是链表和Hash表)
    |--TreeSet(底层是二叉树结构(红黑树是一种自平衡的二叉树))

    详解:
    Set
    无序(存储顺序和取出顺序不一致),唯一(虽然set集合的元素无序,但是作为集合来说肯定有它的存储顺序,而你的存储顺序和它的顺序一致,这代表不了有序。)
    |-- HashSet

    HashSet底层依赖的是hashCode()和equals()
    所以存储自定义对象时,对象需要重写hashCode()和equals()来保证唯一性(自动重写即可)

    HashSet 底层源码:
    ①HashSet<String> set = new HashSet<String>();
       public HashSet() {
            map = new HashMap<>();
        }
    ②set.add("atomic");
    private static final Object PRESENT = new Object();
    public boolean add(E e) {
            return map.put(e, PRESENT)==null;
        }
    源码结论:HashSet是一个HashMap,它的值存储在HashMap的Key中,因此hashSet数据是唯一的

    if (e.hash == hash && //先看hashCode(),如果hash值相同,不添加元素
        ((k = e.key) == key || (key != null && key.equals(k)))){break;} //如果元素值/地址值相同(元素重复),也不添加元素
    p = e;//以上三种情况外才添加元素

    TreeSet:自然排序,唯一(TreeSet底层就是treeMap
    API: A NavigableSet implementation based on a TreeMap
    ①如果是基本类型,会自动进行排序,去重
    ②如果是自定义类型,需要重写...

    真正的比较依赖于元素的CompareTo()方法,而这个方法是定义在Comparable里面的,
    所以要想重写该方法,就必须先实现COmparable接口,这个接口表示的就是自然排序
    底层是二叉树结构(红黑树是一种自平衡的二叉树)。
    二叉树三种遍历:
    中序遍历:先左子树,后根节点,再右子树
    前序遍历:先根节点,后左子树,再右子树
    后序遍历:先左子树,后右子树,再根节点

    源码:①TreeSet<String> set = new TreeSet<String>();
       ②set.add("d");

    public boolean add(E e) {
            return m.put(e, PRESENT)==null;
        }
    private transient NavigableMap<E,Object> m;
    API: A NavigableSet implementation based on a TreeMap

    TreeMap的put();
      public V put(K key, V value) {
            Entry<K,V> t = root;
            if (t == null) {
                compare(key, key); // type (and possibly null) check
    
    

                root = new Entry<>(key, value, null);
                size = 1;
                modCount++;
                return null;
            }
            int cmp;
            Entry<K,V> parent;
            // split comparator and comparable paths
            Comparator<? super K> cpr = comparator;
            if (cpr != null) {
                do {
                    parent = t;
                    cmp = cpr.compare(key, t.key);//key-t.key
                    if (cmp < 0)                  // key小放左边
                        t = t.left;
                    else if (cmp > 0)             // key大放右边
                        t = t.right;
                    else
                        return t.setValue(value); //相等,不放
                } while (t != null);
            }
            else {
                if (key == null)
                    throw new NullPointerException();
                @SuppressWarnings("unchecked")
                    Comparable<? super K> k = (Comparable<? super K>) key;
                do {
                    parent = t;
                    cmp = k.compareTo(t.key);
                    if (cmp < 0)
                        t = t.left;
                    else if (cmp > 0)
                        t = t.right;
                    else
                        return t.setValue(value);
                } while (t != null);
            }
            Entry<K,V> e = new Entry<>(key, value, parent);
            if (cmp < 0)
                parent.left = e;
            else
                parent.right = e;
            fixAfterInsertion(e);
            size++;
            modCount++;
            return null;
        }


    总结:TreeSet集合保证元素排序和唯一性的原理
    唯一性:根据比较的返回值是否为0来决定的
    排序:
    A:自然排序(元素具备比较性)
    让元素所属的类实现自然排序接口Comparable
    B:比较器排序(集合具备比较性)
    让集合的构造方法接收一个比较器接口的子类对象Comparator
    举例:A:
    // 方式1:
    TreeSet<Student> ts1 = new TreeSet<Student>();
    // 如果一个类想要进行自然排序,就必须实现自然排顺序接口
    public class Student implements Comparable<Student>{
      private String name;
      private int age;
      @Override
                   public int compareTo(Student o) {
                     // 按年龄,主要条件
                      int num = this.age - o.age;
                      //  次要条件,年龄相同时好要看名字是否相同
                      //  如果名字和年龄都相同,才是一个元素
                      int num2 = num == 0?this.name.compareTo(o.name):num;
                      return num2;

    /* 按名字排序
                   @Override
                   public int compareTo(Student o) {
      
                      //  按姓名,主要条件
                      int num = this.name.length() - o.name.length();
                      //  姓名长度相同不代表内容相同
                      int num2 = num == 0?this.name.compareTo(o.name):num;
                      //  姓名长度和内容相同,不代表年龄相同
                      int num3 = num2 == 0?this.age-o.age:num2;
                      return num3;
                      }
    */
              }

    B:
    //方式2
    TreeSet<Student> ts2 = new TreeSet<Student>(new MyComparator());
    // 实现Comparator()接口
                 public class MyComparator implements Comparator<Student> {
                  @Override
                    public int compare(Student o1, Student o2) {
                        // 按姓名,主要条件
                        int num = o1.getName().length() - o2.getName().length();
                        // 姓名长度相同不代表内容相同
                        int num2 = num == 0 ? o1.getName().compareTo(o2.getName()) : num;
                        // 姓名长度和内容相同,不代表年龄相同
                        int num3 = num2 == 0 ? o1.getAge() - o2.getAge() : num2;
                        return num3;
                    }
                }
    //方式3(匿名类方式)
                TreeSet<Student> ts3 = new TreeSet<Student>(new Comparator<Student>() {
                    @Override
                    public int compare(Student o1, Student o2) {
                        // 按姓名,主要条件
                        int num = o1.getName().length() - o2.getName().length();
                        // 姓名长度相同不代表内容相同
                        int num2 = num == 0 ? o1.getName().compareTo(o2.getName()) : num;
                        // 姓名长度和内容相同,不代表年龄相同
                        int num3 = num2 == 0 ? o1.getAge() - o2.getAge() : num2;
                        return num3;
                    }
                });
  • 相关阅读:
    RabbitMQ入门-消息订阅模式
    RabbitMQ入门-消息派发那些事儿
    RabbitMQ入门-高效的Work模式
    RabbitMQ入门-从HelloWorld开始
    RabbitMQ入门-初识RabbitMQ
    CMake INSTALL 命令设置exe dll lib的安装位置
    VS调试DLL代码使用”附加到进程“
    模型自身面片重合引起的闪烁破损解决方法
    地球表面使用世界坐标系绘制物体闪烁破损处理方法
    3dmax osg格式导出插件 osgExp OpenSceneGraph Max Exporter
  • 原文地址:https://www.cnblogs.com/ysloong/p/6249722.html
Copyright © 2020-2023  润新知