Java集合面试题总结

文章目录

    • 一、 主要容器概述
    • 二、 List
      • 1. ArrayList
      • 2. LinkedList
      • 3. Vector
      • 4. ArrayList 与 LinkedList 异同
      • 5. ArrayList和Vector的区别
      • 6. System.arraycopy()和 Arrays.copyOf()
    • 三、 Set
      • 1. 哈希表
      • 2. HashSet
      • 3. hashCode()
      • 4. TreeSet
    • 四、 Map
      • 1. HashMap
      • 2. HashTable
      • 3. LinkedHashMap
    • 五、 Collections常用方法
      • 排序
      • 查找,替换操作
      • 同步控制
      • 设置不可变集合

一、 主要容器概述

注意Collection是一个接口,是List和Set的公共接口,Collections是一个工具类

Java容器主要有三个

  • List:是一个有序集合,可以放重复的数据
  • Set:是一个无序集合,不允许放重复的数据
  • Map:是一个无序集合,集合中包含一个键对象,-一个值对象,键对象不允许重复,值对象可以重复(身份证号-姓名)

size,length,length ()区别?

  • size()是集合中使用,统计集合中元素的个数
  • length是数组的一个成员属性
  • length ()是String的一个方法

Iterator

方法 描述
boolean hasNext() 判断集合中是否还有下一个元素
Object next() 返回下一个元素
void remove() 从集合中删除一个由 next()方法返回的元素

二、 List

  • ArrayList:查询数据比较快,添加和删除数据比较慢(基于可变数组)
  • LinkedList:查询数据比较慢,添加和删除数据比较快(基于链表数据结构)

1. ArrayList

查找比较快,增删比较慢

  • 底层是基于数组。
  • 默认容量是10
  • 每次扩容1.5倍
  • 如果增加 0.5 倍后的新容量超过限制的容量,则用所需的最小容量与限制的容量进行判断,超过则指定为 Integer 的最大值
  • 通过数组的复制将原数据复制到一个更大(新的容量大小)的数组

如何保证线程安全?

  • 继承ArrayList,重写其中的方法
  • List list = Collections.synchronizedList(new ArrayList());

2. LinkedList

增删比较快,查找比较慢

  • 基于链表的,除了存放数据,还需要存放指针,所以占用的空间会大一些

  • 它实现了 Deque 接口,使得 LinkedList 类也具有队列的特性

  • 不是线程安全的

3. Vector

Vector与 ArrayList 一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一 个线程能够写 Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问 ArrayList 慢。

4. ArrayList 与 LinkedList 异同

  • 是否保证线程安全: ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;

  • 底层数据结构: Arraylist 底层使用的是 Object 数组; LinkedList 底层使用的是双向链表数据结构JDK1.6之前为循环链表, JDK1.7 取消了循环。注意双向链表和双向循环链表的区别:);

  • 插入和删除是否受元素位置的影响

    • ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。 比如:执行 add(Ee) 方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是 O(1)。但是如果要在指定位置 i 插入和删除元素的话( add(int index, E element) )时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。
    • LinkedList 采用链表存储,所以插入,删除元素时间复杂度不受元素位置的影响,都是近似 O(1)而数组为近似 O(n)。
  • 是否支持快速随机访问: LinkedList 不支持高效的随机元素访问,而 ArrayList 支持。快速随机访问就

5. ArrayList和Vector的区别

  • Vector 类的所有方法都是同步的。可以由两个线程安全地访问一个 Vector 对象、但是一个线程访问 Vector的话代码要在同步操作上耗费大量的时间。
  • Arraylist 不是同步的,所以在不需要保证线程安全时时建议使用 ArrayList。【CopyOnWriteArrayList 是同步的】。

6. System.arraycopy()和 Arrays.copyOf()

  1. arraycopy()需要目标数组,将原数组拷贝到你自己定义的数组里,而且可以选择拷贝的起点和长度以及放入新数组中的位置
  2. copyOf()是系统自动在内部新建一个数组,并返回该数组。

三、 Set

在Set家族中,常用的有三种

  • HashSet: 采用 Hashmap 的 key 来储存元素,主要特点是无序的,基本操作都是 O(1) 的时间复杂度,很快。
  • LinkedHashSet: 这个是一个 HashSet + LinkedList 的结构,特点就是既拥有了 O(1) 的时间复杂度,又能够保留插入的顺序。
  • TreeSet: 采用红黑树结构,特点是可以有序,可以用自然排序或者自定义比较器来排序;缺点就是查询速度没有 HashSet 快。

1. 哈希表

哈希表是一种数据结构,哈希表能够提供快速存取操作。哈希表是基于数组的,所以也存在缺点,数
组一旦创建将不能扩展。

正常的数组,如果需要查询某个值,需要对数组进行遍历,只是一种线性查找,查找的速度比较慢。
如果数组中的元素值和下标能够存在明确的对应关系,那么通过数组元素的值就可以换算出数据元素的下
**标,通过下标就可以快数定位数组元素,这样的数组就是哈希表。**一张哈希表:

元素值 10 11 12 13 14 15 16 17 18
元素下标 0 1 2 3 4 5 6 7 8

以上我们的示例元素值和下标的关系为

  • 元素下标=元素值-10,此时的示例 hashcode 就是和数组下标一致了,取得 hashcode 方法如下:

    //取得 hashCode
    pubic int hashCode(int value) {
    	return value – 10;
    }
    

2. HashSet

  • HashSet中的数据是无序的不可重复的。
  • 采用 Hashmap 的 key 来储存元素

3. hashCode()

覆盖了 equals 和 hashCode,当 hashCode 相同,它会调用 equals 进行比较,如果 equals 比较相等将不加把此元素加入到 Set 中

但 equals 比较不相等会重新根据 hashCode 换算位置仍然会将该元素加入进去的。

hashCode() 与 equals() 的相关规定

  • 如果两个对象相等,则 hashcode 一定也是相同的
  • 两个对象相等,对两个对象分别调用 equals 方法都返回 true
  • 两个对象有相同的 hashcode 值,它们也不一定是相等的
  • 因此, equals 方法被覆盖过,则 hashCode 方法也必须被覆盖
  • hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)

再次强调:特别是向 HashSet 或 HashMap 中加入数据时必须同时覆盖 equals 和 hashCode 方法,应该
养成一种习惯覆盖 equals 的同时最好同时覆盖 hashCode

Java 要求

  • 两个对象 equals 相等,那么它的 hashcode 相等
  • 两个对象 equals 不相等,那么它的 hashcode 并不要求它不相等,但一般建议不相等hashcode 相等不代表两个对象相等(采用 equals 比较)

为什么重写 equals() 就必须要重写 hashCode()?

  • 当key的hashCode()计算的数值相同时,就会出现hash冲突

处理 hash 冲突有哪些方法?Java 中用的哪一种?为什么?另一种方法你在工作中用过吗?在什么情况下用得多?

见:https://xzzz2020.gitee.io/post/d0otbdSTy

4. TreeSet

  • TreeSet 可以对 Set 集合进行排序, 默认自然排序(即升序)
  • 引用类型实现排序,需要实现Comparator或者Comparable

Comparable 和Comparator 的区别?

  • Comparable是自然排序,需要实体类实现,需要修改源代码
  • Comparator 是定制排序,不需要更改源代码,定义一个比较规则,传递给需要调用的方法中,将比较策略与数据分离,体现了策略模式
  • Comparator 比Comparable 优先级更高

四、 Map

关于HashMap的全部面试题见:https://xzzz2020.gitee.io/post/hashmap

Map 中可以放置键值对,也就是每一个元素都包含键对象和值对象, Map 实现较常用的为 HashMap,HashMap 对键对象的存取和 HashSet 一样,仍然采用的是哈希算法,所以如果使用自定类作为 Map 的键对象,必须复写 equals 和 hashCode 方法

Map中常用的三个

  • HashMap: 与 HashSet 对应,也是无序的,O(1)。
  • LinkedHashMap: 这是一个「HashMap + 双向链表」的结构,落脚点是 HashMap,所以既拥有 HashMap 的所有特性还能有顺序。
  • TreeMap: 是有序的,本质是用二叉搜索树来实现的。

1. HashMap

实现原理

  • 对于HashMap中的每个key,根据一个Hash函数,计算出一个Hash值,对应就是桶的编号,桶实际上是用数组实现的
  • Hash函数跟HashMap的容量有关系

如果不同的元素的key算出了相同的哈希值,那么该怎么存放呢?

  • 这就是哈希碰撞,即多个 key 对应了同一个桶。

HashMap 中是如何保证元素的唯一性的呢?即相同的元素会不会算出不同的哈希值呢?

  • 通过 hashCode() 和 equals() 方法来保证元素的唯一性。

2. HashTable

是一个线程安全的,但是锁住了全部数据效率低下

Java集合面试题总结_第1张图片

HashMap 和 Hashtable 的区别 ?

  1. 线程是否安全: HashMap 是非线程安全的, HashTable 是线程安全的; HashTable 内部的方法基本都经过 synchronized 修饰。(如果你要保证线程安全的话就使用 ConcurrentHashMap 吧!);

  2. 效率:因为线程安全的问题, HashMap 要比 HashTable 效率高一点。另外, HashTable 基本被淘汰,不要在代码中使用它;

  3. 对 Null key 和 Null value 的支持: HashMap 中, null 可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为 null。但是在 HashTable 中 put 进的键值只要有一个 null,直接抛出 NullPointerException。

  4. 初始容量大小和每次扩充容量大小的不同
    ①创建时如果不指定容量初始值, Hashtable 默认的初始大小为 11,之后每次扩充,容量变为原来的 2n+1。
    HashMap 默认的初始化大小为 16。 之后每次扩充,容量变为原来的 2 倍。
    ②创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为 2的幂次方大小(HashMap 中的 tableSizeFor() 方法保证,下面给出了源代码)。也就是说 HashMap 总是使用 2 的幂作为哈希表的大小,后面会介绍到为什么是 2 的幂次方。

  5. 底层数据结构: JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默
    认为 8)时,将链表转化为红黑树,以减少搜索时间。 Hashtable 没有这样的机制。

3. LinkedHashMap

保存了记录的插入顺序,在用 Iterator 遍历 LinkedHashMap时,先得到的记录肯定是先插入的,也可以在构造时带参数,按照访问次序排序 。

五、 Collections常用方法

排序

void reverse(List list)//反转
void shuffle(List list)//随机排序
void sort(List list)//按自然排序的升序排序
void sort(List list, Comparator c)//定制排序,由 Comparator 控制排序逻辑
void swap(List list, int i , int j)//交换两个索引位置的元素
void rotate(List list, int distance)//旋转。当 distance 为正数时,将 list 后 distance 个元素整体移到前面。当 distance 为负数时,将 list 的前 distance 个元素整体移到后面。  

查找,替换操作

int binarySearch(List list, Object key)//对 List 进行二分查找,返回索引,注意 List 必须是有序的
int max(Collection coll)//根据元素的自然顺序,返回最大的元素。
int min(Collection coll)
int max(Collection coll, Comparator c)//根据定制排序,返回最大元素,排序规则由 Comparatator 类控制。
int min(Collection coll, Comparator c) void fill(List list, Object obj)//用指定的元素代替指定 list 中
所有元素。 int frequency(Collection c, Object o)//统计元素出现次数
int indexOfSubList(List list, List target)//统计 target 在 list 中第一次出现的索引,找不到则返回-1,
int lastIndexOfSubList(List source, list target).
boolean replaceAll(List list, Object oldVal, Object newVal), 用新元素替换旧元素  

同步控制

Collections 提供了多个 synchronizedXxx()方法,该方法可以将指定集合包装成线程同步的集合,从而解决多
线程并发访问集合时的线程安全问题。

我们知道 HashSet, TreeSet, ArrayList,LinkedList,HashMap,TreeMap 都是线程不安全的。 Collections 提供了多个静态方法可以把他们包装成线程同步的集合

synchronizedCollection(Collection<T> c) //返回指定 collection 支持的同步(线程安全的)collection。
synchronizedList(List<T> list) //返回指定列表支持的同步(线程安全的) List。
synchronizedMap(Map<K,V> m) //返回由指定映射支持的同步(线程安全的) Map。
synchronizedSet(Set<T> s) //返回指定 set 支持的同步(线程安全的) set。

设置不可变集合

emptyXxx(): 返回一个空的、不可变的集合对象,此处的集合既可以是 List,也可以是 Set,还可以是 Map。

singletonXxx(): 返回一个只包含指定对象(只有一个或一个元素)的不可变的集合对象,此处的集合可以
是: List, Set, Map。

unmodifiableXxx(): 返回指定集合对象的不可变视图,此处的集合可以是: List, Set, Map。 上面三类方法的参数是原有的集合对象,返回值是该集合的”只读“版本

你可能感兴趣的:(面试题总结,java)