集合框架知识总结

目录

集合框架

Collection接口

   List

ArrayList

LinkedList

Vector

   Set

HashSet

LinkedHashSet

TreeSet

Map接口

HashMap

   HashMap底层数据结构详解

   在HashMap中的元素存储过程 put()(HashMap如何处理哈希冲突)

   HashMap的扩容机制

   HashMap在JDK1.7和JDK1.8中的不同

   *为什么HashMap中,包装类适合作为key?

LinkedHashMap

TreeMap

HashTable

Iterator迭代器

Iterator迭代器的优缺点

Iterator和ListIterator的区别

Collections工具类

Sort()的两种重载方式(Comparable、Comparator)

      Comparable

      Comparator

快速失败机制fail-fast

各个集合的区别

ArrayList和LinkedList区别

ArrayList和Vector的区别

HashMap和HashTable的区别

HashSet和HashMap的区别


集合框架

   集合框架定义:

     集合框架时表示和操作集合而规定的一种统一、标准的体系结构。

   集合框架的好处/用途:

  1. 容量自增长
  2. 提供了高性能的数据结构和算法,提高了程序的速度和质量
  3. 允许不同API之间的相互操作,API之间可以传递集合
  4. 可以方便地扩展和改写集合,提高代码的复用性和可操作性
  5. 使用jdk自带的集合类,可以降低代码的维护和学习新API的成本

Collection接口

      Collection是最基本的集合接口,Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的子接口,如List和Set。

   List

  • ArrayList

      底层由数组实现,查找快,增删慢。

  • LinkedList

      底层由链表实现,查找慢,增删快。

      实现了List接口中没有定义的方法,专门操作表头和表尾,可以当做堆栈、队列和双向队列使用。

  • Vector

      线程安全,底层由数组实现。

   Set

  • HashSet

      底层由哈希表实现,无序。

      HashSet基于HashMap实现,HashSet的值存放在Hashmap的key上,HashMap的value统一为PRESENT。

      有关HashSet的操作,基本上都是直接调用底层HashMap的相关方法来完成。

      HashSet存储数据、扩容机制等步骤参考HashMap部分。》》》HashMap

  • LinkedHashSet

      底层由链表和哈希表实现,链表保证了元素顺序和存储的顺序一致,哈希表保证了元素的唯一性。

  • TreeSet

      底层由二叉树实现,有序。

      TreeSet通过重写hashCode()和equals()方法来保证其唯一性;通过二叉树结构保证了元素的有序性。可以参考TreeMap。

      保证有序性的方法参考》》》Sort()的两种重载方式(Comparable、Comparator)

Map接口

  • HashMap

      底层由数组和链表/红黑树实现,无序。

      具有很快的访问速度。

   HashMap底层数据结构详解

         HashMap内部实现是一个 桶数组 ,每个桶中存放着一个单链表的头结点。其中,每个节点存储的是一个键值对整体(Entry),HashMap采用拉链法解决哈希冲突。

         在JDK1.8,由数组和链表组成,数组为Hashmap的主体,链表的目的主要是为了用拉链法解决哈希冲突。当链表长度大于8,且数组长度大于64时,链表将转化为红黑树,以减少搜索时间。

   在HashMap中的元素存储过程 put()(HashMap如何处理哈希冲突)

         1.使用扰动函数

            使用key.hashCode()计算hash值并将值赋给变量a;

            将变量a向后移动16位,为变量b;

            将变量a和变量b做异或运算(二进制相同为0,不同为1),此时得到经过扰动函数处理后的hash值。

         *为什么要将得到的变量a向后移动16位并做异或运算?

            如果只使用原来的hash值取余,那么相当于参加运算的只有hash值的低位;所以我们右移16位,让hashcode取值出的高位也参与运算,进一步降低哈希碰撞的概率,使得函数数据分配更加平均。

         2.根据hash值计算数组下标

            index=(table.length-1)&hash

         *HashMap为什么不使用hashcode()处理后的hash值直接作为数组下标?

            hashCode()方法返回的是int整数类型,由于整数的范围太大,而HashMap的数组容量范围远远不及hashCode()返回的值的范围。

            HashMap在通常情况下,是取不到最大值的;并且设备上也难以提供这么大的存储空间,从而导致这个hash值不在数组大小范围内,进而无法匹配存储位置。

         *HashMap的长度为什么是2的幂次方?

            计算数组下标的方式是用一个取余(%)的方式来进行计算的。取余操作中如果除数是2的幂次,则等价于与其除数-1的与(&)操作

            如果HashMap的长度是2的幂次方,则采用的是二进制的位操作&,相比较于用%取余来说,能够大大提高效率。

         3.比较此时取得的index值是否相等

            如果不相等,则表示存储的两个对象一定不相等,直接存储进相应数组中;

            如果相等,则将值放入相应的数组中,等待下一步比较。

         4.用equals()判断两个key是否相同

            如果key相同,则将其原始的value值覆盖。

            如果key不同,则将当前的key-value键值对放入相应的链表中。

   HashMap的扩容机制

         1.涉及的参数

            Capacity:HashMap的当前长度,HashMap的长度是2的幂。

            LoadFactor:HashMap的负载因子,默认值为0.75.

         2.扩容时机

            当Map中包含的Entry(键值对整体)的数量大于等于 theshold=loadFactor(负载因子)*Capacity(桶数组长度)

            且新建的Entry刚好落在一个非空的桶上,此刻触发扩容机制,将其容量扩大为2倍

            当size大于等于thshold的时候,并不一定会触发扩容机制。只要有一个新建的Entry出现哈希冲突,则立刻 resize()。 

         3.resize()步骤

            扩容:创建一个新的Entry空数组(新的桶数组),长度是原数组的2倍。

            rehash

               1 遍历原Entry数组,把所有的Entry重新Hash到新数组。

               2 判断e.hash(oldCap-1)是否等于0。

                  等于0,表示e.hash&(oldCap-1)和e.hash&(newCap-1)的结果相同,则rehash后的新数组下标等于原来的数组下标。

                  等于1,下标等于原来下标加上旧的桶数组长度。

   HashMap在JDK1.7和JDK1.8中的不同

         1.resize()扩容优化

         2.引入了红黑树,避免了由于单条链表过长而影响查询效率的问题。

         3.解决了多线程死循环问题,但仍是非线程安全的,多线程时可能造成数据丢失问题。

   *为什么HashMap中,包装类适合作为key?

         1.包装类的特性能够保证hash值的不可更改性和计算准确性,有效减少哈希冲突的概率。

         2.包装类都是final类型,即不可变性,保证key的不可更改性,不会存在获取hash值不同的情况。

         3.包装类内部已经重写了equals()、hashCode()等方法,遵守了HashMap内部的规范,不容易出现hash值计算冲突的概率。

  • LinkedHashMap

      底层由HashMap实现,有序。

      LinkedHashMap需要维护元素的插入顺序,所以性能略低于HashMap。但在迭代访问元素时有很好的性能,因为他使用了链表来维护内部顺序。

  • TreeMap

      底层由红黑树实现,有序。

      实现了SortMap接口,能够把它保存的记录根据键排序,默认按照升序排序。

      保证有序性的方法参考》》》Sort()的两种重载方式(Comparable、Comparator)

  • HashTable

      线程安全,和HashMap相似。

      是遗留类,通过在put()、get()、size()等各种方法上加Synchronized锁来保证线程安全。这导致所有并发操作都要竞争通一把锁,其他线程只能等待,大大降低了并发操作的效率。

Iterator迭代器

   Iterator迭代器主要用于遍历Collection集合中的元素。

   所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了iterator接口的对象。

   Iterator只能用于遍历集合,Iterator本身不提供承装对象的能力。如果需要创造Iterator对象,必须有一个被迭代的集合。

  • Iterator迭代器的优缺点

         优点:屏蔽内部遍历细节,访问集合只需要通过Iterator给定的方法即可。

         缺点:增加新的集合类需要增加新的迭代器类,迭代器类和集合类成对增加。

  • Iterator和ListIterator的区别

          1.遍历的集合种类

            Iterator可以遍历List和Set。

            ListIterator只能遍历List。

         2.遍历方向

            Iterator只能单向遍历。

            ListIterator可以双向遍历(向前向后)。

         3.功能

            ListIterator实现了Iterator接口,添加了一些额外的功能。如添加一个元素、替换一个元素、获取前面或后面元素的索引位置。

Collections工具类

   Collections是一个针对集合类的工具类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全等操作。

  • Sort()的两种重载方式(Comparable、Comparator)

         sort()的两种重载方式分别是实现Comparable、Comparator接口。

      Comparable

            Java提供的Comparable接口,出自java.lang包。其中只包含一个方法 compareTo(Object obj)。它会返回一个负整数、0、正整数;分别表示传入的对象小于、等于、大于已有的对象。

            Comparable通过比较实体对象来调用的。但一个实体只能实现一个接口。所以扩展性不是很好,和类绑定了。

      Comparator

            Java提供的Comparator接口,出自java.util包,它有两个方法,compare(Object obj1,Object obj2)和 equals()。

            compare方法比较两个参数,得出他们的顺序关系。它会返回一个负整数、0、正整数,分别表示第一个参数小于、等于、大于已有的对象。

            equals方法有一个参数,用来确定参数对象是否等于这个comparator。这个方法仅在要比较的对象也是一个comparator,同时它的时序关系与这个comparator相同时,才会返回true。

            Comparator是比较器,可以直接执行。采用了策略模式,一个实体对象可以根据需要设计多个比较器。隔离型好,方便。

快速失败机制fail-fast

   快速失败机制是Java集合中的一种错误检测机制。

   当多个线程对集合进行结构上改变的操作时,就可能产生fail-fast机制。

各个集合的区别

  • ArrayList和LinkedList区别

         1.数据结构实现

            ArrayList的底层数据结构是数组。

            LinkedList的底层数据结构是链表。

         2.随机访问效率(读、查找)

            ArrayList效率较高,LinkedList效率较低。

            LinkedList是线性的数据存储方式,需要移动指针从前向后依次查找。

         3.增加和删除效率

            LinkedList效率较高,ArrayList效率较低。

            ArrayList 中,在非首尾的增加和删除操作会影响数组内其他元素的下标。

         4.内存空间占用

            LinkedList比ArrayList更占内存。

            LinkedList的节点中,不仅要存储数据,还存储了两个引用,分别指向前一个元素和后一个元素。

  • ArrayList和Vector的区别

         1.线程安全

            Vector线程安全;ArrayList非线程安全。

            Vector使用了synchronized锁来实现线程安全。

         2.性能

            ArrayList性能较好。

            Vector性能较差。

         3.扩容机制

            ArrayList每次扩容增加为原数组的2倍。

            Vector每次扩容增加为原数组的1.5倍。

  • HashMap和HashTable的区别

         1.线程安全

            HashMap线程不安全;HashTable线程安全。

            HashTable中的一些方法加入了synchronized关键字来保证HashTable中的对象是性能安全的。

         2.是否允许null

            HashMap可以将null作为一个条目的key或value。

            HashTable中不能放入空值,最多只有一个key作为null,但是可以有无数个value值作为null。

         3.性能

            HashMap的性能最好;HashTable的性能最差。

            如果追求性能安全,建议使用ConCurrentHashMap。

         4.初始容量

            这里分为两种情况。

            #创建时不规定初始容量值

             HashTable默认初始大小为11;

             HashMap默认初始大小为16。

            #创建时给定初始容量值

             HashTable直接使用给定容量大小;

             HashMap将其扩充为其2的幂次方大小。(因为HashMap的大小是2的幂次方)

         5.扩容机制

            HashMap每次扩容增加为原来的2倍。

            HashTable每次扩容增加为原来的2n+1倍。

  • HashSet和HashMap的区别

         1.实现接口不同

            HashSet实现了Set接口;

            HashMap实现了Map接口。

         2.存储内容类型

            HashSet存储单列数据对象;

            HashMap存储键值对。

         3.元素添加方式

            HashSet调用add()方法向Set中添加元素;

            HashMap调用put()方法向Map中添加元素。

         4.性能

            HashSet较慢,HashMap较快。

            因为HashMap使用唯一的键来获取对象。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值