• List、Set、Map的学习


    参考:

    https://www.cnblogs.com/mlfz/p/10435954.html

    https://blog.csdn.net/zhangqunshuai/article/details/80660974

    https://www.cnblogs.com/IvesHe/p/6108933.html

    LIST,SET,MAP理解

    集合

    集合与数组

    数组(可以存储基本数据类型)是用来存现对象的一种容器,但是数组的长度固定,不适合在对象数量未知的情况下使用。

    集合(只能存储对象,对象类型可以不一样)的长度可变,可在多数情况下使用。

    集合中接口和类的关系

    Collection接口是集合类的根接口,Java中没有提供这个接口的直接的实现类。但是却让其被继承产生了两个接口,就是Set和List。Set中不能包含重复的元素。List是一个有序的集合,可以包含重复的元素,提供了按索引访问的方式。

    Map是Java.util包中的另一个接口,它和Collection接口没有关系,是相互独立的,但是都属于集合类的一部分。Map包含了key-value对。Map不能包含重复的key,但是可以包含相同的value。

    Iterator所有的集合类,都实现了Iterator接口,这是一个用于遍历集合中元素的接口,主要包含以下三种方法:
    1.hasNext()是否还有下一个元素。
    2.next()返回下一个元素。
    3.remove()删除当前元素。

     大致继承关系如下(图片来自网络)

    list,set,map对比

    接口子接口是否有序是否允许元素重复
    Collection             
    List    ArrayList
           LinkedList
           Vector
    Set AbstractSet
       HashSet
       TreeSet 是(用二叉排序树)
    Map AbstractMap 使用key-value来映射和存储数据,key必须唯一,value可以重复
       HashMap  
       TreeMap 是(用二叉排序树) 使用key-value来映射和存储数据,key必须唯一,value可以重复

    LIST(有序、可重复)

    List里存放的对象是有序的,同时也是可以重复的,List关注的是索引,拥有一系列和索引相关的方法,查询速度快。因为往list集合里插入或删除数据时,会伴随着后面数据的移动,所有插入删除数据速度慢。

    ArrayList

      ArrayList是基于数组的,在初始化ArrayList时,会构建空数组(Object[] elementData={})。ArrayList是一个无序的,它是按照添加的先后顺序排列,当然,他也提供了sort方法,如果需要对ArrayList进行排序,只需要调用这个方法,提供Comparator比较器即可

    add操作:

      1)如果是第一次添加元素,数组的长度被扩容到默认的capacity,也就是10.

      2) 当发觉同时添加一个或者是多个元素,数组长度不够时,就扩容,这里有两种情况:

      只添加一个元素,例如:原来数组的capacity为10,size已经为10,不能再添加了。需要扩容,新的capacity=old capacity+old capacity>>1=10+10/2=15.即新的容量为15。

      当同时添加多个元素时,原来数组的capacity为10,size为10,当同时添加6个元素时。它需要的min capacity为16,而按照capacity=old capacity+old capacity>>1=10+10/2=15。new capacity小于min capacity,则取min capacity。

      对于添加,如果不指定下标,就直接添加到数组后面,不涉及元素的移动,如果要添加到某个特定的位置,那需要将这个位置开始的元素往后挪一个位置,然后再对这个位置设置。

    Remove操作:

     Remove提供两种,按照下标和value。

      1)remove(int index):首先需要检查Index是否在合理的范围内。其次再调用System.arraycopy将index之后的元素向前移动。

      2)remove(Object o):首先遍历数组,获取第一个相同的元素,获取该元素的下标。其次再调用System.arraycopy将index之后的元素向前移动。

    Get操作:

      这个比较简单,直接对数组进行操作即可。

    LinkedList

      LinkedList是基于链表的,它是一个双向链表,每个节点维护了一个prev和next指针。同时对于这个链表,维护了first和last指针,first指向第一个元素,last指向最后一个元素。LinkedList是一个无序的链表,按照插入的先后顺序排序,不提供sort方法对内部元素排序。

    Add元素:

      LinkedList提供了几个添加元素的方法:addFirst、addLast、addAll、add等,时间复杂度为O(1)。

    Remove元素:

      LinkedList提供了几个移除元素的方法:removeFirst、removeLast、removeFirstOccurrence、remove等,时间复杂度为O(1)。

    Get元素:

      根据给定的下标index,判断它first节点、last直接距离,如果index<size(数组元素个数)/2,就从first开始。如果大于,就从last开始。这个和我们平常思维不太一样,也许按照我们的习惯,从first开始。这也算是一点小心的优化吧。

    遍历

    在类集中提供了以下四种的常见输出方式:

    1)Iterator:迭代输出,是使用最多的输出方式。

    2)ListIterator:是Iterator的子接口,专门用于输出List中的内容。

    3)foreach输出:JDK1.5之后提供的新功能,可以输出数组或集合。

    4)for循环

    代码示例如下:

    for的形式:for(int i=0;i<arr.size();i++){...}

    foreach的形式: for(int i:arr){...}

    iterator的形式:
    Iterator it = arr.iterator();
    while(it.hasNext()){ object o =it.next(); ...}

    SET(无序、不能重复)

    Set里存放的对象是无序,不能重复的,集合中的对象不按特定的方式排序,只是简单地把对象加入集合中。

    HashSet

      HashSet是基于HashMap来实现的,操作很简单,更像是对HashMap做了一次“封装”,而且只使用了HashMap的key来实现各种特性,而HashMap的value始终都是PRESENT。

      HashSet不允许重复(HashMap的key不允许重复,如果出现重复就覆盖),允许null值,非线程安全。

    构造方法

    HashSet() 
      构造一个新的空 set,其底层 HashMap 实例的默认初始容量是 16,加载因子是 0.75。
    HashSet(Collection<? extends E> c) 
      构造一个包含指定 collection 中的元素的新 set。
    HashSet(int initialCapacity) 
      构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和默认的加载因子(0.75)。
    HashSet(int initialCapacity, float loadFactor)
      构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和指定的加载因子。

    方法

    boolean add(E e) 
      如果此 set 中尚未包含指定元素,则添加指定元素。
    void clear()
      从此 set 中移除所有元素。
    ** Object clone() 
      返回此 HashSet 实例的浅表副本:并没有复制这些元素本身。
    boolean contains(Object o) 
      如果此 set 包含指定元素,则返回 true。
    boolean isEmpty()**
      如果此 set 不包含任何元素,则返回 true。
    ** Iterator iterator() 
      返回对此 set 中元素进行迭代的迭代器。
    boolean remove(Object o) 
      如果指定元素存在于此 set 中,则将其移除。
    int size()**
      返回此 set 中的元素的数量(set 的容量)。

    TreeSet

      基于 TreeMap 的 NavigableSet 实现。使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator进行排序,具体取决于使用的构造方法。

      构造方法和方法比较类似就不说了

    遍历(和list相似)

    对 set 的遍历

    1.迭代遍历:

    Set<String> set = new HashSet<String>();  
    Iterator<String> it = set.iterator();  
    while (it.hasNext()) {  
      String str = it.next();  
      System.out.println(str);  
    }  

    2.for(foreach)循环遍历:

    for (String str : set) {  
          System.out.println(str);  
    }  

    MAP(键值对、键唯一、值不唯一)

      Map集合中存储的是键值对,键不能重复,值可以重复。根据键得到值,对map集合遍历时先得到键的set集合,对set集合进行遍历,得到相应的值。

    HashMap

      数组方式存储key/value,线程非安全允许null作为key和value,key不可以重复,value允许重复,不保证元素迭代顺序是按照插入时的顺序,key的hash值是先计算key的hashcode值,然后再进行计算,每次容量扩容会重新计算所以key的hash值,会消耗资源,要求key必须重写equals和hashcode方法

      默认初始容量16,加载因子0.75,扩容为旧容量乘2,查找元素快,如果key一样则比较value,如果value不一样,则按照链表结构存储value,就是一个key后面有多个value;

    方法

    1、添加:

      V put(K key, V value) (可以相同的key值,但是添加的value值会覆盖前面的,返回值是前一个,如果没有就返回null)

      putAll(Map<? extends K,? extends V> m) 从指定映射中将所有映射关系复制到此映射中(可选操作)。

    2、删除

      remove() 删除关联对象,指定key对象

      clear() 清空集合对象

    3、获取

      value get(key) 可以用于判断键是否存在的情况。当指定的键不存在的时候,返回的是null。

    4、判断:

      boolean isEmpty() 长度为0返回true否则false

      boolean containsKey(Object key) 判断集合中是否包含指定的key

      boolean containsValue(Object value) 判断集合中是否包含指定的value

    4、长度:

     Int size()

      map的主要的方法就这几个

    Hashtable

      Hashtable与HashMap类似,是HashMap的线程安全版,它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtale在写入时会比较慢,它继承自Dictionary类,不同的是它不允许记录的键或者值为null,同时效率较低。

    LinkedHashMap

    LinkedHashMap保存了记录的插入顺序,在用Iteraor遍历LinkedHashMap时,先得到的记录肯定是先插入的,在遍历的时候会比HashMap慢,有HashMap的全部特性。

    TreeMap

      基于红黑二叉树的NavigableMap的实现,线程非安全,不允许null,key不可以重复,value允许重复,存入TreeMap的元素应当实现Comparable接口或者实现Comparator接口,会按照排序后的顺序迭代元素,两个相比较的key不得抛出classCastException。主要用于存入元素的时候对元素进行自动排序,迭代输出的时候就按排序顺序输出

    遍历

    第一种:KeySet()
      将Map中所有的键存入到set集合中。因为set具备迭代器。所有可以迭代方式取出所有的键,再根据get方法。获取每一个键对应的值。 keySet():迭代后只能通过get()取key 。
      取到的结果会乱序,是因为取得数据行主键的时候,使用了HashMap.keySet()方法,而这个方法返回的Set结果,里面的数据是乱序排放的。

        Map map = new HashMap();
        map.put("key1","lisi1");
        map.put("key2","lisi2");
        map.put("key3","lisi3");
        map.put("key4","lisi4");  
        //先获取map集合的所有键的set集合,keyset()
        Iterator it = map.keySet().iterator();
        //获取迭代器
        while(it.hasNext()){
            Object key = it.next();
            System.out.println(map.get(key));
        }

    第二种: values() 获取所有的值.
    Collection values()不能获取到key对象

            Collection<String> vs = map.values();
            Iterator<String> it = vs.iterator();
            while (it.hasNext()) {
                String value = it.next();
                System.out.println(" value=" + value);
            }

    第三种:entrySet()
    Set<Map.Entry<K,V>> entrySet() //返回此映射中包含的映射关系的 Set 视图。(一个关系就是一个键-值对),就是把(key-value)作为一个整体一对一对地存放到Set集合当中的。Map.Entry表示映射关系。entrySet():迭代后可以e.getKey(),e.getValue()两种方法来取key和value。返回的是Entry接口。
    典型用法如下:

    // 返回的Map.Entry对象的Set集合 Map.Entry包含了key和value对象
            Set<Map.Entry<Integer, String>> es = map.entrySet();
    
            Iterator<Map.Entry<Integer, String>> it = es.iterator();
    
            while (it.hasNext()) {
                
                // 返回的是封装了key和value对象的Map.Entry对象
                Map.Entry<Integer, String> en = it.next();
    
                // 获取Map.Entry对象中封装的key和value对象
                Integer key = en.getKey();
                String value = en.getValue();
    
                System.out.println("key=" + key + " value=" + value);
            }

      推荐使用第三种方式,即entrySet()方法,效率较高。
      对于keySet其实是遍历了2次,一次是转为iterator,一次就是从HashMap中取出key所对于的value。而entryset只是遍历了第一次,它把key和value都放到了entry中,所以快了。两种遍历的遍历时间相差还是很明显的。

    总结:

    Vector和ArrayList

      1,vector是线程同步的,所以它也是线程安全的,而arraylist是线程异步的,是不安全的。如果不考虑到线程的安全因素,一般用arraylist效率比较高。

      2,如果集合中的元素的数目大于目前集合数组的长度时,vector增长率为目前数组长度的100%,而arraylist增长率为目前数组长度的50%。如果在集合中使用数据量比较大的数据,用vector有一定的优势。

      3,如果查找一个指定位置的数据,vector和arraylist使用的时间是相同的,如果频繁的访问数据,这个时候使用vector和arraylist都可以。而如果移动一个指定位置会导致后面的元素都发生移动,这个时候就应该考虑到使用linklist,因为它移动一个指定位置的数据时其它元素不移动。

      ArrayList 和Vector是采用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,都允许直接序号索引元素,但是插入数据要涉及到数组元素移动等内存操作,所以索引数据快,插入数据慢,Vector由于使用了synchronized方法(线程安全)所以性能上比ArrayList要差,LinkedList使用双向链表实现存储,按序号索引数据需要进行向前或向后遍历,但是插入数据时只需要记录本项的前后项即可,所以插入数度较快。

    arraylist和linkedlist

      1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。

      2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。

      3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。 这一点要看实际情况的。若只对单条数据插入或删除,ArrayList的速度反而优于LinkedList。但若是批量随机的插入删除数据,LinkedList的速度大大优于ArrayList. 因为ArrayList每插入一条数据,要移动插入点及之后的所有数据。

    HashMap与TreeMap

      1、 HashMap通过hashcode对其内容进行快速查找,而TreeMap中所有的元素都保持着某种固定的顺序,如果你需要得到一个有序的结果你就应该使用TreeMap(HashMap中元素的排列顺序是不固定的)。

      2、在Map 中插入、删除和定位元素,HashMap是最好的选择。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。使用HashMap要求添加的键类明确定义了hashCode()和 equals()的实现。

      两个map中的元素一样,但顺序不一样,导致hashCode()不一样。

      同样做测试:
        在HashMap中,同样的值的map,顺序不同,equals时,false;
        而在treeMap中,同样的值的map,顺序不同,equals时,true,说明,treeMap在equals()时是整理了顺序了的。

    HashTable与HashMap

      1、同步性:Hashtable是线程安全的,也就是说是同步的,而HashMap是线程序不安全的,不是同步的。

      2、HashMap允许存在一个为null的key,多个为null的value 。

      3、hashtable的key和value都不允许为null。

      4、初始容量,HashMap为16,hashTable为11,扩容逻辑,HashMap为 oldSize*2,HashTable为oldSize*2+1

      5、HashMap作者多了 Doug Lea,继承抽象Map,HashTable继承字典类

    文章作者:

    杨立果

    文章地址https://www.cnblogs.com/yangliguo/p/7476788.html 有改动

     
     
     
     
     
     
     
     
     

    Java集合中List,Set以及Map等集合体系详解

    概述:

    • List , Set, Map都是接口,前两个继承至Collection接口,Map为独立接口
    • Set下有HashSet,LinkedHashSet,TreeSet
    • List下有ArrayList,Vector,LinkedList
    • Map下有Hashtable,LinkedHashMap,HashMap,TreeMap
    • Collection接口下还有个Queue接口,有PriorityQueue类

    这里写图片描述

    注意:

    • Queue接口与List、Set同一级别,都是继承了Collection接口。
      看图你会发现,LinkedList既可以实现Queue接口,也可以实现List接口.只不过呢, LinkedList实现了Queue接口。Queue接口窄化了对LinkedList的方法的访问权限(即在方法中的参数类型如果是Queue时,就完全只能访问Queue接口所定义的方法 了,而不能直接访问 LinkedList的非Queue的方法),以使得只有恰当的方法才可以使用。

    • SortedSet是个接口,它里面的(只有TreeSet这一个实现可用)中的元素一定是有序的。

    总结:

    Connection接口:

    — List 有序,可重复

    • ArrayList
      优点: 底层数据结构是数组,查询快,增删慢。
      缺点: 线程不安全,效率高
    • Vector
      优点: 底层数据结构是数组,查询快,增删慢。
      缺点: 线程安全,效率低
    • LinkedList
      优点: 底层数据结构是链表,查询慢,增删快。
      缺点: 线程不安全,效率高

    —Set 无序,唯一

    • HashSet
      底层数据结构是哈希表。(无序,唯一)
      如何来保证元素唯一性?
      1.依赖两个方法:hashCode()和equals()

    • LinkedHashSet
      底层数据结构是链表和哈希表。(FIFO插入有序,唯一)
      1.由链表保证元素有序
      2.由哈希表保证元素唯一

    • TreeSet
      底层数据结构是红黑树。(唯一,有序)
      1. 如何保证元素排序的呢?
      自然排序
      比较器排序
      2.如何保证元素唯一性的呢?
      根据比较的返回值是否是0来决定

    针对Collection集合我们到底使用谁呢?(掌握)

    唯一吗?

    是:Set

    排序吗?

    是:TreeSet或LinkedHashSet
    否:HashSet
    如果你知道是Set,但是不知道是哪个Set,就用HashSet。

    否:List

    要安全吗?

    是:Vector
    否:ArrayList或者LinkedList

    查询多:ArrayList
    增删多:LinkedList
    如果你知道是List,但是不知道是哪个List,就用ArrayList。

    如果你知道是Collection集合,但是不知道使用谁,就用ArrayList。
    如果你知道用集合,就用ArrayList。

    说完了Collection,来简单说一下Map.

    Map接口:

    上图:
    这里写图片描述

    Map接口有三个比较重要的实现类,分别是HashMap、TreeMap和HashTable。

    • TreeMap是有序的,HashMap和HashTable是无序的。
    • Hashtable的方法是同步的,HashMap的方法不是同步的。这是两者最主要的区别。

    这就意味着:

    • Hashtable是线程安全的,HashMap不是线程安全的。
    • HashMap效率较高,Hashtable效率较低。
      如果对同步性或与遗留代码的兼容性没有任何要求,建议使用HashMap。 查看Hashtable的源代码就可以发现,除构造函数外,Hashtable的所有 public 方法声明中都有 synchronized关键字,而HashMap的源码中则没有。
    • Hashtable不允许null值,HashMap允许null值(key和value都允许)
    • 父类不同:Hashtable的父类是Dictionary,HashMap的父类是AbstractMap

    重点问题重点分析:

    (一).TreeSet, LinkedHashSet and HashSet 的区别

    1. 介绍

    • TreeSet, LinkedHashSet and HashSet 在java中都是实现Set的数据结构
    • TreeSet的主要功能用于排序
    • LinkedHashSet的主要功能用于保证FIFO即有序的集合(先进先出)
    • HashSet只是通用的存储数据的集合

    2. 相同点

    • Duplicates elements: 因为三者都实现Set interface,所以三者都不包含duplicate elements
    • Thread safety: 三者都不是线程安全的,如果要使用线程安全可以Collections.synchronizedSet()

    3. 不同点

    • Performance and Speed: HashSet插入数据最快,其次LinkHashSet,最慢的是TreeSet因为内部实现排序
    • Ordering: HashSet不保证有序,LinkHashSet保证FIFO即按插入顺序排序,TreeSet安装内部实现排序,也可以自定义排序规则
    • null:HashSet和LinkHashSet允许存在null数据,但是TreeSet中插入null数据时会报NullPointerException

    4. 代码比较

      public static void main(String args[]) {
            HashSet<String> hashSet = new HashSet<>();
            LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
            TreeSet<String> treeSet = new TreeSet<>();
    
            for (String data : Arrays.asList("B", "E", "D", "C", "A")) {
                hashSet.add(data);
                linkedHashSet.add(data);
                treeSet.add(data);
            }
    
            //不保证有序
            System.out.println("Ordering in HashSet :" + hashSet);
    
            //FIFO保证安装插入顺序排序
            System.out.println("Order of element in LinkedHashSet :" + linkedHashSet);
    
            //内部实现排序
            System.out.println("Order of objects in TreeSet :" + treeSet);
    
    
        }

    运行结果:
    Ordering in HashSet :[A, B, C, D, E] (无顺序)
    Order of element in LinkedHashSet :[B, E, D, C, A] (FIFO插入有序)
    Order of objects in TreeSet :[A, B, C, D, E] (排序)

    (二).TreeSet的两种排序方式比较

    1.排序的引入(以基本数据类型的排序为例)

    由于TreeSet可以实现对元素按照某种规则进行排序,例如下面的例子

    public class MyClass {
    
        public static void main(String[] args) {
            // 创建集合对象
            // 自然顺序进行排序
            TreeSet<Integer> ts = new TreeSet<Integer>();
    
            // 创建元素并添加
            // 20,18,23,22,17,24,19,18,24
            ts.add(20);
            ts.add(18);
            ts.add(23);
            ts.add(22);
            ts.add(17);
            ts.add(24);
            ts.add(19);
            ts.add(18);
            ts.add(24);
    
            // 遍历
            for (Integer i : ts) {
                System.out.println(i);
            }
        }
    }

    运行结果:
    17
    18
    19
    20
    22
    23
    24

    2.如果是引用数据类型呢,比如自定义对象,又该如何排序呢?

    测试类:

    public class MyClass {
        public static void main(String[] args) {
            TreeSet<Student> ts=new TreeSet<Student>();
            //创建元素对象
            Student s1=new Student("zhangsan",20);
            Student s2=new Student("lis",22);
            Student s3=new Student("wangwu",24);
            Student s4=new Student("chenliu",26);
            Student s5=new Student("zhangsan",22);
            Student s6=new Student("qianqi",24);
    
            //将元素对象添加到集合对象中
            ts.add(s1);
            ts.add(s2);
            ts.add(s3);
            ts.add(s4);
            ts.add(s5);
            ts.add(s6);
    
            //遍历
            for(Student s:ts){
                System.out.println(s.getName()+"-----------"+s.getAge());
            }
        }
    }

    Student.java:

    public class Student {
        private String name;
        private int age;
    
        public Student() {
            super();
            // TODO Auto-generated constructor stub
        }
    
        public Student(String name, int age) {
            super();
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }

    结果报错:

    这里写图片描述
    原因分析:
    由于不知道该安照那一中排序方式排序,所以会报错。
    解决方法:
    1.自然排序
    2.比较器排序

    (1).自然排序

    自然排序要进行一下操作:
    1.Student类中实现 Comparable接口
    2.重写Comparable接口中的Compareto方法

    compareTo(T o)  比较此对象与指定对象的顺序。
    public class Student implements Comparable<Student>{
        private String name;
        private int age;
    
        public Student() {
            super();
            // TODO Auto-generated constructor stub
        }
    
        public Student(String name, int age) {
            super();
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        @Override
        public int compareTo(Student s) {
            //return -1; //-1表示放在红黑树的左边,即逆序输出
            //return 1;  //1表示放在红黑树的右边,即顺序输出
            //return o;  //表示元素相同,仅存放第一个元素
            //主要条件 姓名的长度,如果姓名长度小的就放在左子树,否则放在右子树
            int num=this.name.length()-s.name.length();
            //姓名的长度相同,不代表内容相同,如果按字典顺序此 String 对象位于参数字符串之前,则比较结果为一个负整数。
            //如果按字典顺序此 String 对象位于参数字符串之后,则比较结果为一个正整数。
            //如果这两个字符串相等,则结果为 0
            int num1=num==0?this.name.compareTo(s.name):num;
            //姓名的长度和内容相同,不代表年龄相同,所以还要判断年龄
            int num2=num1==0?this.age-s.age:num1;
            return num2;
        }
    }运行结果:

    lis-----------22
    qianqi-----------24
    wangwu-----------24
    chenliu-----------26
    zhangsan-----------20
    zhangsan-----------22

    (2).比较器排序

    比较器排序步骤:
    1.单独创建一个比较类,这里以MyComparator为例,并且要让其继承Comparator接口
    2.重写Comparator接口中的Compare方法

    compare(T o1,T o2)      比较用来排序的两个参数。
    

    3.在主类中使用下面的 构造方法

    TreeSet(Comparator<? superE> comparator)
              构造一个新的空 TreeSet,它根据指定比较器进行排序。
    

    测试类:

    public class MyClass {
    
        public static void main(String[] args) {
            //创建集合对象
            //TreeSet(Comparator<? super E> comparator) 构造一个新的空 TreeSet,它根据指定比较器进行排序。
            TreeSet<Student> ts=new TreeSet<Student>(new MyComparator());
    
            //创建元素对象
            Student s1=new Student("zhangsan",20);
            Student s2=new Student("lis",22);
            Student s3=new Student("wangwu",24);
            Student s4=new Student("chenliu",26);
            Student s5=new Student("zhangsan",22);
            Student s6=new Student("qianqi",24);
    
            //将元素对象添加到集合对象中
            ts.add(s1);
            ts.add(s2);
            ts.add(s3);
            ts.add(s4);
            ts.add(s5);
            ts.add(s6);
    
            //遍历
            for(Student s:ts){
                System.out.println(s.getName()+"-----------"+s.getAge());
            }
        }
    }

    Student.java:

    public class Student {
        private String name;
        private int age;
    
        public Student() {
            super();
            // TODO Auto-generated constructor stub
        }
    
        public Student(String name, int age) {
            super();
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
    }

    MyComparator类:

    public class MyComparator implements Comparator<Student> {
    
        @Override
        public int compare(Student s1,Student s2) {
            // 姓名长度
            int num = s1.getName().length() - s2.getName().length();
            // 姓名内容
            int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
            // 年龄
            int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
            return num3;
        }
    
    }

    运行结果:

    lis-----------22
    qianqi-----------24
    wangwu-----------24
    chenliu-----------26
    zhangsan-----------20
    zhangsan-----------22

    (三). 性能测试

    对象类:

    class Dog implements Comparable<Dog> {
        int size;
        public Dog(int s) {
            size = s;
        }
        public String toString() {
            return size + "";
        }
        @Override
        public int compareTo(Dog o) {
           //数值大小比较
            return size - o.size;
        }
    }

    主类:

    public class MyClass {
    
        public static void main(String[] args) {
    
            Random r = new Random();
            HashSet<Dog> hashSet = new HashSet<Dog>();
            TreeSet<Dog> treeSet = new TreeSet<Dog>();
            LinkedHashSet<Dog> linkedSet = new LinkedHashSet<Dog>();
    
            // start time
            long startTime = System.nanoTime();
            for (int i = 0; i < 1000; i++) {
                int x = r.nextInt(1000 - 10) + 10;
                hashSet.add(new Dog(x));
            }
    
            // end time
            long endTime = System.nanoTime();
            long duration = endTime - startTime;
            System.out.println("HashSet: " + duration);
    
            // start time
            startTime = System.nanoTime();
            for (int i = 0; i < 1000; i++) {
                int x = r.nextInt(1000 - 10) + 10;
                treeSet.add(new Dog(x));
            }
            // end time
            endTime = System.nanoTime();
            duration = endTime - startTime;
            System.out.println("TreeSet: " + duration);
    
            // start time
            startTime = System.nanoTime();
            for (int i = 0; i < 1000; i++) {
                int x = r.nextInt(1000 - 10) + 10;
                linkedSet.add(new Dog(x));
            }
    
            // end time
            endTime = System.nanoTime();
            duration = endTime - startTime;
            System.out.println("LinkedHashSet: " + duration);
        }
    
    }
    
    

    运行结果:

    HashSet: 1544313
    TreeSet: 2066049
    LinkedHashSet: 629826
    虽然测试不够准确,但能反映得出,TreeSet要慢得多,因为它是有序的。

    嘿嘿

    好了,至此完结.小伙伴有问题的话,请留言

    参考文章:
    HashSet、TreeSet和LinkedHashSet的使用区别
    Collection集合总结
    HashMap、TreeMap和HashTable的区别

     
     
     
     
     
     
     
     
     
     
     
     
     
     

    List、Set、Map的区别

                                                                                                   (图一)

    1.面试题:你说说collection里面有什么子类。

    (其实面试的时候听到这个问题的时候,你要知道,面试官是想考察List,Set)

    正如图一,list和set是实现了collection接口的。

    (图二)

    List:1.可以允许重复的对象。

        2.可以插入多个null元素。

            3.是一个有序容器,保持了每个元素的插入顺序,输出的顺序就是插入的顺序。

            4.常用的实现类有 ArrayList、LinkedList 和 Vector。ArrayList 最为流行,它提供了使用索引的随意访问,而 LinkedList 则对于经常需要从 List 中添加或删除元素的场合更为合适。

     (图三)

     Set:1.不允许重复对象

         2. 无序容器,你无法保证每个元素的存储顺序,TreeSet通过 Comparator  或者 Comparable 维护了一个排序顺序。

            3. 只允许一个 null 元素

            4.Set 接口最流行的几个实现类是 HashSet、LinkedHashSet 以及 TreeSet。最流行的是基于 HashMap 实现的 HashSet;TreeSet 还实现了 SortedSet 接口,因此 TreeSet 是一个根据其 compare() 和 compareTo() 的定义进行排序的有序容器。

    (图四)

    1.Map不是collection的子接口或者实现类。Map是一个接口。

    2.Map 的 每个 Entry 都持有两个对象,也就是一个键一个值,Map 可能会持有相同的值对象但键对象必须是唯一的。

    3. TreeMap 也通过 Comparator  或者 Comparable 维护了一个排序顺序。

    4. Map 里你可以拥有随意个 null 值但最多只能有一个 null 键。

    5.Map 接口最流行的几个实现类是 HashMap、LinkedHashMap、Hashtable 和 TreeMap。(HashMap、TreeMap最常用)

    2.面试题:什么场景下使用list,set,map呢?

    (或者会问为什么这里要用list、或者set、map,这里回答它们的优缺点就可以了)

    答:

    1. 如果你经常会使用索引来对容器中的元素进行访问,那么 List 是你的正确的选择。如果你已经知道索引了的话,那么 List 的实现类比如 ArrayList 可以提供更快速的访问,如果经常添加删除元素的,那么肯定要选择LinkedList。

    2. 如果你想容器中的元素能够按照它们插入的次序进行有序存储,那么还是 List,因为 List 是一个有序容器,它按照插入顺序进行存储。

    3. 如果你想保证插入元素的唯一性,也就是你不想有重复值的出现,那么可以选择一个 Set 的实现类,比如 HashSet、LinkedHashSet 或者 TreeSet。所有 Set 的实现类都遵循了统一约束比如唯一性,而且还提供了额外的特性比如 TreeSet 还是一个 SortedSet,所有存储于 TreeSet 中的元素可以使用 Java 里的 Comparator 或者 Comparable 进行排序。LinkedHashSet 也按照元素的插入顺序对它们进行存储。

    4. 如果你以键和值的形式进行数据存储那么 Map 是你正确的选择。你可以根据你的后续需要从 Hashtable、HashMap、TreeMap 中进行选择。

    大家可以跟着下面的步骤一起尝试一下。

    1.我们知道了列表要实现排序,需要重写comparable接口的compareTo的方法。

    但是是我不知道comparaTo里面要怎么写呢,它有传入参数吗?它有返回值吗?如果有事什么类型的呢?ok,下面一起来做一下。先把这个链接的帮助文档下载下来。下载完之后,打开帮助文档,

     

    2.看完了帮助文档是不是心里稍微有点底气了呢,那现在打开eclipse我们一起来写一写吧。

    首先我们要比较对象的哪个属性呢。年龄?身高?还是体重?刚刚看帮助文档已经知道了,所以下面大家一起来写一下。

     如果大家也是像上图这种写法,那么再想一想有没有更好的办法。(我这样吻是肯定有的,好好看看帮助文档,你就知道了,我知道你只要用心想想,肯定想出来的!)

    好了,写完年龄,不去继续花几分钟把按照身高来排序也写一下吧。

     
  • 相关阅读:
    Coursera课程笔记----计算导论与C语言基础----Week 7
    Coursera课程笔记----计算导论与C语言基础----Week 6
    Coursera课程笔记----计算导论与C语言基础----Week 5
    Coursera课程笔记----P4E.Capstone----Week 6&7
    Coursera课程笔记----P4E.Capstone----Week 4&5
    Coursera课程笔记----P4E.Capstone----Week 2&3
    图解 Java 垃圾回收机制,写得非常好!
    别在 Java 代码里乱打日志了,这才是正确的打日志姿势!
    聊一聊Java 泛型中的通配符 T,E,K,V,?
    Java开发最常犯的10个错误,打死都不要犯!
  • 原文地址:https://www.cnblogs.com/xuwc/p/13905206.html
Copyright © 2020-2023  润新知