Java面试复习6 Java中的集合collection(list,set),Map等等
声明:本面试复习都基于一本未署名的Java面试宝典所述,根据自己的理解写出了这一专栏的复习博客
-
已知一个HashMap<Integer, User> 集合,User 有name (String) 和age (int) 属性。请写一个方法实现对HashMap的排序功能,该方法接收HashMap<Integer, User> 为形参,返回类型为HashMap<Integer, User> ,
要求对HashMap中的User的age倒序进行排序。排序时key=value键值对不得拆散。(作者说这是他的面试题,hhh)看到这个题,第一反应是头都大了,因为过去好久了,对于集合这块的只是已经遗忘的差不多额,那就借助此题复习了。
首先是集合的体系分类:
Java 集合可分为 Collection 和 Map 两种体系。
-
Collection接口: 单列数据, 定义了存取一组对象的方法的集合
其中一种是list集合,元素有序可重复,可以有多个null值
ArrayList,可变数组
LinkedList,双向链表
Vector,可变数组,线程安全
---------
另一种是set集合,元素无序且不可重复,只能有一个null值。
collection接口是list和set还有Queue接口的父接口。
该接口有一个iterator迭代器方法用于遍历集合Iterator iter = coll.iterator();//回到集合起点 while(iter.hasNext()){//必须进行此判断,判断是否有下一个元素可读取 Object obj = iter.next();//获取下一元素 if(obj.equals("Tom")){ iter.remove();//删除符合条件的集合元素 } //Iterator可以删除集合的元素, 但是是遍历过程中通过迭代器对象的remove方法, //不是集合对象的remove方法。 //如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove 方法, //再调用remove都会报IllegalStateException。
还有另外一种遍历方式我们经常用:foreach循环
for(Person p : persons){//Person:遍历元素的类型 //p:便利后自定义元素的名称 //persons:要遍历的结构名称 ... } //Java 5.0 提供了 foreach 循环迭代访问 Collection和数组。 //遍历操作不需获取Collection或数组的长度,无需使用索引访问元素。 //遍历集合的底层调用Iterator完成操作。 //foreach还可以用来遍历数组。
-
Map接口: 双列数据,保存具有映射关系“key-value对”的集合
①Map与Collection并列存在。用于保存具有映射关系的数据:key-value
②Map 中的 key 和 value 都可以是任何引用类型的数据
③Map 中的 key 用Set来存放, 不允许重复,即同一个 Map 对象所对应的类,须重写hashCode()和equals()方法
④常用String类作为Map的“键”
⑤key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到唯一的、确定的 value
⑥Map接口的常用实现类: HashMap、 TreeMap、 LinkedHashMap和Properties。 其中, HashMap是 Map 接口使用频率最高的实现类
OK,我们继续看这道题,这道题主要是考的HashMap相关的知识,
HashMap①HashMap是 Map 接口使用频率最高的实现类。
②允许使用null键和null值,与HashSet一样,不保证映射的顺序。
③所有的key构成的集合是Set:无序的、不可重复的。所以, key所在的类要重写:equals()和hashCode()
④所有的value构成的集合是Collection:无序的、可以重复的。所以, value所在的类要重写: equals()
⑤一个key-value构成一个entry所有的entry构成的集合是Set:无序的、不可重复的
⑥HashMap 判断两个 key 相等的标准是:两个 key 通过 equals() 方法返回 true,hashCode 值也相等。
⑦HashMap 判断两个 value相等的标准是:两个 value 通过 equals() 方法返回 true。
复习到这里,就非常纳闷了,既然HashMap和Map都是无序的,为什么题目还要求给排序呢?那就得看Map接口的实现类中谁是可以排序的。
那我们就挨个看看:
-
LinkedHashMap
①LinkedHashMap 是 HashMap 的子类
②在HashMap存储结构的基础上,使用了一对双向链表来记录添加元素的顺序
③与LinkedHashSet类似, LinkedHashMap 可以维护 Map 的迭代顺序:迭代顺序与 Key-Value 对的插入顺序一致 -
TreeMap
①TreeMap存储 Key-Value 对时, 需要根据 key-value 对进行排序。
②TreeMap 可以保证所有的 Key-Value 对处于有序状态。
③TreeSet底层使用红黑树结构存储数据
④TreeMap 的 Key 的排序:
<1>自然排序: TreeMap 的所有的 Key 必须实现 Comparable 接口,而且所有的 Key 应该是同一个类的对象,否则将会抛出 ClasssCastException
<2>定制排序:创建 TreeMap 时,传入一个 Comparator 对象,该对象负责对TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现Comparable 接口
⑤TreeMap判断两个key相等的标准:两个key通过compareTo()方法或者compare()方法返回0。 -
HashTable
①Hashtable是个古老的 Map 实现类, JDK1.0就提供了。不同于HashMap,
②Hashtable是线程安全的。
③Hashtable实现原理和HashMap相同,功能相同。底层都使用哈希表结构,查询速度快,很多情况下可以互用。
④与HashMap不同, Hashtable 不允许使用 null 作为 key 和 value
⑤与HashMap一样, Hashtable 也不能保证其中 Key-Value 对的顺序
⑥Hashtable判断两个key相等、两个value相等的标准, 与HashMap一致。 -
Properties
①Properties 类是 Hashtable 的子类,该对象用于处理属性文件
②由于属性文件里的 key、 value 都是字符串类型,所以 Properties 里的 key和 value 都是字符串类型
③存取数据时,建议使用setProperty(String key,String value)方法和getProperty(String key)方法
作者找到的就是Map的链表结构LinkedHashMap类,由于它是HashMap的子类,那么我们获取到的子类对象,就和父类的属性相同。
想好了一切,就是对排序方法的选择,在集合这里的对排序方法的选择侧重于集合所提供的的Collections工具类中的方法,直接调用就可以了。
工具类中的排序操作: (均为static方法)
reverse(List): 反转 List 中元素的顺序
shuffle(List): 对 List 集合元素进行随机排序
sort(List): 根据元素的自然顺序对指定 List 集合元素按升序排序
sort(List, Comparator): 根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
swap(List, int, int): 将指定 list 集合中的 i 处元素和 j 处元素进行交换看作者的解答:
可以看到的是,这里是对于无法拆分键值对的情况下进行的排序,先通过set集合获取map的键值对集合(整体获取),再将无序的set集合转换为有序的list集合,然后通过工具类比较对象中的age属性,得出排序后的list集合,最后给创建一个LinkHashMap的对象,将其存储进去,就得到了一个按照age排序的LinkHashMap集合。然后通过父类的引用,得到父类的相同集合。就算是解答完成了。 -
-
集合的安全性问题。请问ArrayList,HashSet,HashMap是线程安全的吗?如果不是怎样得到想要的线程安全的集合
这里我们需要知道的是:在默认的集合中,HashTable和Vector是线程安全的,其他的如果未经设置,都不是线程安全的。其实区分线程的安全与否也就是看他是否加上了同步锁,也就是synchronized关键字,是同步锁的标志。
Collections工具类提供了相关的API,可以让上面那3个不安全的集合变为安全的。如下:其实本质上就是加上了synchronized关键字。
-
ArrayList内部是用什么实现的?
内部是Object[]数组,当传入参数(即为数组长度)为空时,返回一个空的数组,相当于new了一个 Object[0] 数组,当传入参数小于零时报错,当大于零时就根据传入参数创建对应长度的新的数组。
这里我们需要注意的是,传入集合转变为数组的过程。
集合和数组作为两种不同的存放数据的方式,有什么不同?
①数组一旦被创建,其长度就是确定的。而集合是动态可变的。
②数组声明的类型,就决定了进行元素初始化时的类型,集合却可以有多种类型的存放
数组在存储数据方面的弊端
①数组初始化以后,长度就不可变了,不便于扩展(即使扩展利用了ArrayList也是在根本上new 了一个全新的list集合(也就是ArrayList可变数组),而本来的数组仍然不变)
②数组中提供的属性和方法少,不便于进行添加、删除、插入等操作, 且效率不高。同时无法直接获取存储元素的个数
③ 存储数据的特点单一,数组存储的数据是有序的、可以重复的。集合中有不同的接口可以满足不同的需要。
具体代码说明
在将集合转变为数组的过程中,底层的代码是这么实现的: -
并发集合和普通集合如何区别?
额…这个是真的没看明白,但是还是要粘贴上作者的解答,算是挖个坑吧。
-
List的三个实现类是哪几个,各有什么特点?
①ArrayList底层结构是数组,底层查询快,增删慢。
②LinkedList底层结构是链表型的,增删快,查询慢。
③vector底层结构是数组 线程安全的,增删慢,查询慢。
-
List,Map,Set的区别?
结构上
List和Set都属于单列存储数据,Map属于双列存储数据(键值对形式);
List是有序的,可重复的,允许有多个null值得存在,
Set是无序的不可重复的,只允许有一个null值存在,但是元素在集合中的位置由元素的hashcode值决定(也就是其位置是固定改的,用户无法做出更改,所以这个相对的确定因为其不可控也被归为无序),
Map中的无序的,其key值是不可重复的,value值是允许重复的
Map对于null值得处理是根据其子类不尽相同的,具体如下。
实现类上
List有三个实现类,一个是ArrayList(其特点是查询快,增删慢的一个可变数组);另一个是LinkedList(其特点是查询慢,增删快的一个链表);最后是Vector(其特点是线程安全,但是效率低下,查询增删都比较慢的一个可变数组)
Set有三个实现类,一个是Hashset(底层是HashMap实现,无序不可重复,使用时需要重写equals和hashcode方法),另一个是LinkedHashSet(继承HashSet,同时又基于LinkedHashMap来进行实现,底层使用的是LinkedHashMap),TreeSet(SortedSet接口实现类,可保证集合元素处于有序的状态,底层使用红黑树结构存储,查询速度比List快)
红黑树参考博客:https://www.cnblogs.com/yangecnu/p/Introduce-Red-Black-Tree.html
Map有五个接口实现类,一个是最常用的HashMap(支持null值和null键,key要重写两个方法,且key值是个set集合无序不可重复,value只需要重写equals方法,且value是个collection集合无序但是可重复),另一个是LinkedHashMap(保存记录了插入顺序的双向链表),还有一个是HashTable(是线程安全的,但是效率低,并且不允许有null值的存在),还有TreeMap(SortMap接口实现类,和TreeSet一样,底层都是红黑树结构存储数据,可以保证key-value的有序状态(默认为自然排序,根据键值得升序),但是使用之前需要进行排序),还有一个Properties(HashTable的子类,用于处理属性文件,其key和value都为字符串类型)
-
HashMap和HashTable的区别
看上面的就够了,上面的写的足够详细了,或者看作者写的,懒得码字了…
-
数组和链表分别比较适合于什么场景?为什么?
数组适用于在数据比较少,并且对于存放的数据大都做的是查找工作的情况下,
原因:数组是在内存中连续存储的(因此数据在数组中的内存地址就是连续的,在查找起来就比较快),但是他一经创建,申请出的内存大小便是确定的,在进行数据的增加操作时,就时常会发生数组越界异常,使操作变得异常繁琐,又会在进行删除操作时,因为空值而浪费内存空间,所以在进行增删插入时想要避免这些问题所要做的工作就相对繁琐,操作时效率比较低。链表适用于在对数据的量无法估计的时候,并且对于数据的操作多是一些增删插入的操作。
原因:链表是在内存中离散存储的(因此他在执行查找操作时的效率较低),但由于是动态申请内存空间,形成链表,所以只有在需用的时候才会向JVM申请内存空间,每个数据申请的内存空间可以在内存的任意位置,而后再通过元素的指针来使这些数据关联起来,根据需求来动态地执行插入增删操作(效率较高) -
Java中ArrayList和LinkedList的区别?
-
List a=new ArrayList()和ArrayList a =new ArrayList()的区别?
这就是一种多态的体现,区别就是前者是一个List集合,是父类,所以他无法使用ArrayList,是子类,中所特有的方法。
-
要对集合更新操作时,ArrayList 和LinkedList哪个更适合?
参考数组和链表的特点回答,因为二者的根本就分别是他们。
更新属有查询,有删除,有插入,有增加,因此要根据不同情况下做不同的回答。 -
请用两个队列模拟堆栈结构。
队列是先进先出,而堆栈属于先进后出。
本篇博客的最后附上两张collection和Map的概览图