集合

作者: 小灰灰_5c75 | 来源:发表于2018-08-14 19:36 被阅读0次

    Java集合

    1、集合的由来

    程序的需求也许并不能确定创建对象的数量,甚至不知道对象的类型,为了满足普适的编程需要,我们需要在任何时候,任何地点创建任意数量的对象,那么容纳对象的容器是什么呢?首先想到的是数组,但是数组只能存储统一类型、固定长度的数据,所以引入了集合。

    可以参考以下:

    ArrayList:http://www.cnblogs.com/ysocean/p/6812674.html

    2、集合是什么

             Java集合类在java.util包中,是用来存放对象的容器。

    注意:1、集合只能存放对象。例如村一个int类型1放入集合中,其实是自动转成Integer类对象存入的,Java每一种类型都有对应的引用类型。

    2、集合是存放多个对象的引用,对象还是放在堆内存中。

    3、集合可以存放不同类型、数量的数据类型。

    4、集合特点

    特点:除了map集合,其他集合都实现了Iterator接口,这是遍历集合元素的接口,主要是hashNext(),next(),remove()三种方法。为了实现反向迭代和迭代时增加元素,他的一个子接口Listiterator有再此基础上添加了三种方法,add(),previous(),hasPrevious()。也就是说实现了Interator接口,遍历集合中元素时,只能从前往后遍历,被遍历的元素不会再次被遍历,通常无序集合都是实现这个接口例如HashSet;而元素有序的集合,一般实现都是ListIterator接口,实现这个接口的集合可以使用listIterator()迭代方法双向遍历,可以通过next()访问下一个元素,也可以通过previous()访问上一个元素例如ArrayList。

            补充: 还有一个特点是抽象类的使用。实现一个集合类,去实现抽象的接口会非常麻烦,工作量很大。这个时候可以使用抽象类,这些抽象类中给我们提供了许多现成的实现,我们只需要根据自己的需求重写一些方法或者添加一些方法就可以实现自己需要的集合类,工作量大大降低。

    集合详解

    Iterator

    迭代器,它是集合的顶层接口(不包括map系列的集合,Map接口是map系列集合的顶层接口)

    1、Objiect next():返回迭代器刚越过的元素的引用,返回值是Object,需要强转成自己需要的类型。

           2、boolean hasNext():判断容器内是否还有可访问的元素。

           3、void remove():删除迭代器刚越过(it.next()之后)的元素。

    没有越过任何元素就要remove 异常 迭代删除

    4、除了map系列的集合我们都能够通过迭代器对集合中的元素进行遍历。

    注意:我们可以在源码中追溯到集合的顶层接口,比如 Collection 接口,可以看到它继承的是类Iterable

    源码

    Conllection:List接口和Set接口的父接口

    Collection基础方法

    List:有序、可重复的集合

    List接口继承于Conllection接口,所有基本的方法如上图。

    List接口三个典型实现

    1、ArrayList()、Vector()、LinkedList()

    Vector()基本不用了,多查询用ArrayList,多增删用LinkedList,吃不准就用ArrayList

    List常用集合比较

    2、除此之外,List 接口遍历还可以使用普通 for 循环进行遍历,指定位置添加元素,替换元素等等。

    常用操作

            注意:在迭代时,iterator()方法只能取,不能增加元素,如果要增加,就使用:ListIterator listIterator = list.listIterator();上面集合特点中的ListIterator接口中的方法,将iterator()方法直接换为listIterator(),使用对应接口接收。

        顺便一提下for循环的另一种写法:

    迭代器使用后在内存中除去

    泛型相关请移步:点我。。

    Set接口

    1、典型实现HashSet()是一个无序、不重复的集合。

    Set s = newHashSet();

    HashSet不能保证元素的顺序;不可重复;不是线程安全;集合元素可为null;

    底层数据结构是哈希表,存在的意义是加快查询速度。在一般的数组中索引位置是随机的,元素的取值和元素的位置之间不存在确定的关系,因此,在数组中查找特定的值时,需要把查找值和一系列的元素进行比较,此时查询效率在于查找过程中比较的速度。而HashSet集合底层数组索引和查询值之间有一个确定的关系:index=hash(value),所以只需要调用这个公式就能快速找到索引和元素。

    对于HashSet,如果两个对象通过equals()方法返回true则这两个对象的hashCode值也应该相等。

    1、当向HashSet集合中存入一个元素时,HashSet会先调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据hashCode值决定该对象在HashSet中的存储位置

    1.1、如果 hashCode 值不同,直接把该元素存储到hashCode() 指定的位置

    1.2、如果 hashCode 值相同,那么会继续判断该元素和集合对象的 equals() 作比较

    1.2.1、hashCode 相同,equals 为 true,则视为同一个对象,不保存在 hashSet()中

    1.2.2、hashCode 相同,equals 为 false,则存储在之前对象同槽位的链表上,这非常麻烦,我们应该约束这种情况,即保证:如果两个对象通过 equals() 方法返回 true,这两个对象的 hashCode 值也应该相同。

    注意:每一个存储到哈希表中的对象,都得提供hashCode() 和 equals() 方法的实现,用来判断是否是同一个对象。

    对于 HashSet 集合,我们要保证如果两个对象通过 equals() 方法返回 true,这两个对象的 hashCode 值也应该相同。

    2、Set linkedHashSet =new LinkedHashSet();

    不可重复、有序。底层采用链表和hash表操作,hash表保证元素唯一性。

    Set treeSet = new TreeSet();

    TreeSet有序:不可重复,底层使用红树,擅长范围查询。

    自然排序、自定义排序。

    3、Set接口总结

    共同点:1、不允许元素重复

                   2、线程不安全,解决办法:Set set = Collections.synchronizedSet(set 对象)

    不同点:

    HashSet:不保证元素的添加顺序,底层时hash表算法。判断元素相等,equals()方法返回true,hashCode()值相等,即要求hashSet中元素覆盖equals()和hashCode()方法。

    LinkedHashSet:HashSet的子类,底层采用hash表算法以及链表算法,即保证元素的查询效率也保证了元素的添加顺序,但是整体性能低于HashSet。

    TreeSet不能保证元素的添加顺序,但是会对集合中的元素进行排序。底层是红黑树算法,(树结构适合范围查询)

    Map

    1、Key – value 键值对,key不允许重复,value允许重复

    (1)严格来说Map并不是一个集合,而是两个集合之间的映射关系。

    (2)两个集合每一条映射关系都是一条数据,即Entry(key,value)。Map 可以看成是由多个 Entry 组成。

    (3)因为 Map 集合即没有实现于 Collection 接口,也没有实现 Iterable 接口,所以不能对 Map 集合进行 for-each 遍历

    2、HashMap源码分析

    提出问题:

    (1)HashMap底层如何存储数据;

    (2)HashMap几个主要方法;

    (3)HashMap如何真正确定元素存储位置以及如何处理哈希冲突

    (4)HashMap扩容机制是?

    (5)JDK 1.8在扩容和解决哈希冲突上对HashMap源码做了哪些修改?有什么好处?

    1、  HashMap存储数据是根据键值对存储数据的,并在存储多个数据时,数据键是唯一的,过有相同的键,之前的键值会被覆盖。保证HashMap正确存储数据,就要确保作为键的类已经重写了equals()方法。

    2、HashMap存储数据的位置与添加数据的键的hashCode()返回值有关。所以元素在用HashMap存储时确保已经重写了hashCode()方法。

    3、 HashMap最多允许一条键为空,键值可以多个为空。

    4、HashMap存储时是乱序的,并且因为扩容会导致元素存储位置改变,因此,遍历顺序也是不确定的。

    5、 HashMap线程不安全,可以使用Collections.synchronizedMap(Map map)方法使HashMap具有线程安全能力,或者使用ConcurrentHashMap。

    解决问题

    HashMap底层存储结构

    JDK1.7之前,

    hashCode()、equals()

    1、首先,他们都是属于Object基类的方法:

    public boolean equals(Object obj) {

      return (this == obj);

    }

    public native int hashCode();

    2、equals方法默认比较两个对象的引用是否是同一个内存地址。而hashCode这是一个native本地方法,其实默认hashCode方法返回的就是对应对象的内存地址。

    通过toString方法,toString方法返回的是[类名@十六进制内存地址],由源码可看出内存地址与hashCode返回值相同。

    public String toString() {

       return getClass().getName()+ "@" + Integer.toHexString(hashCode());

    }

    面试题目:hashCode方法返回的是对象的内存地址么?

    Object基类的hashCode方法默认返回对象的内存地址,但是一些场景下需要重写hashCode方法,比如使用Map来存储对象,重写之后的hashCode就不是对象的内存四肢。

    equals详解

    equals()方法既然是Object基类的方法,我们创建的所有对象就都有这个方法,并有权利去重写这个方法,该方法返回一个boolean值,代表两个对象是否相等,例如:

    String str1 = "abc";

    String str2 = "abc";

    str1.equals(str2);//true

    显然String类一定重写了equals方法,否则两个String对象的内存地址一定不同。看下String类和equals方法:

    public boolean equals(Object anObject) {

       //首先判断两个对象的内存地址是否相同

       if (this == anObject) {

           return true;

       }

       //判断连个对象是否属于同一类型。

       if (anObject instanceof String) {

           String anotherString =(String)anObject;

           int n = value.length;

           //长度相同的情况下逐一比较 char 数组中的每个元素是否相同

           if (n ==anotherString.value.length) {

               char v1[] = value;

               char v2[] =anotherString.value;

               int i = 0;

               while (n-- != 0) {

                   if (v1[i] !=v2[i])

                       returnfalse;

                   i++;

               }

               return true;

           }

       }

       return false;

    }

    可以看出equals不仅仅依靠this==obj来判断对象是否相同。事实上所有Java定义好的一些现有的数据类型都重写了该方法。当我们自己定义引用数据类型的时候应该依靠什么原则去判断两个对象是否相同,这就需要我们根据业务需求来把握。但是我们要遵循如下规则:

    自反性(reflexive)对于任意不为null的引用值x,x.equals(x)一定是true。

    对称性(symmetric) 对于任意不为null的引用之x和y,当且仅当x.equlas(y)=true时,y.equlas(x)=true。

    传递性(transitive)对于任意不为null的引用值x,y,z如果 x.equals(y) 是 true,同时 y.equals(z) 是 true,那么x.equals(z)一定是 true。

    一致性(consistent)对于任意不为null的引用值x和y,如果用于equals比较的对象信息没有被修改的话,多次调用时 x.equals(y) 要么一致地返回 true 要么一致地返回 false。

    对于任意不为 null 的引用值 x,x.equals(null) 返回 false。

    equals() 与==

    两者的主要区别是需要看比较的对象是什么样的类型。

    Java数据类型分为基本数据类型和引用数据类型,基本数据类型包括byte,short,int ,long,float,double,Boolean,char八种。对于基本数据类型==操作符判断的是左右两边变量的值。

    int a = 10;

    int b = 10;

    float c = 10.0f;

    //以下输出结果均为true

    System.out.println("(a == b) = " + (a == b));

    System.out.println("(b == c) = " + (b == c));

    而对于引用数据类型==操作符判断的就是等号两边的指向的对象的内存地址是否相同。通过==判断的两个引用数据类型变量,如果相同就是同一个对象。

    EntryClass entryClass1 = new EntryClass(1);

    EntryClass entryClass2 = new EntryClass(1);

    EntryClass entryClass3 = entryClass1;

     // (entryClass1 ==entryClass2) = false  

    System.out.println(" (entryClass1 == entryClass2) = " +(entryClass1 == entryClass2));

    // (entryClass1 == entryClass3) = true

    System.out.println(" (entryClass1 == entryClass3) = " +(entryClass1 == entryClass3));

    equals 与 == 操作符的区别总结如下:

    1、若 == 两侧都是基本数据类型,则判断的是左右两边操作数据的值是否相等

    2、若 == 两侧都是引用数据类型,则判断的是左右两边操作数的内存地址是否相同。若此时返回 true , 则该操作符作用的一定是同一个对象。

    3、Object 基类的 equals 默认比较两个对象的内存地址,在构建的对象没有重写 equals 方法的时候,与 == 操作符比较的结果相同。

    4、equals 用于比较引用数据类型是否相等。在满足equals 判断规则的前体系,两个对象只要规定的属性相同我们就认为两个对象是相同的。

    hashCode详解

    hashCode方法没有equals方法使用那么频繁,说到hashCode就不得不结合Java的Map容器,类似于HashMap这种使用了哈希算法容器会根据对象的hashCode返回值来初步确定对象在容器中的位置,然后内部再根据一定的hash算法来实现元素的存取。

    Hash法简介

    Hash算法,又被称为散列算法,基本上,哈希算法就是将对象本身的键值,通过特定的数学函数或使用其他办法,转化成相应的数据存储地址。

    Hash算法的用处:

    如果我们存放了元素{2,3,4,5,6}的数组中找到的数值等于6的值的索引,我们会怎么做,我们需要遍历数组才能拿到对应的索引。数组越大,效率越低。

             如果我们在存放之前就定好规则就可以很快拿到我们想要的结果。之前我们在数组中存放元素可能是按顺序添加的,但是如果我们按照一种既定的数学函数运算得到对应的索引值放如对应的值,和数组的下标形成映射关系的话,那么就可以在想取某个值时使用映射关系就可以找到对应的值的数组下标了。

    在常见的 hash 函数中有一种最简单的方法交「除留余数法」,操作方法就是将要存入数据除以某个常数后,使用余数作为索引值。

            但是上述简单的 hash 算法,缺点也是很明显的,比如 77 和 88 对 11 取余数得到的值都是 0,但是角标为0 位置已经存放了 77 这个数据,那88就不知道该去哪里了。上述现象在哈希法中有个名词叫碰撞:

    碰撞:若两个不同的数据经过相同哈希函数运算后,得到相同的结果,那么这种现象就做碰撞。

    于是在设计 hash 函数的时候我们就要尽可能做到:

    1、降低碰撞的可能性

    2、尽量将要存入的元素经过 hash 函数运算后的结果,尽量能够均匀的分布在指定的容器(我们在称之为桶)。

    hashCode与hash算法的关系

    hashCode 方法其实就是一种 hash 算法,只是有的类覆写好提供给我们了,有些就需要我们手动去覆写。比如我们可以看一下 String 提供给我们的 hashCode 算法:

    public int hashCode() {

       int h = hash;//默认是0

       if (h == 0 &&value.length > 0) {

           char val[] = value;

            //字符串转化的 char 数组中每一个元素都参与运算

           for (int i = 0; i

               h = 31 * h +val[i];

           }

           hash = h;

       }

       return h;

    }

            前文说了 hashCode 方法与 java 中使用散列表的集合类息息相关,我们拿 Set 来举例,我们都知道 Set 中是不允许存放重复的元素的。那么我们凭借什么来判断已有的 Set 集合中是否有何要存入的元素重复的元素呢?有人可能会说我们可以通过 equals 来判断两个元素是否相同。那么问题又来,如果 Set 中已经有 10000个元素了,那么之后在存入一个元素岂不是要调用 10000 次 equals 方法。显然这不合理,性能低到令人发指。那要怎么办才能保证即高效又不重复呢?答案就在于 hashCode 这个函数。

            经过之前的分析我们知道 hash 算法是使用特定的运算来得到数据的存储位置的,那么 hashCode 方法就充当了这个特定的函数运算。这里我们可以简单认为调用 hashCode 方法后得到数值就是元素的存储位置(其实集合内部还做了进一步的运算,以保证尽可能的均匀分布在桶内)。

            当 Set 需要存放一个元素的时候,首先会调用 hashCode 方法去查看对应的地址上有没有存放元素,如果没有则表示 Set 中肯定没有相同的元素,直接存放在对应位置就好,但是如果 hashCode 的结果相同,即发生了碰撞,那么我们在进一步调用该位置元素的 equals 方法与要存放的元素进行比较,如果相同就不存了,如果不相同就需要进一步散列其它的地址。这样我们就可以尽可能高效的保证了无重复元素的方法。

             面试题:hashCode方法作用和意义何在:在Java中hashCode的主要作用就是提高容器查找和存储的快捷性,如HashSet,HashTable,HashMap等,hashCode是用来在散列存储结构中确定对象的存储地址。

    hashCode和equals方法的关系

    1.  调用 equals 返回 true 的两个对象必须具有相等的哈希码。

    2.  [endif]如果两个对象的hashCode 返回值相同,调用它们 equals 方法不一返回 true 。

    我们先来看下第一个结论:调用 equals 返回 true 的两个对象必须具有相等的哈希码。为什么这么要求呢?比如我们还拿 Set 集合举例,Set 首先会调用对象的 hashCode 方法寻找对象的存储位置,如果两个相同的对象调用 hashCode 方法得到的结果不同,那么造成的后果就是 Set 中存储了相同的元素,而这样的结果肯定是不对的。所以就要求调用 equals 返回 true 的两个对象必须具有相等的哈希码

    那么第二条为什么hashCode 返回值相同,两个对象却不一定相同呢?这是因为,目前没有完美的 hash 算法能够完全的避免 「哈希碰撞」,既然碰撞是无法完全避免的所以两个不相同的对象总有可能得到相同的哈希值。所以我们只能尽可能的保证不同的对象的hashCode 不相同。事实上,对于HashMap 在存储键值对的时候,就会发生这样的情况,在 JDK 1.7 之前,HashMap 对键的哈希值碰撞的处理方式,就是使用所谓的‘拉链法’。 具体实现会在之后分析HashMap 的时候说到。

    总结

    本文总结了 equals 方法和 hashCode 方法的作用和意义。并学习了在覆写这两个方法的时候需要注意的要求。需要注意的是,关于这两个方法在面试的时候还是很有可能被问及的所以,我们至少要明白:

    hashCode 返回值不一定对象的存储地址,比如发生哈希碰撞的时候。

    调用equals 返回 true 的两个对象必须具有相等的哈希码。

    如果两个对象的hashCode 返回值相同,调用它们equals 方法不一返回 true 。

    很常用实现类:(待实践)

    Map和Set关系

    1、都有几个类型的集合。HashMap和HashSet,采用hash表算法; TreeMap和TreeSet都使用红黑树算法,适合范围查询;LikedHashMap、LikedHashSet都采用hash表和红黑树算法。

    2、  大神分析过的Set底层源码。可以看出,Set集合就是Map集合的key组成。

    大神的源码标注

    以下内容仅供参考:

    Java集合类:

    Set、List、Map、Queue使用场景梳理

    1) Collection

    一组"对立"的元素,通常这些元素都服从某种规则

       1.1) List必须保持元素特定的顺序

       1.2) Set不能有重复元素

       1.3) Queue保持一个队列(先进先出)的顺序

    2) Map

    一组成对的"键值对"对象

    Java集合架构层次关系:

    1. Interface Iterable

    迭代器接口,这是Collection类的父接口。实现这个Iterable接口的对象允许使用foreach进行遍历,也就是说,所有的Collection集合对象都具有"foreach可遍历性"。这个Iterable接口只

    有一个方法: iterator()。它返回一个代表当前集合对象的泛型迭代器,用于之后的遍历操作

    1.1 Collection

    Collection是最基本的集合接口,一个Collection代表一组Object的集合,这些Object被称作Collection的元素。Collection是一个接口,用以提供规范定义,不能被实例化使用

       1) Set

       Set集合类似于一个罐子,"丢进"Set集合里的多个对象之间没有明显的顺序。Set继承自Collection接口,不能包含有重复元素(记住,这是整个Set类层次的共有属性)。

       Set判断两个对象相同不是使用"=="运算符,而是根据equals方法。也就是说,我们在加入一个新元素的时候,如果这个新元素对象和Set中已有对象进行注意equals比较都返回false,  则Set就会接受这个新元素对象,否则拒绝。

        因为Set的这个制约,在使用Set集合的时候,应该注意两点:1)为Set集合里的元素的实现类实现一个有效的equals(Object)方法、2) 对Set的构造函数,传入的Collection参数不能包含重复的元素

           1.1) HashSet

           HashSet是Set接口的典型实现,HashSet使用HASH算法来存储集合中的元素,因此具有良好的存取和查找性能。当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据该HashCode值决定该对象在HashSet中的存储位置。

         值得主要的是,HashSet集合判断两个元素相等的标准是两个对象通过equals()方法比较相等,并且两个对象的hashCode()方法的返回值相等

               1.1.1) LinkedHashSet

               LinkedHashSet集合也是根据元素的hashCode值来决定元素的存储位置,但和HashSet不同的是,它同时使用链表维护元素的次序,这样使得元素看起来是以插入的顺序保存的。

      当遍历LinkedHashSet集合里的元素时,LinkedHashSet将会按元素的添加顺序来访问集合里的元素。

       LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet的性能,但在迭代访问Set里的全部元素时(遍历)将有很好的性能(链表很适合进行遍历)

        1.2) SortedSet   

         此接口主要用于排序操作,即实现此接口的子类都属于排序的子类

        1.2.1) TreeSet

             TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态

        1.3) EnumSet

           EnumSet是一个专门为枚举类设计的集合类,EnumSet中所有元素都必须是指定枚举类型的枚举值,该枚举类型在创建EnumSet时显式、或隐式地指定。EnumSet的集合元素也是有序的,

        它们以枚举值在Enum类内的定义顺序来决定集合元素的顺序

       2) List

       List集合代表一个元素有序、可重复的集合,集合中每个元素都有其对应的顺序索引。List集合允许加入重复元素,因为它可以通过索引来访问指定位置的集合元素。List集合默认按元素的添加顺序设置元素的索引

           2.1) ArrayList

           ArrayList是基于数组实现的List类,它封装了一个动态的增长的、允许再分配的Object[]数组。

           2.2) Vector

           Vector和ArrayList在用法上几乎完全相同,但由于Vector是一个古老的集合,所以Vector提供了一些方法名很长的方法,但随着JDK1.2以后,java提供了系统的集合框架,就将Vector改为实现List接口,统一归入集合框架体系中

               2.2.1) Stack

               Stack是Vector提供的一个子类,用于模拟"栈"这种数据结构(LIFO后进先出)

           2.3) LinkedList

           implements List, Deque。实现List接口,能对它进行队列操作,即可以根据索引来随机访问集合中的元素。同时它还实现Deque接口,即能将LinkedList当作双端队列使用。自然也可以被当作"栈来使用"

       3) Queue

       Queue用于模拟"队列"这种数据结构(先进先出 FIFO)。队列的头部保存着队列中存放时间最长的元素,队列的尾部保存着队列中存放时间最短的元素。新元素插入(offer)到队列的尾部,访问元素(poll)操作会返回队列头部的元素,队列不允许随机访问队列中的元素。结合生活中常见的排队就会很好理解这个概念

           3.1) PriorityQueue

           PriorityQueue并不是一个比较标准的队列实现,PriorityQueue保存队列元素的顺序并不是按照加入队列的顺序,而是按照队列元素的大小进行重新排序,这点从它的类名也可以看出来

           3.2) Deque

           Deque接口代表一个"双端队列",双端队列可以同时从两端来添加、删除元素,因此Deque的实现类既可以当成队列使用、也可以当成栈使用

               3.2.1) ArrayDeque

               是一个基于数组的双端队列,和ArrayList类似,它们的底层都采用一个动态的、可重分配的Object[]数组来存储集合元素,当集合元素超出该数组的容量时,系统会在底层重新分配一个Object[]数组来存储集合元素

               3.2.2) LinkedList

    1.2 Map

    Map用于保存具有"映射关系"的数据,因此Map集合里保存着两组值,一组值用于保存Map里的key,另外一组值用于保存Map里的value。key和value都可以是任何引用类型的数据。Map的key不允许重复,即同一个Map对象的任何两个key通过equals方法比较结果总是返回false。

    关于Map,我们要从代码复用的角度去理解,java是先实现了Map,然后通过包装了一个所有value都为null的Map就实现了Set集合

    Map的这些实现类和子接口中key集的存储形式和Set集合完全相同(即key不能重复)

    Map的这些实现类和子接口中value集的存储形式和List非常类似(即value可以重复、根据索引来查找)

       1) HashMap

        和HashSet集合不能保证元素的顺序一样,HashMap也不能保证key-value对的顺序。并且类似于HashSet判断两个key是否相等的标准也是: 两个key通过equals()方法比较返回true、同时两个key的hashCode值也必须相等

           1.1) LinkedHashMap

           LinkedHashMap也使用双向链表来维护key-value对的次序,该链表负责维护Map的迭代顺序,与key-value对的插入顺序一致(注意和TreeMap对所有的key-value进行排序进行区分)

       2) Hashtable是一个古老的Map实现类

           2.1) Properties

           Properties对象在处理属性文件时特别方便(windows平台上的.ini文件),Properties类可以把Map对象和属性文件关联起来,从而可以把Map对象中的key-value对写入到属性文件中,也可以把属性文件中的"属性名-属性值"加载到Map对象中

       3) SortedMap

        正如Set接口派生出SortedSet子接口,SortedSet接口有一个TreeSet实现类一样,Map接口也派生出一个SortedMap子接口,SortedMap接口也有一个TreeMap实现类

           3.1) TreeMap

           TreeMap就是一个红黑树数据结构,每个key-value对即作为红黑树的一个节点。TreeMap存储key-value对(节点)时,需要根据key对节点进行排序。TreeMap可以保证所有的key-value对处于有序状态。同样,TreeMap也有两种排序方式: 自然排序、定制排序

       4) WeakHashMap

       WeakHashMap与HashMap的用法基本相似。区别在于,HashMap的key保留了对实际对象的"强引用",这意味着只要该HashMap对象不被销毁,该HashMap所引用的对象就不会被垃圾回收。但WeakHashMap的key只保留了对实际对象的弱引用,这意味着如果WeakHashMap对象的key所引用的对象没有被其他强引用变量所引用,则这些key所引用的对象可能被垃圾回收,当垃圾回收了该key所对应的实际对象之后,WeakHashMap也可能自动删除这些key所对应的key-value对

       5) IdentityHashMap

       IdentityHashMap的实现机制与HashMap基本相似,在IdentityHashMap中,当且仅当两个key严格相等(key1 == key2)时,IdentityHashMap才认为两个key相等

       6) EnumMap

       EnumMap是一个与枚举类一起使用的Map实现,EnumMap中的所有key都必须是单个枚举类的枚举值。创建EnumMap时必须显式或隐式指定它对应的枚举类。EnumMap根据key的自然顺序

    (即枚举值在枚举类中的定义顺序)

    继续学习。。。

    参考各位简书大神和博客

    相关文章

      网友评论

          本文标题:集合

          本文链接:https://www.haomeiwen.com/subject/hqfbbftx.html