Java容器

  1. 说说常见的集合有哪些?
    • 实现Collection接口的集合:
      • 实现List的集合:
        •  ArrayList:以数组的类型进行存储,线程不安全,查询效率高,添加删除效率低(因为会对数组中的元素进行移动)
        • LinkedList:以双向链表的类型进行存储,线程不安全,查询效率低(因为需要双向遍历),添加删除效率高(因为只需要记录前后两个元素)
        • vector:以数组的类型进行存储,线程安全,查询效率高,添加删除效率低----------->现在不推荐使用,因为扩容只扩容1倍,而且只能在尾部进行插入与删除,效率太低,所以应使用java.util.concurrent.CopyOnWriteArrayList,也是线程安全的
        • stack:vector的子类,特点是先进后出
      • 实现set的集合:
        • HashSet:无序且不可重复,集合元素可以为null,在进行遍历输出时可能不会按照添加顺序进行输出,重复是通过equals方法判断对象是否相等
        • LinkedHashSet:根据元素的HashCode值决定元素存储位置,并且使用双向链表进行维护元素的次序,在进行遍历输出时会按照添加的顺序进行输出。遍历效率比HashSet高,但插入效率比HashSet低
        • TreeSet:实现了SortedSet接口,遍历时会将添加的元素排序输出
    • 实现Map的接口的集合:
        • HashMap:以键值对(key,value)的方式进行存储,线程不安全,键值对key、value的值能为null,继承AbstractMap
        • HashTable:以键值对(key,value)的方式进行存储,线程安全(所有方法都使用了synchronize修饰),键值对不能为null,继承Dictionary
        • TreeMap:以红黑树的类型进行有序的存储,自己可以自定义排序器
        • ConcurrentHashMap:线程安全,比HashTable效率高
  2. HashMap的实现原理?
    • HashMap基于hash算法实现的,通过put(key,Value)存储,get(key)获取。当使用put进行存储时,HashMap会根据key的HashCode()计算出的hash值,根据hash值将value存储到bucket中。当计算出存在hash冲突时,HashMap的做法是使用链表和红黑树进行存储相同hash值的value
  3. HashSet的实现原理?
    • HashSet是基于HashMap实现的,HashSet底层使用HashMap来保存所有元素,可以这样说,HashSet中的元素,只是保存在HashMap的key上,而value使用final static的Object对象标识,所以HashSet的实现比较简单,相关的HashSet的操作,基本上是直接调用HashMap中的方法
  4. HashMap和HashTable之间的区别?
    • HashMap:键值对key-value都能为null,线程不安全,继承AbstractMap
    • HashTable:键值对key-value不能为null,线程安全,继承Dictionary
  5. HashTable和ConcurrentHashMap之间的区别?
    • HashTable实现线程安全是为所有的方法加上synchronize的修饰,当有一个线程进行修改时会锁定整个HashTable,导致其他线程都处于阻塞状态,在一个高并发的场景下,就会使效率会急剧下降
    • ConcurrentHashMap实现线程安全是减小锁的粒度(分段锁),ConcurrentHashMap的主干是Segment数组,Segment继承与ReentrantLock(可重入锁),一个Segment就是一个子哈希表,Segment中维护着一个HashEntry数组,在并发环境下,对于不同的Segment的数据操作不用考虑锁竞争,ConcurrentHashMap含有16个Segment(默认),每个Segment都锁定一部分数据
        • ConcurrentHashMap是由Segment数据结构和HashEntry数据结构组成。Segment是一种重入锁ReentranLock,扮演锁的角色。HashEntry存储着键值对数据。
        • 一个ConcurrentHashMap实例中包含由若干个Segment对象组成的数组,每个Segment对象守护整个散列映射表的若干个桶,每个桶是由若干个HashEntry数组对象连接起来的链表。
        • 每个Segment都守护着一个由HashEntry对象连接成的链表,如果要对链表中的HashEntry进行修改时,必须首先获得它对应的Segment锁
  6. HashMap的put()方法的具体流程?
    • HashMap在1.8之前的存储方式是【数组+链表】,1.8之后的存储方式是【数组+链表+红黑树】链表长度>8时,链表转为红黑树
    • 判断HashMap是否为null或者长度是否为0,如果为null或长度为0,则进行resize()扩容
      • resize()扩容是在【length的长度>=[size*扩容因子(0.75)]】时,扩容到原来长度2倍,当长度被扩容到(2^30)时,阈值会被修改为(2^31-1)此后就不会在进行扩容
    • 通过key的hash值与HashMap的长度做与运算获取key的索引,判断索引位置是否为null,如果为null,则直接将数据插入
    • 如果不为null,则进行判断new key是否与old key相同,并且key!=null,相同,则覆盖旧值
    • 如果不相同,则发生了哈希冲突,判断数据结构是树还是链表,是树直接在树中插入或更新
    • 如果是链表,则需要对链表进行遍历,判断链表中next位置是否为null
    • 如果为null则将数据插入链表,然后进行判断是否链表长度length>=7,满足则转为红黑树,将链表所有元素加入到红黑树中
    • 如果不为null,判断new key与old key是否相同,并且key!=null,如果满足,直接覆盖,不满足则继续遍历链表
    • 最后判断扩容,当实际存在的键值对数量>最大容量,就进行扩容
  7. HashMap是如何解决哈希冲突?
    • 哈希:也称"散列",任意输入通过哈希函数处理,输出特定长度的值,这就是哈希值
    • 哈希函数的特点:
      • 通过同一哈希函数得到相同的哈希值,输入值不一定相同
      • 通过同一哈希函数得到不同的哈希值,输入值一定不相同
    • 哈希冲突:通过统一哈希函数得到相同的哈希值,输入值不相同时,就产生了哈希冲突
    • HashMap的存储结构是【数组+链表】,解决哈希冲突的办法就是"链地址法"
  8. HashMap中indexFor()方法中h&(length-1)是什么意思?
    • 此方法主要是为了得到key值的索引,X%2^n=X&(2^n-1),因为HashMap的length默认是16(2^5),扩容操作也是扩容到原来的2倍,而且与操作的效率比较高
  9. Collection和Collections的区别?
    • Collection:是集合类的接口,继承此接口的集合有List和Set
    • Collections:是集合类的工具类,提供了一些静态方法实现对各种集合的查询、排序以及线程安全化等操作
  10. ArrayList、vector、LinkedList的存储性和特性?
    • ArrayList和vector都是以数组的方式进行存储,查询效率都很高,插入删除效率因为这两个操作都会涉及数组内元素的移动,所以效率低
    • LinkedList:以双向链表的方式进行存储,查询效率低,因为查询时需要双向遍历链表,但是插入和删除的效率高,因为只需要记录前后元素
  11. 如何实现数组和List之间的转换?
    • 数组-->List:Arrays.toList(Array);
    • List-->数组:List自带的toArray()方法
  12. 在Queue中poll()和remove()有什么区别?
    • 相同点:都是返回Queue中的第一个元素,并且在Queue中将此元素删除
    • 区别:当Queue中没有元素时,poll()会返回null,remove()会抛出异常NoSuchElementException异常
  13. Array和ArrayList有何区别?
    • Array能够存储基本数据类型和对象,ArrayList只能存储对象
    • Array是指定大小的,ArrayList是可以自动扩展的
    • Array的内置方法没有ArrayList多
  14. 如何决定使用HashMap还是TreeMap?
    • HashMap:当你需要在Map中进行插入、删除和查询操作,因为HashMap效率要高
    • TreeMap:当你需要对一个key集合进行排序时
  15. 集合中的迭代器(Iterator)是什么?它有什么特点?
    • Iterator可以遍历任意的Collection接口,在使用一个Collection时可以使用迭代器方法获得迭代器实例。Iterator取代了Java集合框架中的Enumeration,迭代器允许调用者在迭代过程中移除元素(Iterator.remove())
    • Iterator的特点:效率比普通的for循环要高,在当前遍历的集合元素被更改的时候(List.remove()、List.add(String str)等),就会抛出ConcurrentModificationException异常,即并发修改异常。详见:https://blog.csdn.net/sukier_JE/article/details/79898892
  16. Iterator和ListIterator有什么区别?
    • Iterator可以遍历Set和List集合,而ListIterator只能遍历List
    • Iterator只能单向遍历,而ListIterator可以双向遍历
    • ListIterator从Iterator接口继承,解决了并发修改的问题
  17. 怎么确保一个集合不能被修改?
    • 使用Collections.unmodifiableCollection(Collection c)方法来创建一个只读的集合,当对只读集合进行修改时,集合都会抛出java.lang.UnsupportedOperationException异常
  18. 如何遍历HashMap集合?
    • 方式一:获取key集合,通过key值遍历value
    • 方式二:通过Iterator进行遍历map.entrySet()集合
    • 方式三:使用forEach()直接遍历map.entrySet()集合,推荐使用
    •  方式四:直接遍历Value集合,只能遍历value得不到key

posted @ 2019-08-08 17:40  SvaloR  阅读(196)  评论(0编辑  收藏  举报