java集合,面试大考点

1. 总揽全局

java集合包括 Collection接口Map接口,其中Collection接口包括 LIst接口Set接口 ,最后还有也别重要的 Iterator迭代器接口Collections工具类

1.1 Java集合的引出

面向对象语言对事物的体现都是以对象的形式,为了方便对多个对象进行操作,就要对对象进行存储(这里的存储,主要指的是内存层面的存储,不涉及持久化存储)。其中,使用Array(数组)也可以存储对象,但是具有一些弊端 :
(1)数组在内存存储方面的特点

  • 数组初始化以后,长度就确定了
  • 数组声明的类型,就决定了进行元素初始化时的类型(优点)

(2)数组在存储数据方面的弊端

  • 数组初始化以后,长度就不可变了,不便于扩展
  • 数组中提供的属性和方法少,不便于进行添加、删除、插入等操作,且效率不高。同时无法直接获取存储的实际元素的个数(也就是无属性、方法可用于获取存储的实际元素的个数)。
  • 数组存储的数据是有序的、可以重复的。对于无序、不可重复的需求无法满足。

而Java集合可以动态的把多个对象的引用放如容器中,可以用于存储数量不等的多个对象,还可以用于保存具有映射关系的关联数组。 解决了数组存储数据方面的弊端

1.2 框图

  1. Collection
    在这里插入图片描述
  2. Map
    在这里插入图片描述

2. Collection接口

2.1 简介

Collection接口是List、Set 、Queue 等接口的父接口,该接口里定义的方法既可用于操作Set集合,也可用于操作List 和 Queue 集合。

【注】:JDK不提供此接口的任何直接实现,而是提供更具体的子接口(如:List、Set)实现;从JDK 5.0 增加了泛型以后,Java集合可以记住容器中对象的数据类型。

2.2 常用方法

在这里插入图片描述
v

3. iterator 迭代器接口

3.1 简介

在这里插入图片描述

3.2 iterator接口常用方法

在这里插入图片描述

3.2.1 hasNext() 和 next() 方法

在这里插入图片描述
在调用 it.next() 方法之前需要调用 it.hasNext() 进行检测。若不调用,且下一条记录无效,则直接调用 it.next() 会抛出 NoSuchElementException 异常。

在这里插入图片描述

3.2.2 remove() 方法

  • iterator 可以删除集合的元素,但是遍历过程之中通过迭代器对象的 remove 方法,不是集合对象的 remove 方法。
  • 如果还未调用next() 或在上一次调用next() 方法之后已经调用了 remove 方法,再调用 remove 都会报 IllegalStateException 。

3.3 使用foreach循环遍历集合元素

增强for循环:
遍历集合的底层调用iterator完成操作
在这里插入图片描述
【注】:foreach不只可以遍历集合,还可以遍历数组。

来道面试小练习!!!!
在这里插入图片描述
结果:

st
st
st
st
st
null
null
null
null
null

4. List接口

4.1 概述

鉴于java中数组用来存储数据的局限性,我们通常使用List代替数组。List集合类中元素有序、且可重复,集合中的每个元素都有其对应的顺序索引,可以根据索引存取容器中的元素。又因其可以根据元素多少,自动扩容,可以称其为“动态数组”。List接口的实现类常用的有:ArrayList、LinkedList、Vector(Vector的子类为Stack)。

4.2 List接口方法

在这里插入图片描述

4.3 LIst实现类

4.3.1 ArrayList

ArrayList 是 LIst 接口的典型实现类、主要实现类,本质上是对象引用的一个“变长”数组(动态数组)。它是线程不安全的,所以效率高;底层使用Object[ ] elementData 存储。

ArrayList的JDK1.8之前与之后的实现区别?

  • JDK1.7:ArrayList像饿汉式,直接创建一个初始容量为10的数组;
    list.add(123); 相当于 elementData[0] = new Integer(123);
    若容量不够,默认扩容为原来的1.5倍,同时需要将原有数组中的数据复制到新的数组中。
  • JDK1.8:ArrayList像懒汉式,一开始创建一个长度为0的数组,当添加第一个元素时再创建一个初始容量为10的数组;
    后续的添加和扩容操作与JDK1.7无异。

【注】:

  1. 在工具类Arrays中有Arrays.asList()方法返回的是一个固定长度List集合,不能再添加,否则报java.lang.UnsupportedOperationException;
  2. 建议开发中使用带参的构造器,直接输入容量,可以防止因扩容带来的时间复杂度增大;
  3. 调用 Collections.sychronizedList(list) 方法可以让ArrayList转变成线程安全的。

4.3.2 LinkedList

对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储。

新增方法:
在这里插入图片描述

4.3.3 Vector

作为List接口的古老实现类,它是线程安全的,所以效率低;底层使用Object[ ] elementData 存储。

在各种 list 中,最好把ArrayList作为缺省选择。当插入、删除频繁时,使用LinkedList;Vector总是比ArrayList慢,所以尽量避免使用。

新增方法:
在这里插入图片描述

4.4 来一个面试题

在这里插入图片描述
在这里插入图片描述
结果:

[1,2]
//因为当添加时,add方法只有一个,所以参数会自动装箱,但是当调用remove时,remove方法有两个(Collection、List),一般能不装箱就不装箱,所以这里的2是索引。

5. Set 接口

5.1 概述

Set 接口是Collection接口的子接口,它没有额外的方法,且不允许包含相同的元素。Set集合判断两个对象是否相同不是使用 == 运算符,而是根据equals()方法。

5.2 Set的实现类

5.2.1 HsahSet

HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类。HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的 存取、查找、删除性能 。

特点:

  1. 不保证元素的排列顺序
  2. HashSet不是线程安全的
  3. 集合元素可以为null

HashSet 集合判断两个元素相等的标准:
两个对象通过 hashCode () 方法比较相等,并且两个对象的 equals() 方法返回值也相等 。 因此对于存放在 Set 容器中的对象,对应的类一定要重写 equals 和 hashCode(Object obj) 方法,以实现对象相等规则 。即:“相等的对象必须具有相等的散列码 。

向HashSet中添加元素的过程:

  1. 当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据 hashCode 值,通过某种散列函数决定该对象在 HashSet 底层数组中的存储位置 。( 这个散列函数会与底层数组的长度相计算得到在数组中的下标,并且这种散列函数计算还尽可能保证能均匀存储元素,越是散列分布该散列函数设计的越好)
  2. 如果两个元素的 hashCode() 值相等,会再继续调用 equals 方法,如果equals 方法结果为 true,添加失败;如果 为 false,那么会保存该元素,但是该数组的位置已经有元素了,那么会通过链表的方式继续链接 。
    【注】:如果 两个元素的 equals() 方法返回 true ,但它们的 hashCode () 返回值不相等, hashSet 将会把它们存储在不同的位置,但依然可以添加成功 。

在这里插入图片描述
重写equals()方法的基本原则:
向Set中添加的数据,其所在类一要重写hashCode() 和 equals() 方法,重写 equals 方法的时候一般都需要同时复写 hashCode 方法 。 通常参与计算 hashCode 的对象的属性也应该参与到 equals() 中进行计算。当两个对象的 equals() 方法比较返回 true 时,这两个对象的 hashCode()方法的返回值也应相等。

5.2.2 LinkedHashSet

LinkedHashSet 是 HashSet 的子类,它是根据元素的 hashCode 值来决定元素的存储位置,但同时使用双向链表维护元素的次序。因此,LinkedHashSet 插入性能略低于 HashSet,但在迭代访问 Set 里的全部元素时有很好的性能。
在这里插入图片描述

5.2.3 TreeSet

TreeSet 是 SortedSet 接口的实现类,可以确保集合元素处于排序状态,其底层使用红黑树结构存储数据。

特点:

  1. 向TreeSet中添加的数据,要求是相同类的对象。
  2. 可以按照添加对象的指定属性进行排序。
  3. 有序,查询速度比List快

TreeSet 两种排序方式:自然排序(实现Comparable)、定制排序(实现Comparator)(默认情况下采用自然排序)。

自然排序:
在这里插入图片描述
定制排序:
在这里插入图片描述
【注】:对于 TreeSet 集合而言,它 判断两个对象是否相等的唯一标准是:两个对象通过 compareTo (Object obj ) 方法比较返回值。当需要把一个对象放入 TreeSet 中,重写该对象对应的 equals() 方法时,应保证该方法与 compareTo (Object obj ) 方法有一致的结果:如果两个对象通过equals() 方法比较返回 true ,则通过 compareTo (Object obj ) 方法比较应 返回 0 。否则,让人难以理解。

5.3面试题

在这里插入图片描述
结果:
在这里插入图片描述

6. Map接口

6.1 概述

Map接口 与 Collection接口 并列存在,用于保存具有 映射关系 的数据 :key value。key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到唯一的、确定的 value。key 和 value可以是任何引用类型的数据,其中key 用 Set 来存放(无序的、不可重复的),具体使用什么Set要看是什么Map,即 key 对象所对应的类须重写 hashCode 和 equals 方法(以HashMap为例);value用Collection存放(无序的、可重复的),即 value 所在类要重写 equals();一 个 key、value 对构成一 个 entry 对象,所有的 entry 构成的集合是 Set(无序的、不可重复的)。

在这里插入图片描述

6.2 常用方法

在这里插入图片描述
在这里插入图片描述

6.3 Map实现类

在这里插入图片描述

6.3.1 HashMap

1.简介
HashMap作为Map的主要实现类,java1.2版本出现,其线程是不安全的,但是效率高。允许使用 null 键和 null 值,与 HashSet 一样,不保证映射的顺序。

2.存储结构

  • JDK 7及以前版本: HashMap 是数组+链表结构 (即为链地址法)

  • JDK 8版本发布以后: HashMap 是数组+链表+红黑树实现。
    在这里插入图片描述
    JDK 7及以前版本:
    实例化: 当实例化一个 HashMap 时,系统会创建一个长度为 Capacity 的 Entry 数组,这个长度在哈希表中被称为容量(Capacity),在这个数组中可以存放元素的位置我们称之为 “桶”(bucket),每个bucket 都有自己的索引,系统可以根据索引快速的查找 bucket 中的元素 。每个 bucket 中存储一个元素,即一个 Entry 对象。但每一个 Entry 对象可以带一个引用变量,用于指向下一个元素。因此,在一个桶中就有可能生成一个 Entry 链 。而且新添加的元素作为链表的 head 。
    添加元素: 向HashMap 中添加 entry1 (key,value) 需要首先计算 entry1 中 key 的哈希值 (根据 key 所在类的 hashCode() 计算得到),此哈希值经过处理以后,得到在底层 Entry[] 数组中要存储的位置 i 。如果位置 i 上没有元素,则 entry 1 直接添加成功 。如果位置 i 上已经存在 entry2(或还有链表存在的 entry3,entry4),则需要通过循环的方法,依次比较 entry1 中 key 和 其他 entry 的。如果彼此 hash 值不同,则直接添加成功 。 如果 hash 值不同,继续比较二者是否 equals 。如果返回值为 true,则使用 entry1 的 value 去替换 equals 为 true 的 entry 的 value 。如果遍历一遍以后,发现所有的 equals 返回都为 false, 则 entry1 仍可添加成功 。 entry1 指向原有的 entry 元素 。
    HashMap扩容: 当 HashMap 中的元素越来越多的时候,hash 冲突的几率也就越来越高, 因为数组的长度是固定的 。 所以为了提高查询的效率,就要对 HashMap 的数组进行扩容,而在 HashMap 数组扩容之后,最消耗性能的点就出现了:原数组中的数据必须重新计算其在新数组中的位置,并放进去,这就是 resize 。
    什么时候进行扩容呢? : 当 HashMap 中的元素个数超过 数组大小×loadFactor 时,就会进行数组扩容,loadFactor 的默认值为0.75,这是一个折中的取值 。默认情况下数组大小为 16 那么当 HashMap 中元素个数超过 16×0.75=12(这个值就是代码中的 threshold 值),且要存放的位置非空时,就把数组的大小扩展为 2×16=32 ,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知 HashMap 中元素的个数,那么预设元素的个数能够有效的提高 HashMap 的性能 。

    JDK 8版本:
    实例化: 当实例化一个 HashMap 时,会初始化 initialCapacity 和 loadFactor,在 put 第一对映射关系时,系统会创建一个长度为initialCapacity 的 Node 数组,这个长度在哈希表中被称为容量 Capacity,在这个数组中可以存放元素的位置我们称之为 “桶”(bucket),每个 bucket 都有自己的索引,系统可以根据索引快速的查找 bucket 中的元素 。
    添加元素: 每个 bucket 中存储一个元素,即一个 Node 对象,但每一个 Node 对象可以带一个引用变量 next,用于指向下一个元素。因此,在一个桶中,就有可能生成一个 Node 链 ,也可能是一个 TreeNode 对象。每一个 TreeNode 对象可以有两个叶子结点 left 和 right,因此在一个桶中,就有可能生成一个 TreeNode 树 。而新添加的元素作为链表的 last 或 树的叶子结点 。
    HashMap 什么时候进行扩容呢?: 当HashMap 中的元素个数超过数组大小×loadFactor 时,当 HashMap 中元素个数超过 16×0.75=12(这个值就是代码中的 threshold 值),且要存放的位置非空时,就把数组的大小扩展为 2×16=32 ,即扩大一倍,然后重新计算每个元素在数组中的位置。
    HashMap 什么时候进行树形化呢?: 当 HashMap 中的其中一个链的对象个数如果达到了 8 个,此时如果 capacity 没有达到 64 ,那么 HashMap 会先扩容解决,如果已经达到了 64 ,那么这个链会变成树,结点类型由 Node 变成 TreeNode 类型。当然,如果当映射关系被移除后,下次 resize 方法时判断树的结点个数低于 6 个,也会把树再转为链表。
    关于映射关系的 key 是否可以修改 answer :不要修改:映射关系存储到 HashMap 中会存储 key 的 hash 值,这样就不用在每次查找时重新计算每一个 Entry 或 Node (TreeNode )的 hash 值了,因此如果已经 put 到 Map 中的映射关系,再修改 key 的属性,而这个属性又参与 hashcode 值的计算,那么会导致匹配不上。

6.3.2 LinkedHashMap

LinkedHashMap 是 HashMap 的子类,java1.4版本出现,在遍历map元素时,可以按照添加的顺序实现遍历,其顺序遍历是因为双向链表结构实现,所以对于频繁的遍历操作,此类执行效率高于HashMap。
在这里插入图片描述

6.3.3 TreeMap

java1.2版本出现,按照添加的键值对进行排序,实现排序遍历,此时考虑key的自然排序定制排序。底层使用的是红黑树。

  • 自然排序:TreeMap 的所有的 Key 必须实现 Comparable 接口,而且所有的 Key 应该是同一个类的对象,否则将会抛出ClasssCastException !
  • 定制排序:创建 TreeMap 时,传入一个 Comparator 对象,该对象负责对 TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现 Comparable 接口 !

TreeMap判断两个key相等的标准:两个key通过compareTo()方法或者compare()方法返回0。

6.3.4 Hashtable

作为古老的实现类,java1.0版本出现,其线程是安全的,所以效率低不能存储值为 null 的 key 和 value。Hashtable实现原理和HashMap相同,功能相同。底层都使用哈希表结构,查询速度快,很多情况下可以互用。Hashtable判断两个key相等、两个value相等的标准也与HashMap一致。

6.3.5 Properties

Properties 是 Hashtable 的子类,常用来处理配置文件。key 和 value 都是 String 类型。存取数据时,建议使用setProperty(String key,String value)方法和 getProperty(String key)方法
在这里插入图片描述

6.4 面试题

1.谈谈你对HashMap中put/get方法的认识?如果了解再谈谈HashMap的扩容机制?默认大小是多少?什么是负载因子(或填充比)?什么是吞吐临界值(或阈值、threshold)?
答:该问题上面都有介绍,这里补充一点,吞吐临界值(或阈值、threshold) = 数组大小×loadFactor !

在这里插入图片描述

7 Collections工具类

Collections 是一个操作 Set、List 和 Map 等集合的工具类,它提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法。

7.1 常用方法

在这里插入图片描述
在这里插入图片描述

7.2 同步控制

Collections 类中提供了多个 synchronizedXxx() 方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值