java核心——集合框架常见面试题总结

java集合常见面试题总结

1、java集合体系(List、Set、Collection、Map的区别和联系)



(1)Collection 接口存储一组不唯一,即可重复,无序的对象
(2)List 接口存储一组不唯一,可重复,有序(插入顺序)的对象
(3)Set 接口存储一组唯一,不可重复,无序的对象
(4)Map接口存储一组键值对象,提供key到value的映射。Key无序,唯一。value不要求有序,允许重复。(如果只使用key存储,而不使用value,那就是Set)

2、Vector和ArrayList的区别和联系

相同点
1)实现原理相同—底层都使用数组
2)功能相同—实现增删改查等操作的方法相似
3)都是长度可变的数组结构,很多情况下可以互用
不同点:
1)Vector是早期JDK版本提供,ArrayList是新版本替代Vector的
2)Vector线程安全,ArrayList重速度轻安全,线程非安全
长度需增长时,Vector默认增长一倍,ArrayList增长50%

3、ArrayList和LinkedList的区别和联系

相同点:
两者都实现了List接口,都具有List中元素有序、不唯一的特点。
不同点:
ArrayList实现了长度可变的数组,在内存中分配连续空间。遍历元素和随机访问元素的效率比较高,查询快,增删慢;

LinkedList采用链表存储方式。插入、删除元素时效率比较高,查询慢,增删快

4、HashMap和Hashtable的区别和联系

相同点:
实现原理相同,功能相同,底层都是哈希表结构,查询速度快,在很多情况下可以互用
不同点:
1、Hashtable是早期提供的接口,HashMap是新版JDK提供的接口
2、Hashtable继承Dictionary类,HashMap实现Map接口
3、Hashtable线程安全,HashMap线程非安全
4、Hashtable不允许null值,HashMap允许null值

5、HashSet的使用和原理(hashCode()和equals())

    1)哈希表的查询速度特别快,时间复杂度为O(1)。
    2)HashMap、Hashtable、HashSet这些集合采用的是哈希表结构,需要用到hashCode哈希码,hashCode是一个整数值。
    3)系统类已经覆盖了hashCode方法 自定义类如果要放入hash类集合,必须重写					hashcode。如果不重写,调用的是Object的hashcode,而Object的hashCode实际				上是地址。
    4)向哈希表中添加数据的原理:当向集合Set中增加对象时,首先集合计算要增加对象的hashCode码,根据该值来得到一个位置用来存放当前对象,如在该位置没有一个				对象存在的话,那么集合Set认为该对象在集合中不存在,直接增加进去。如果在该				位置有一个对象存在的话,接着将准备增加到集合中的对象与该位置上的对象进行				equals方法比较,如果该equals方法返回false,那么集合认为集合中不存在该对象,			在进行一次散列,将该对象放到散列后计算出的新地址里。如果equals方法返回true,			那么集合认为集合中已经存在该对象了,不会再将该对象增加到集合中了。
    5)在哈希表中判断两个元素是否重复要使用到hashCode()和equals()。hashCode决定数据			在表中的存储位置,而equals判断是否存在相同数据。
    6) Y=K(X) :K是函数,X是哈希码,Y是地址

6、TreeSet的原理和使用(Comparable和comparator)

    1)TreeSet集合,元素不允许重复且有序(自然顺序)
    2)TreeSet采用树结构存储数据,存入元素时需要和树中元素进行对比,需要指定比较策略。
    3)可以通过Comparable(外部比较器)和Comparator(内部比较器)来指定比较策略,实现了Comparable的系统类可以顺利存入TreeSet。自定义类可以实现Comparable接口来指定比较策略。
    4)可创建Comparator接口实现类来指定比较策略,并通过TreeSet构造方法参数传入。这种方式尤其对系统类非常适用。

7、集合和数组的比较(为什么引入集合)

    数组不是面向对象的,存在明显的缺陷,集合完全弥补了数组的一些缺点,比数组更灵活更实用,可大大提高软件的开发效率而且不同的集合框架类可适用于不同场合。具体如下:
    1)数组的效率高于集合类.
    2)数组能存放基本数据类型和对象,而集合类中只能放对象。
    3)数组容量固定且无法动态改变,集合类容量动态改变。
    4)数组无法判断其中实际存有多少元素,length只告诉了array的容量。
    5)集合有多种实现方式和不同的适用场合,而不像数组仅采用顺序表方式。
    6)集合以类的形式存在,具有封装、继承、多态等类的特性,通过简单的方法和属性调用即可实现各种复杂操作,大大提高软件的开发效率。

8、Collection和Collections的区别

    1)Collection是Java提供的集合接口,存储一组不唯一,无序的对象。它有两个子接口List和Set。
    2)Java中还有一个Collections类,专门用来操作集合类 ,它提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。

9、下列说法正确的有()(选择一项)

A. LinkedList继承自List
B. AbstractSet继承自Set
C.继承自AbstractSet
D.TreeMap继承自HashMap
答案: C
分析:A:LinkedList实现List接口
B:AbstractSet实现Set接口
D:TreeMap继承AbstractMap

10、Java的HashMap和Hashtable有什么区别?HashSet和HashMap有什么区别?使用这些结构保存的数需要重载的方法是哪些?

答:
HashMap与Hashtable实现原理相同,功能相同,底层都是哈希表结构,查询速度快,在很多情况下可以互用
两者的主要区别如下:
1、Hashtable是早期JDK提供的接口,HashMap是新版JDK提供的接口
2、Hashtable继承Dictionary类,HashMap实现Map接口
3、Hashtable线程安全,HashMap线程非安全
4、Hashtable不允许null值,HashMap允许null值

HashSet与HashMap的区别:
1、HashSet底层是采用HashMap实现的。HashSet 的实现比较简单,HashSet 的绝大部分方法都是通过调用 HashMap 的方法来实现的,因此 HashSet 和 HashMap 两个集合在实现本质上是相同的。
2、HashMap的key就是放进HashSet中对象,value是Object类型的。
3、当调用HashSet的add方法时,实际上是向HashMap中增加了一行(key-value对),该行的key就是向HashSet增加的那个对象,该行的value就是一个Object类型的常量

11、列出Java中的集合类层次结构?

答:
Java中集合主要分为两种:Collection和Map。
Collection是List和Set接口的父接口;ArrayList和LinkedList是List的实现类;HashSet和TreeSet是Set的实现类;LinkedHashSet是HashSet的子类。HashMap和TreeMap是Map的实现类;LinkedHashMap是HashMap的子类。
图中:虚线框中为接口,实线框中为类。

12、List,Set,Map各有什么特点

答:
List 接口存储一组不唯一,有序(插入顺序)的对象。
Set 接口存储一组唯一,无序的对象。
Map接口存储一组键值对象,提供key到value的映射。key无序,唯一。value不要求有序,允许重复。(如果只使用key存储,而不使用value,那就是Set)。

13、ArrayList list=new ArrayList(20);中的list扩充几次()

A 0
B. 1
C. 2
D. 3
答案:A
分析:已经指定了长度, 所以不扩容

14、List、Set、Map哪个继承自Collection接口,一下说法正确的是()

A List Map
B. Set Map
C. List Set
D. List Map Set
答案:C
分析:Map接口继承了java.lang.Object类,但没有实现任何接口.

15、合并两个有序的链表

public class Solution {
	public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
		if (l1 == null || l2 == null) {  
            return l1 != null ? l1 : l2;  
        }  
        ListNode head = l1.val < l2.val ? l1 : l2;  
        ListNode other = l1.val >= l2.val ? l1 : l2;  
        ListNode prevHead = head;  
        ListNode prevOther = other;  
        while (prevHead != null) {  
            ListNode next = prevHead.next;  
            if (next != null && next.val > prevOther.val) {  
                prevHead.next = prevOther;  
                prevOther = next;  
            }  
            if(prevHead.next==null){  
                prevHead.next=prevOther;  
                break;  
            }  
            prevHead=prevHead.next;  
        }  
        return head;
	}
}

16、用递归方式实现链表的转置。

/**
Definition for singly-linked list.
public class ListNode {
int val;
ListNode next;
ListNode(int x) { val = x; }
* }
*/
public class Solution {
	public ListNode reverseList(ListNode head) {
		if(head==null||head.next ==null)
            return head;
        ListNode prev = reverseList(head.next);
        head.next.next = head;
        head.next = null;
        return prev;
	}
}

17、给定一个不包含相同元素的整数集合,nums,返回所有可能的子集集合。解答中集合不能包含重复的子集。

public class Solution {
    public List<List<Integer>> subsets (int[] nums) {
        List<List<Integer>> res = new ArrayList<ArrayList<Integer>>();
        List<Integer> item = new ArrayList<Integer>();
        if(nums.length == 0 || nums == null)
            return res;
        Arrays.sort(nums); //排序
        dfs(nums, 0, item, res);  //递归调用
        res.add(new ArrayList<Integer>());  //最后加上一个空集
        return res;
    }
    public static void dfs(int[] nums, int start, List<Integer> item, List<List<Integer>> res){
        for(int i = start; i < nums.length; i ++){
            item.add(nums[i]);
            //item是以整数为元素的动态数组,而res是以数组为元素的数组,在这一步,当item增加完元素后,item所有元素构成一个完整的子串,再由res纳入
            res.add(new ArrayList<Integer>(item));
            dfs(nums, i + 1, item, res);
            item.remove(item.size() - 1);
        }
    }
}

18、以下结构中,哪个具有同步功能()

A. HashMap
B. ConcurrentHashMap
C. WeakHashMap
D. TreeMap
答案:B
分析:
A,C,D都线程不安全,B线程安全,具有同步功能

19、以下结构中,插入性能最高的是()

A ArrayList
B. Linkedlist
C. tor
D. Collection
答案:B
分析:
数组插入、删除效率差,排除A
tor不是java里面的数据结构,是一种网络路由技术;因此排除C
Collection 是集合的接口,不是某种数据结构;因此排除D

20、以下结构中,哪个最适合当作stack使用()

A LinkedHashMap
B. LinkedHashSet
C. LinkedList
答案:C
分析:
Stack是先进后出的线性结构;所以链表比较合适;不需要散列表的数据结构

21、Map的实现类中,哪些是有序的,哪些是无序的,有序的是如何保证其有序性,你觉得哪个有序性性能更高,你有没有更好或者更高效的实现方式?

答:
1.Map的实现类有HashMap,LinkedHashMap,TreeMap
2.HashMap是有无序的,LinkedHashMap和TreeMap都是有序的(LinkedHashMap记录了添加数据的顺序;TreeMap默认是自然升序)
3. LinkedHashMap底层存储结构是哈希表+链表,链表记录了添加数据的顺序
4. TreeMap底层存储结构是二叉树,二叉树的中序遍历保证了数据的有序性
5. LinkedHashMap有序性能比较高,因为底层数据存储结构采用的哈希表

22、下面的代码在绝大部分时间内都运行得很正常,请问什么情况下会出现问题?根源在哪里?

package com.bjsxt;
import java.util.LinkedList;
public class Stack {
	LinkedList list = new LinkedList();
	public synchronized void push(Object x) {
		synchronized (list) {
			list.addLast(x);
			notify();
		}
	}
	public  synchronized Object pop() throws  Exception{
		synchronized(list){
			if(list.size()<=0){
				wait();
			}
			return list.removeLast( );
		}
	}
}

答:
将if( list.size() <= 0 )
改成:
while( list.size() <= 0 )

23、TreeMap和TreeSet在排序时如何比较元素?Collections工具类中的sort()方法如何比较元素?

答:
TreeSet 要求存放的对象所属的类必须实现Comparable接口,该接口提供了比较元素的compareTo()方法,当插入元素时会 回调该方法比较元素的大小。
TreeMap 要求存放的键值对映射的键必须实现Comparable接口从而根据键对元素进行排序。
Collections 工具类的sort方法有两种重载的形式,第一种要求传入的待排序容器中存放的对象比较实现Comparable接口以实现元素的比较;第二种不强制性的要求容器中的元素必须可比较,但是要求传入第二个参数,参数是Comparator接口的子类型(需要重写compare方法实现元素的比较),相当于一个临时定义的排序规则,其实就是是通过接口注入比较元素大小的算法,也是对回调模式的应用。

24、List里面如何剔除相同的对象?请简单用代码实现一种方法

使用set集合元素不能重复的特点:

public class Test {
	public static void main(String[] args) {
  		List<String> li1 = new ArrayList<String>();
 		li1.add("8");
  		li1.add("8");
  		li1.add("9");
  		li1.add("9");
  		li1.add("0");
  		System.out.println(li1);
  		//方法:将List中数据取出来来存到Set中
  		HashSet<String> set = new HashSet<String>();
  		for(int i=0;i<li1.size();i++){
  			set.add(li1.get(i));
  		}
 		 System.out.println(set);
	}
}

25、Java.util.Map的实现类有

分析:Java中的java.util.Map的实现类
1、HashMap :底层哈希表
2、Hashtable :底层哈希表
3、LinkedHashMap :底层哈希表+链表
4、TreeMap :底层二叉树

26、List、Set、Map 是否继承自Collection 接口?

答:List、Set 的父接口是Collection,Map 不是其子接口,而是与Collection接口是平行关系,互不包含。

Map是键值对映射容器,与List和Set有明显的区别,而Set存储的零散的元素且不允许有重复元素(数学中的集合也是如此),List是线性结构的容器,适用于按数值索引访问元素的情形。

27、说出ArrayList、Vector、LinkedList 的存储性能和特性?

答:ArrayList 和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,
Vector由于使用了synchronized 方法(线程安全),通常性能上较ArrayList 差,
而LinkedList 使用双向链表实现存储(将内存中零散的内存单元通过附加的引用关联起来,形成一个可以按序号索引的线性结构,这种链式存储方式与数组的连续存储方式相比,其实对内存的利用率更高),按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。
Vector属于遗留容器(早期的JDK中使用的容器,除此之外Hashtable、Dictionary、BitSet、Stack、Properties都是遗留容器),现在已经不推荐使用,但是由于ArrayList和LinkedListed都是非线程安全的,如果需要多个线程操作同一个容器,那么可以通过工具类Collections中的synchronizedList方法将其转换成线程安全的容器后再使用(这其实是装潢模式最好的例子,将已有对象传入另一个类的构造器中创建新的对象来增加新功能)。

    补充:遗留容器中的Properties类和Stack类在设计上有严重的问题,Properties是一个键和值都是字符串的特殊的键值对映射,在设计上应该是关联一个Hashtable并将其两个泛型参数设置为String类型,但是Java API中的Properties直接继承了Hashtable,这很明显是对继承的滥用。这里复用代码的方式应该是HAS-A关系而不是IS-A关系,另一方面容器都属于工具类,继承工具类本身就是一个错误的做法,使用工具类最好的方式是HAS-A关系(关联)或USE-A关系(依赖)。同理,Stack类继承Vector也是不正确的。

28、List、Map、Set 三个接口,存取元素时,各有什么特点?

答:
List以特定索引来存取元素,可有重复元素。
Set不能存放重复元素(用对象的equals()方法来区分元素是否重复)。
Map保存键值对(key-value pair)映射,映射关系可以是一对一或多对一。
Set和Map容器都有基于哈希存储和排序树(红黑树)的两种实现版本,基于哈希存储的版本理论存取时间复杂度为O(1),而基于排序树版本的实现在插入或删除元素时会按照元素或元素的键(key)构成排序树从而达到排序和去重的效果。

29、TreeMap和TreeSet在排序时如何比较元素?Collections工具类中的sort()方法如何比较元素?

答:TreeSet要求存放的对象所属的类必须实现Comparable接口,该接口提供了比较元素的compareTo()方法,当插入元素时会回调该方法比较元素的大小。
TreeMap要求存放的键值对映射的键必须实现Comparable接口从而根据键对元素进行排序。
Collections工具类的sort方法有两种重载的形式,第一种要求传入的待排序容器中存放的对象比较实现Comparable接口以实现元素的比较;第二种不强制性的要求容器中的元素必须可比较,但是要求传入第二个参数,参数是Comparator接口的子类型(需要重写compare方法实现元素的比较),相当于一个临时定义的排序规则,其实就是是通过接口注入比较元素大小的算法,也是对回调模式的应用。

例子1:
Student.java

package com.bjsxt;
public class Student implements Comparable<Student> {  
    private String name;        // 姓名  
    private int age;            // 年龄  
    public Student(String name, int age) {  
        this.name = name;  
        this.age = age;  
    }  
    @Override  
    public String toString() {  
        return "Student [name=" + name + ", age=" + age + "]";  
    }  
    @Override  
    public int compareTo(Student o) {  
        return this.age - o.age; // 比较年龄(年龄的升序)  
    }  
  }

Test01.java

package com.bjsxt;
import java.util.Set;  
import java.util.TreeSet;  
  
class Test01 {  
  public static void main(String[] args) {  
        Set<Student> set = new TreeSet<>();     // Java 7的钻石语法(构造器后面的尖括号中不需要写类型)  
        set.add(new Student("Hao LUO", 33));  
        set.add(new Student("XJ WANG", 32));  
        set.add(new Student("Bruce LEE", 60));  
        set.add(new Student("Bob YANG", 22));  
          for(Student stu : set) {  
            System.out.println(stu);  
        }  
//      输出结果:   
//      Student [name=Bob YANG, age=22]  
//      Student [name=XJ WANG, age=32]  
//      Student [name=Hao LUO, age=33]  
//      Student [name=Bruce LEE, age=60]  
    }  
}

例子2:
Student.java

package com.bjsxt;
public class Student {  
    private String name;    // 姓名  
    private int age;        // 年龄  
    public Student(String name, int age) {  
        this.name = name;  
        this.age = age;  
    }  
    /**
     * 获取学生姓名
     */  
    public String getName() {  
        return name;  
    }  
    /**
     * 获取学生年龄
     */  
    public int getAge() {  
        return age;  
    }  
    @Override  
    public String toString() {  
        return "Student [name=" + name + ", age=" + age + "]";  
    }  
  }

Test02.java

package com.bjsxt;
import java.util.ArrayList;  
import java.util.Collections;  
import java.util.Comparator;  
import java.util.List;  
  
  class Test02 {  
   public static void main(String[] args) {  
        List<Student> list = new ArrayList<>();     // Java 7的钻石语法(构造器后面的尖括号中不需要写类型)  
        list.add(new Student("Hao LUO", 33));  
        list.add(new Student("XJ WANG", 32));  
        list.add(new Student("Bruce LEE", 60));  
        list.add(new Student("Bob YANG", 22));  
           
        // 通过sort方法的第二个参数传入一个Comparator接口对象  
        // 相当于是传入一个比较对象大小的算法到sort方法中  
        // 由于Java中没有函数指针、仿函数、委托这样的概念  
        // 因此要将一个算法传入一个方法中唯一的选择就是通过接口回调  
        Collections.sort(list, new Comparator<Student> () {  
         @Override  
            public int compare(Student o1, Student o2) {  
                return o1.getName().compareTo(o2.getName());    // 比较学生姓名  
            }  
        });  
           
        for(Student stu : list) {  
            System.out.println(stu);  
        }  
//      输出结果:   
//      Student [name=Bob YANG, age=22]  
//      Student [name=Bruce LEE, age=60]  
//      Student [name=Hao LUO, age=33]  
//      Student [name=XJ WANG, age=32]  
    }  
}

30、 请讲讲你所知道的常用集合类以及主要方法?

最常用的集合类是List 和 Map。
List 的具体实现包括 ArrayList 和 LinkedList,它们是可变大小的列表,比较适合构建、存储和操作任何类型对象的元素列表。List 适用于按数值索引访问元素的情形。
Map 提供了一个更通用的元素存储方法。 Map 集合类用于存储元素对(称作"键"和"值"),其中每个键映射到一个值

31、请说明Collection 和 Collections的区别。

Collection 是集合类的上级接口,继承与他的接口主要有Set 和List.
Collections 是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作

32、请简要说明ArrayList,Vector,LinkedList的存储性能和特性是什么?

ArrayList 和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector由于使用了synchronized方法(线程安全),通常性能上较ArrayList差,而LinkedList使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。

33、请说明ArrayList和LinkedList的区别?

ArrayList和LinkedList都实现了List接口,他们有以下的不同点:
ArrayList是基于索引的数据接口,它的底层是数组。它可以以O(1)时间复杂度对元素进行随机访问。与此对应,LinkedList是以元素列表的形式存储它的数据,每一个元素都和它的前一个和后一个元素链接在一起,在这种情况下,查找某个元素的时间复杂度是O(n)。
相对于ArrayList,LinkedList的插入,添加,删除操作速度更快,因为当元素被添加到集合任意位置的时候,不需要像数组那样重新计算大小或者是更新索引。
LinkedList比ArrayList更占内存,因为LinkedList为每一个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素。

34、请你说明HashMap和Hashtable的区别?

HashMap和Hashtable都实现了Map接口,因此很多特性非常相似。但是,他们有以下不同点:
HashMap允许键和值是null,而Hashtable不允许键或者值是null。
Hashtable是同步的,而HashMap不是。因此,HashMap更适合于单线程环境,而Hashtable适合于多线程环境。
HashMap提供了可供应用迭代的键的集合,因此,HashMap是快速失败的。另一方面,Hashtable提供了对键的列举(Enumeration)。
一般认为Hashtable是一个遗留的类

35、请说说快速失败(fail-fast)和安全失败(fail-safe)的区别?

Iterator的安全失败是基于对底层集合做拷贝,因此,它不受源集合上修改的影响。java.util包下面的所有的集合类都是快速失败的,而java.util.concurrent包下面的所有的类都是安全失败的。快速失败的迭代器会抛出ConcurrentModificationException异常,而安全失败的迭代器永远不会抛出这样的异常。

36、请你说说Iterator和ListIterator的区别?

Iterator和ListIterator的区别是:
Iterator可用来遍历Set和List集合,但是ListIterator只能用来遍历List。
Iterator对集合只能是前向遍历,ListIterator既可以前向也可以后向。
ListIterator实现了Iterator接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等。

37、请简单说明一下什么是迭代器?

Iterator提供了统一遍历操作集合元素的统一接口, Collection接口实现Iterable接口,
每个集合都通过实现Iterable接口中iterator()方法返回Iterator接口的实例, 然后对集合的元素进行迭代操作.
有一点需要注意的是:在迭代元素的时候不能通过集合的方法删除元素, 否则会抛出ConcurrentModificationException 异常. 但是可以通过Iterator接口中的remove()方法进行删除.

38、请解释为什么集合类没有实现Cloneable和Serializable接口?

克隆(cloning)或者是序列化(serialization)的语义和含义是跟具体的实现相关的。因此,应该由集合类的具体实现来决定如何被克隆或者是序列化。
实现Serializable序列化的作用:将对象的状态保存在存储媒体中以便可以在以后重写创建出完全相同的副本;按值将对象从一个从一个应用程序域发向另一个应用程序域。
实现 Serializable接口的作用就是可以把对象存到字节流,然后可以恢复。所以你想如果你的对象没有序列化,怎么才能进行网络传输呢?要网络传输就得转为字节流,所以在分布式应用中,你就得实现序列化。如果你不需要分布式应用,那就没必要实现实现序列化。

39、请说明Java集合类框架的基本接口有哪些?

集合类接口指定了一组叫做元素的对象。集合类接口的每一种具体的实现类都可以选择以它自己的方式对元素进行保存和排序。有的集合类允许重复的键,有些不允许。
Java集合类提供了一套设计良好的支持对一组对象进行操作的接口和类。Java集合类里面最基本的接口有:
Collection:代表一组对象,每一个对象都是它的子元素。
Set:不包含重复元素的Collection。
List:有顺序的collection,并且可以包含重复元素。
Map:可以把键(key)映射到值(value)的对象,键不能重复。

40、请你说明一下ConcurrentHashMap的原理?

ConcurrentHashMap 类中包含两个静态内部类 HashEntry 和 Segment。HashEntry 用来封装映射表的键 / 值对;Segment 用来充当锁的角色,每个 Segment 对象守护整个散列映射表的若干个桶。每个桶是由若干个 HashEntry 对象链接起来的链表。一个 ConcurrentHashMap 实例中包含由若干个 Segment 对象组成的数组。HashEntry 用来封装散列映射表中的键值对。在 HashEntry 类中,key,hash 和 next 域都被声明为 final 型,value 域被声明为 volatile 型。

static final class HashEntry<K,V> {
       final K key;                       // 声明 key 为 final 型
       final int hash;                   // 声明 hash 值为 final 型
       volatile V value;                 // 声明 value 为 volatile 型
       final HashEntry<K,V> next;      // 声明 next 为 final 型
  
   HashEntry(K key, int hash, HashEntry<K,V> next, V value) {
           this.key = key;
           this.hash = hash;
           this.next = next;
           this.value = value;
       }
}

在ConcurrentHashMap 中,在散列时如果产生“碰撞”,将采用“分离链接法”来处理“碰撞”:把“碰撞”的 HashEntry 对象链接成一个链表。由于 HashEntry 的 next 域为 final 型,所以新节点只能在链表的表头处插入。 下图是在一个空桶中依次插入 A,B,C 三个 HashEntry 对象后的结构图:
图1. 插入三个节点后桶的结构示意图:

注意:由于只能在表头插入,所以链表中节点的顺序和插入的顺序相反。
Segment 类继承于 ReentrantLock 类,从而使得 Segment 对象能充当锁的角色。每个 Segment 对象用来守护其(成员对象

41、请解释一下TreeMap?

TreeMap是一个有序的key-value集合,基于红黑树(Red-Black tree)的 NavigableMap实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator进行排序,具体取决于使用的构造方法。
TreeMap的特性:
根节点是黑色
每个节点都只能是红色或者黑色
每个叶节点(NIL节点,空节点)是黑色的。
如果一个节点是红色的,则它两个子节点都是黑色的,也就是说在一条路径上不能出现两个红色的节点。
从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

42、请说明ArrayList是否会越界?

ArrayList是实现了基于动态数组的数据结构,而LinkedList是基于链表的数据结构, 对于随机访问get和set,ArrayList要优于LinkedList,因为LinkedList要移动指针;ArrayList并发add()可能出现数组下标越界异常。

43、请你说明concurrenthashmap有什么优势以及1.7和1.8区别?

Concurrenthashmap线程安全的,1.7是在jdk1.7中采用Segment + HashEntry的方式进行实现的,lock加在Segment上面。1.7size计算是先采用不加锁的方式,连续计算元素的个数,最多计算3次:1、如果前后两次计算结果相同,则说明计算出来的元素个数是准确的;2、如果前后两次计算结果都不同,则给每个Segment进行加锁,再计算一次元素的个数;
1.8中放弃了Segment臃肿的设计,取而代之的是采用Node + CAS + Synchronized来保证并发安全进行实现,1.8中使用一个volatile类型的变量baseCount记录元素的个数,当插入新数据或则删除数据时,会通过addCount()方法更新baseCount,通过累加baseCount和CounterCell数组中的数量,即可得到元素的总个数;

44、请你说明一下TreeMap的底层实现?

TreeMap 的实现就是红黑树数据结构,也就说是一棵自平衡的排序二叉树,这样就可以保证当需要快速检索指定节点。
红黑树的插入、删除、遍历时间复杂度都为O(lgN),所以性能上低于哈希表。但是哈希表无法提供键值对的有序输出,红黑树因为是排序插入的,可以按照键的值的大小有序输出。
红黑树性质
性质1:每个节点要么是红色,要么是黑色。
性质2:根节点永远是黑色的。
性质3:所有的叶节点都是空节点(即 null),并且是黑色的。
性质4:每个红色节点的两个子节点都是黑色。(从每个叶子到根的路径上不会有两个连续的红色节点)
性质5:从任一节点到其子树中每个叶子节点的路径都包含相同数量的黑色节点。

45、请你说明ConcurrentHashMap锁加在了哪些地方?

加在每个Segment 上面。

46、请你解释HashMap的容量为什么是2的n次幂?

负载因子默认是0.75, 2^n是为了让散列更加均匀,例如出现极端情况都散列在数组中的一个下标,那么hashmap会由O(1)复杂退化为O(n)的。

47、请你简单介绍一下ArrayList和LinkedList的区别,并说明如果一直在list的尾部添加元素,用哪种方式的效率高?

ArrayList采用数组数组实现的,查找效率比LinkedList高。LinkedList采用双向链表实现的,插入和删除的效率比ArrayList要高。一直在list的尾部添加元素,LinkedList效率要高。

48、如果hashMap的key是一个自定义的类,怎么办?

使用HashMap,如果key是自定义的类,就必须重写hashcode()和equals()。

59、请你解释一下hashMap具体如何实现的?

Hashmap基于数组实现的,通过对key的hashcode & 数组的长度得到在数组中位置,如当前数组有元素,则数组当前元素next指向要插入的元素,这样来解决hash冲突的,形成了拉链式的结构。put时在多线程情况下,会形成环从而导致死循环。数组长度一般是2n,从0开始编号,所以hashcode & (2n-1),(2n-1)每一位都是1,这样会让散列均匀。需要注意的是,HashMap在JDK1.8的版本中引入了红黑树结构做优化,当链表元素个数大于等于8时,链表转换成树结构;若桶中链表元素个数小于等于6时,树结构还原成链表。因为红黑树的平均查找长度是log(n),长度为8的时候,平均查找长度为3,如果继续使用链表,平均查找长度为8/2=4,这才有转换为树的必要。链表长度如果是小于等于6,6/2=3,虽然速度也很快的,但是转化为树结构和生成树的时间并不会太短。还有选择6和8,中间有个差值7可以有效防止链表和树频繁转换。假设一下,如果设计成链表个数超过8则链表转换成树结构,链表个数小于8则树结构转换成链表,如果一个HashMap不停的插入、删除元素,

50、请你说明一下Map和ConcurrentHashMap的区别?

hashmap是线程不安全的,put时在多线程情况下,会形成环从而导致死循环。CoucurrentHashMap是线程安全的,采用分段锁机制,减少锁的粒度。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值