Java面试题六:关于Java容器List、Set、Map,知多少?为什么HashMap扩容是2的倍数?

一、概述

Java容器分为List、Set、Map,他们都是Collection接口的实现类。

Java集合框架主要包含两种类型的容器,一种是集合(Collection),存储元素的集合;另一种是Map,存储键值对映射。接口Collection包含3中类型接口:List、Set、Queue,具体常用的实现类包括:ArrayList、LinkedList、HashSet、LinkedHashSet、HashMap、LinkedHashMap 等。

层级如下:

二、具体容器介绍

1、List

     List是一个接口,继承Collection接口,代表有序的序列。List分为ArrayList、LinkedList和Vector,相比数组都可以动态伸缩。

     ①  ArrayList有很好的随机存取效率,底层使用数组实现,可以认为ArrayList默认是一个可以改变大小的数组,默认容量是10,存储元素有序,可以重复,线程是不安全的,增加和删除速度慢会扩容1.5倍+1的容量。

     ②  LinkedList排列有序,可重复,底层使用双向链表实现的,查询速度慢,增删快,线程是不安全的。LinkedList还实现了Queue接口,所以他还提供了offer(), peek(), poll()等方法。

     ③  Vector是排列有序的,可重复,底层使用的是数组,线程安全,但是效率低,当容量不够的时候,会扩容一倍的容量。

2、Set

   Set继承Collection接口,存储无序且不允许出现重复元素,主要由HashSet和TreeSet两大类以及LinkedHashSet,在调用重复元素的时候,Set集合会调用hashCode()和equal()方法熟悉爱你。

   ①  HashSet存储排列无序,不可重复的元素,底层使用哈希表来实现,底层使用的是HashMap的key来存储元素,使用hashCode的值获取插入元素的位置。

   ②  TreeSet存储排列无序,不可重复的元素,底层使用的是二叉树TreeMap来实现,底层是红黑树结构,每一个元素都是树中的一个结点,插入的元素都会进行排序。

扫描二维码关注公众号,回复: 13130447 查看本文章

   ③  LinkedHashSet,采用的是hash来存储,并用双向链表记录插入顺序,内部是LinkedHashMap

        自定义类使用Set的话需要实现Comparable接口或者传给Set一个Comparator比较器,需要重写Equals函数。TreeSet是实现红黑树数据结构后得到的顺序set。红黑树每个节点的值都大于等于他的左子树的值,小于等于右子树结点的值,这能确保红黑树运行时能快速在树种找到所需节点,这样一来,就可以从一个set里提到一个顺序集合。因此与hashSet相比,treeSet的优势在于:treeSet中的所有元素总是根据指定排序规则保持有序状态。hashSet初始是16个元素的数组,达到75%时扩容。LinkedHashSet是HashSet的子类,该类在HashSet的基础上将每一个元素根据插入顺序用链表连接起来,可以按插入顺序遍历。

3、Queue

   Queue是Java中的队列类,在队列的两端可以使用List、数组和链表来实现。

4、Map

   Map通常是以键值对的形式存储数据,主要实现类有:HashMap,HashTable,TreeMap,linkedHashMap。

   ①  HashTable的键不可重复,值可重复,底层是哈希表,键和值都不允许为空,支持多线程,同一时刻只有一个线程可以写,导致写入时比较慢,属于线程安全的。

   ② HashMap的键不可以重复,值可重复,底层是哈希表,根据键的HashCode 值存储数据,根据键可以直接获取它的值,具有很快的访问速度。允许键为空,值也可以为空。HashMap最多只允许一条记录的键为Null(多条会覆盖);允许多条记录的值为 Null。

   ③  TreeMap存储的元素根据key进行排序,升序,TreeMap不允许key是空的。

   ④   linkedHashMap保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.在遍历的时候会比HashMap慢。key和value均允许为空。

Map遍历方式:

1、map.entrySet()获得Entry集合,然后用foreach遍历或者获得entry集合的iterator遍历

2、map.keySet()获得键集合,然后调用map.get(key)方法访问值,使用forench遍历或者获得key集合的iterator遍历。

3、map.values()获得值集合,然后forench遍历或者获得值集合的iterator遍历

5、为什么HashMap扩容是2的倍数?

        HashMap的容量为什么是2的n次幂,和这个(n - 1) & hash的计算方法有着千丝万缕的关系,符号&是按位与的计算,这是位运算,计算机能直接运算,特别高效,按位与&的计算方法是,只有当对应位置的数据都为1时,运算结果也为1,当HashMap的容量是2的n次幂时,(n-1)的2进制也就是1111111***111这样形式的,这样与添加元素的hash值进行位运算时,能够充分的散列,使得添加的元素均匀分布在HashMap的每个位置上,减少hash碰撞,HashMap计算添加元素的位置时,使用的位运算,这是特别高效的运算;另外,HashMap的初始容量是2的n次幂,扩容也是2倍的形式进行扩容,是因为容量是2的n次幂,可以使得添加的元素均匀分布在HashMap中的数组上,减少hash碰撞,避免形成链表的结构,使得查询效率降低!

 

猜你喜欢

转载自blog.csdn.net/Sunshineoe/article/details/115138414