JAVA容器

Collection接口?

List、Set、Queue、Stack都继承自Collection接口。

Set接口,该接口有两个实现类:HashSet和TreeSet(有序)。

List接口,该接口的实现类:LinkedList、ArrayList、Vector

 

List和Set的区别?

  1. List是有序的,即存储和取出的顺序一致;元素可重复;可通过索引值操作元素
  2. Set是无序的,元素是唯一的

 

ArrayList、Vector、LinkedList有什么区别?

ArrayList、Vector、LinkedList类均在java.util包里,均为可伸缩数组,即可以动态改变长度的数组。

ArrayList、Vector都是数组实现的,支持用下标来访问元素,索引数据的速度比较快,但是对数据的插入操作执行得比较慢。都有一个初始化的容量的大小,元素超过这个大小时就需要动态扩充它们的存储空间。

ArrayList、Vector最大的区别就是同步的使用,Vector的绝大多数方法都是同步的,所以Vector是线程安全的,ArrayList是线程不安全的。就效率而言,ArrayList比较高。

LinkedList采用双向列表来实现的,从列表头开始遍历,访问效率比较低,但是插入效率较高。是非线程安全的容器。

选择:对数据主要是索引操作,选用ArrayList.插入删除操作较多,选择LinkedList.多线程中使用容器时,使用Vector较为安全。

 

总结:ArrayList、Vector底层是数组,查询快,增删慢。

ArrayList线程不安全,效率高;Vector线程安全,效率低

LinkedList底层是链表,查询慢,增删快。

LinkedList线程不安全,效率高

 

HashSet和TreeSet的区别?

HashSet的底层是哈希表,将元素保存到map的key中。如果希望保证元素的唯一性,需要重写对象的hashCode和equals方法

TreeSet的底层是二叉树,元素是按照一定顺序进行排序的。元素排序方式有两种:第一种是让对象所属的类去继承comparable接口,并且重写equals、compareTo、hashCode方法。第二种是自定义比较器,实现comparator接口。在两种排序方式都存在时,第二种方式优先

 

集合之Map

保存具有映射关系的一对数据,即Key-Value

Key不可重复,通过Set来组织的

Vlaue可重复,通过Collection存储的

Map接口,该接口的实现类:HashMap、TreeMap、LinkedHashMap、WeakHashMap、IdentityHashMap.

 

HashMap的实现方式?

HashMap在Java8前是由数组和链表组成的,数组长度默认是16,数组每个元素存储的是链表的头结点。通过哈希函数(类似hash(key.hashCode())%len取模的方式)来计算要添加的元素存放的数组的位置,将要存放的元素插入到该数组元素对应的链表中。

 

该方式存在的问题是,可能每一次取模后都是同一个值,那么某个链表长度就会非常的长,复杂度最好是O (1),最坏是O (n)

所以Java8以及之后,采用的是数组+链表+红黑树,这样性能从O (n)提高到O (logn)。当TREEIFY_THRESHOLD变量超过8时,即链表长度超过8,将该链表转换为红黑树,当UNTREEIFY_THRESHOLD变量小于6时,即链表长度小于6,将红黑树转换为链表。

 

HashMap是延迟创建的,即插入元素时,才进行构造

 

HashMap中put方法的逻辑

  1. 如果HashMap未被初始化过,则初始化
  2. 对Key求Hash值,计算对应的数组下标
  3. 如果该数组元素还没有值,即不存在链表头指针,则直接将该节点放入数组中
  4. 如果该数组元素中存在值,以链表的方式链接到链表后面
  5. 如果链表长度超过阙值,就将链表转成红黑树
  6. 如果链表长度低于6,将红黑树转回链表
  7. 如果节点key值已经存在,就替换原节点的value值
  8. 如果数组满了(容量*加载因子0.75),就需要resize(扩容成原来的2倍后重排)

 

除了将链表转换成红黑树外,HashMap还通过以下的方式减少碰撞,即让计算出的hash值尽可能不一样:

  1. 扰动函数:促使元素位置分布均匀,减少碰撞几率
  2. 使用final对象,并采用合适的equals和hashCode方法,比如使用String对象作为key.final是为了防止key的改变

 

   HashMap计算hash值的方式:key的hashcode值与该值移位16位的值做亦或

 

HashMap扩容存在的问题?

扩容方式是当数组的大小超过容量*加载因子0.75时,数组进行扩容两倍,扩容的方式是新建一个大小为原来两倍的数组,将数组元素复制过去。

问题1:多线程环境下,如果两个线程同时发现数组需要扩容,那么就会存在条件竞争,容易造成死锁

问题2:扩容是一个比较耗时的过程

 

HashMap本身不是线程安全的,想要将其变成线程安全的:

Map hashMap = new HashMap();

Map safeHashMap = Collections.synchronizedMap(hashMap);

 

Hashtable简介

Hashtable与HashMap都是基于Hash实现的,跟HashMap不同的是,Hashtable在每个公有方法前都加了synchronized,所以Hashtable是线程安全的。

 

ConccurentHashMap的实现原理

无论是Hashtable还是由synchronizedMap方法包装的hashMap,都是线程安全的。多线程并发时,需要竞争同一把锁,效率极低。为了提升在多线程下的执行性能,ConccurentHashMap应运而生。

早期的ConccurentHashMap,通过分段锁Segment来实现,默认配置16把锁。相比于Hashtable来说,效率高了16倍。其做法是:将hashmap的数组逻辑上拆分成几个子数组,每个子数组配置一把锁,线程在获取到某把分段锁的时候,才能操作这个子数组,其他线程想要操作这个子数组,则被阻塞。但若是其他线程想要操作别的未被占用的分段锁下的子数组,那么就可以进行操作。

 

Java8之后,ConcurrentHashMap采用CAS+synchronized使锁更细化。即为hashMap的每一个数组元素都加上一把锁,只要key的hash值不冲突,那么锁就不会产生冲突。同时像HashMap一样,将链表和红黑树进行相互转换,以提升效率。

 

 

ConcurrentHashMap插入节点(put)的逻辑

  1. 判断数组是否初始化,没有则进行初始化操作
  2. 通过hash定位数组的索引坐标,判断该数组坐标上是否有Node节点,如果没有则使用CAS进行添加(链表的头节点),添加失败则进入下一次循环,继续尝试添加
  3. 检查内部是否正在扩容,如果正在扩容就帮助它一块扩容
  4. 如果定位到的数组位置对应的元素不为空,那么就是用synchronized锁住该数组元素(即对应位置链表或者红黑树的头节点),并进行元素添加操作
  5. 判断链表长度是否达到临界值8,当长度超过这个值,将链表转换为红黑树

 

总结

  1. 首先使用无锁操作CAS插入头节点,如果别的线程正在插入节点,可能会失败,则重试
  2. 若头节点存在,则尝试获取头节点的同步锁,再进行操作

 

HashMap、Hashtable、ConccurentHashMap的区别?

HashMap是线程不安全的,底层是通过数组+链表+红黑树来实现的。

Hashtable是线程安全的,底层是通过数组+链表来实现的,实现线程安全的方式是在修改数组时,锁住整个对象,效率较低

ConccurentHashMap是线程安全的,通过将锁细粒度到数组的每一个元素来提升并发性能。底层是通过数组+链表+红黑树来实现的。

HashMap的key/value均可为null,而其他的两个类不支持

 

HashMap、Hashtable、TreeMap、WeakHashMap有哪些区别?

HashMap、Hashtable都是基于Hash实现的,存入和取出是随机的,Hashtable是线程安全的,HashMap是线程不安全的。就效率而言,HashMap高于Hashtable。HashMap允许key为null,Hashtable不允许。

TreeMap能够把它保存的记录按照键排序。

LinkedHashMap是HashMap的一个子类,key值是顺序存储的。

WeakHashMap与HashMap类似,不同在于WeakHashMap中key采用的是“弱引用”,只要WeakHashMap中的key不再被外部引用,它就可以被垃圾回收器回收。HashMap中key采用的是“弱引用”,只要WeakHashMap中的key不再被外部引用,它就可以被垃圾回收器回收。

 

HashMap的底层如何判断两个key是否相同?

主要用到了Object类的两个方法:hashCode()-返回对象存储的内存地址

equals()判断两个对象是否相等

在向HashMap中添加键值对<key,value>时,经过以下几个过程:

调用key的hasCode()方法生成一个hash值h1,如果h1不存在于hashMap中,那么直接将<key,value>添加到HashMap中。

如果h1存在,那么找出HashMap中所有hash值为h1的key,然后分别使用key的equals方法进行判断。如果相等,则替代该key的value值,如果都不存在,则新增一个键值对。

基于此,我们在使用自定义对象作为HashMap的key时,一定要重写对象的hashCode()方法和equals()方法,否则就会导致一个HashMap中存了两个key值相等的对象(因为对象的地址不同)。

 

什么是迭代器?

迭代器是一个对象,它的工作是遍历并选择序列中的对象

使用:

使用容器的iterator()方法返回一个Iterator

使用Iterator的hasNext()方法判断容器是否还有元素,如果有,可以使用next()方法获取下一个元素。

可以通过remove()方法删除迭代器返回的元素。

for(Iterator<String> iter=ll.iterator(); iter.hasNext();)

{

String str = (String)iter.next();

System.out.println(str);
}

使用iterator时经常会遇到ConcurrentModificationException异常,原因在于使用Iterator遍历容器的同时又对容器做增加或删除操作所导致的。当调用容器的iterator方法时,会将容器个数赋值给变量exceptedModCount,当调用next方法时,会比较列表实际元素个数和该变量的值,不相等则会抛出这个异常。

解决方式:在遍历过程中将要删除的元素保存到一个集合中,等遍历结束后再调用removeAll()方法来删除,或者使用iter.move()方法。

 

Collection和Collections的区别?

Collection是一个集合接口,提供了对集合对象进行基本操作的通用接口方法。比如,List和Set就是实现这个接口。

Collections类似于一个工具类,不能实例化,提供了一系列静态方法以实现对各种集合的搜索、排序、线程安全化等操作。比如,Collections.sort(list).

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值