Java基础(3)

1、集合框架

所有的集合都在 java.util 包下,java的集合几乎是从Collection 和 map这两个接口中派生出来的,而这两个接口又有一些子类(包括子接口和实现类)

1.1.集合和数组的区别:

数组和链表:数组&链表必会核心知识 - 知乎
在这里插入图片描述

1.2. 常用集合的分类:

标题

 

1.3. list和set的区别:

在这里插入图片描述

 

(1)ArrayList: 底层数据结构是数组,查询快,增删慢,线程不安全,效率高,可以存储重复元素
(2)LinkedList:底层数据结构是链表,查询慢,增删快,线程不安全,效率高,可以存储重复元素
(3)Vector:底层数据结构是数组,查询快,增删慢,线程安全,效率低,可以存储重复元素

Set(Set 接口存储一组唯一,无序的对象。)
HashSet
HashSet 是一个没有重复元素的集合。它是由HashMap实现的,不保证元素的顺序(这里所说的没有顺序是指:元素插入的顺序与输出的顺序不一致),而且HashSet允许使用null。但是只允许有一个null元素!

LinkedHashSet
LinkedHashSet继承自HashSet,其底层是基于LinkedHashMap来实现的,有序,非同步。(LinkedHashSet集合同样是根据元素的hashCode值来决定元素的存储位置,但是它同时使用链表维护元素的次序。这样使得元素看起来像是以插入顺序保存的,也就是说,当遍历该集合时候,LinkedHashSet将会以元素的添加顺序访问集合的元素。)

TreeSet
TreeSet是一个有序集合,其底层是基于TreeMap实现的,非线程安全。TreeSet可以确保集合元素处于排序状态。

Set和List的区别
(1) Set 接口实例存储的是无序的,不重复的数据。List 接口实例存储的是有序的,可以重复的元素。

(2)Set检索效率低下,删除和插入效率高,插入和删除不会引起元素位置改变 <实现类有HashSet,TreeSet>。

(3) List和数组类似,可以动态增长,根据实际存储的数据的长度自动增长List的长度。查找元素效率高,插入删除效率低,因为会引起其他元素位置改变 <实现类有ArrayList,LinkedList,Vector> 。

1.4. Map

Map用于保存具有映射关系的数据,Map里保存着两组数据:key和value,它们都可以使任何引用类型的数据,但key不能重复。所以通过指定的key就可以取出对应的value。

 

 

HashMap源码解读(重点)

HashMap基础
HashMap继承了AbstractMap类,实现了Map,Cloneable,Serializable接口
数据结构(jdk1.7:数组+链表,1.8后增加了红黑树:数组+链表+红黑树):

 

HashMap的容量,默认是16
HashMap的加载因子,默认是0.75
当HashMap中元素数超过容量 x 加载因子时,HashMap会进行扩容。

HashMap类中的元素是Node类,翻译过来就是节点,是定义在HashMap中的一个内部类,实现了Map.Entry接口。

Node类的基本属性有:

hash:key的哈希值 
key:节点的key,类型和定义HashMap时的key相同 
value:节点的value,类型和定义HashMap时的value相同 
next:该节点的下一节点

next记录的是下一个节点本身,也是一个Node节点,这个Node节点也有next属性,记录了下一个节点,于是,只要不断的调用Node.next.next.next……,就可以得到每个节点

HashMap存值过程:

通过键的Hash值确定数组的位置。
找到以后,如果该位置无节点,直接存放。
该位置有节点,则遍历该节点以及后续的节点,比较key值,相等则覆盖,没有就新增节点,默认使用链表,相连节点数超过8的时候,在jdk 1.8中会变成红黑树。
如果Hashmap中的数组使用情况超过一定比例,就会扩容,默认扩容两倍。

常问问题:

  1. hashMap如何解决hash碰撞?有其它方式吗?
    HashMap使用链表来解决碰撞问题,当碰撞发生了,对象将会存储在链表的下一个节点中。即所谓的拉链法
    其它方式:如二次哈希

  2. hashMap怎么在链表上添加数据?在链表的前面还是后面?
    jdk1.7头插法
    jdk1.8的链表从后面插入

  3. Hashmap的默认容量?为什么是16不是15?输入17会是什么容量?
    16,因为只能是2的指数幂容量
    输入17容量是32。(如果HashMap初始化的时候指定了容量,HashMap会把这个容量修改为2的倍数,然后创建对应长度的数组)

  4. HashMap的容量为什么必须要2的指数幂容量?
    计算下标的算法很简单,hash值 和 (length-1)按位与,使用length-1的意义在于,length是2的倍数,所以length-1在二进制来说每位都是1,这样可以保证最大的程度的散列hash值,否则,当有一位是0时,不管hash值对应位是1还是0,按位与后的结果都是0,会造成散列结果的重复。
    总结来说就是:减少hash碰撞。
    备注:与运算,同为1才为1

  5. HashMap的数组是什么时候创建的?
    第一次put的时候
    原因:懒加载的思想,节省内存
    如果new一个hashMap 但并没有使用(或并没有立即使用) 会浪费内存空间

  6. hashMap扩容的原理
    当HashMap中元素数超过容量 x 加载因子时,HashMap会进行扩容。
    当HashMap决定扩容时,会调用HashMap类中的resize(int newCapacity)方法,参数是新的table长度。在JDK1.7和JDK1.8的扩容机制有很大不同。
     
  7. 传统jdk1.7hashMap的缺点?
    a).耗时,链表的轮训非常耗时 ---->时间复杂度 0(n)
    b).并发场景,hashMap是线程不安全的

  8. Jdk1.8为什么引入红黑树?
    因为Map中桶的元素初始化是链表保存的,其查找性能是O(n),而树结构能将查找性能提升到O(log(n))。当链表长度很小的时候,即使遍历,速度也非常快,但是当链表长度不断变长,对查询性能有一定的影响,所以才需要转成树。

  9. hashTable和hashMap的区别
    hashTable是为了解决并发(线程安全)的
    解决方式:给整个HashTable加锁 synchronized
    HashTable:底层数组+链表实现,无论key还是value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable(它把所有方法都加上synchronized关键字来实现线程安全),效率低,
    由此延伸出ConcurrentHashMap 它做了相关优化
     
  10. ConcurrentHashmap和HashTable的区别
    ConcurrentHashMap不需要锁整个Map,相反它划分了N段(segments),要操作哪一段才上锁哪段数据。它给链表或者红黑树所在的节点加锁,不光线程安全,速度相比hashTable的效率也有了N倍提升,默认提升16倍(读操作不加锁,由于ConcurrentHashMap中HashEntry的value变量是 volatile的,也能保证读取到最新的值,对应下面一个问题)
  11. ConcurrentHashMap get方法没有加锁实现,为什么不会导致脏数据?
    给变量加Volatile 保证数据可见性

  12. 1.8的hashMap数据结构,什么时候会转成红黑树
    数组+链表+红黑树(链表长度超过8,并且数组长度不小于64)
    当链表的值小于6则会从红黑树转回链表

  13. 扩容后存储位置的计算方式
    jdk1.7通过再次indexFor()找到数组位置
    jdk1.8通过高低位的桶直接在链表尾部添加

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值