java面试题之集合篇

List(有序、可重复)

List里存放的对象是有序的,同时也是可以重复的,List关注的是索引,拥有一系列和索引相关的方法,查询速度快。因为往list集合里插入或删除数据时,会伴随着后面数据的移动,所有插入删除数据速度慢。

实现了List接口的类

ArrayList、LinkedList、Vector

Set(无序、不能重复)

Set里存放的对象是无序,不能重复的,集合中的对象不按特定的方式排序,只是简单地把对象加入集合中。

实现了Set接口的类

HashSet、LinkedHashSet、TreeSet

Java中Set与List有什么不同?

Set是一个不允许重复元素存在的集合

Set没有索引

Set仅仅允许一个null值

List有索引

List允许N个null值

List可以按插入顺序显示

Arraylist与Vector的区别?

Vector 在Java的第一个版本就引入了,也就是说Vector是一个合法规范的类。

ArrayList在Java1.2版本引入的,是Java 集合框架的组成部分。

Vector是同步的,ArrayList是不同步的。

为什么Map接口不继承Collection 接口?

Set是无序集合,并且不允许重复的元素。

List是有序的集合,并且允许重复的元素。

而Map是键值对,它被视为是键的set和值的set的组合。

Map被设计为键值对的集合,所以不需要继承Collection 接口。

List和Map的实现方式以及存储方式

List使用可变长数组实现方式

Map使用数组加链表的数据结构实现

Comparable和Comparator的不同之处?

Comparable和Comparator接口被用来对对象集合或者数组进行排序。

Comparable接口被用来提供对象的自然排序,我们可以使用它来提供基于单个逻辑的排序。

Comparator接口被用来提供不同的排序算法,我们可以选择需要使用的Comparator来对给定的对象集合进行排序。

如何对Object的list排序?

对objects数组进行排序,我们可以用Arrays.sort()方法

如果要对objects的集合进行排序,需要使用Collections.sort()方法。

fail-fast与fail-safe之间的区别?

fail-fast快速地报告任何的failure。无论何时任何一个问题都会引发 fail-fast系统fails。

在Java fail fast 迭代器中,迭代objects集合有时会出现并发修改异常,出现这种情况有2个原因。如果一个线程正在迭代一个集合,而另一个线程同时试图修改这个集合,在调用remove()方法后,如何我们还试图去修改集合object

fail-safe任何对集合结构的修改都会在一个复制的集合上进行修改,因此不会抛出ConcurrentModificationException

Iterator、ListIterator 和 Enumeration的区别?

Enumeration接口在Java1.2版本开始有,所以Enumeration是合法规范的接口。

Enumeration使用elements()方法。

Iterator对所有Java集合类都有实现

Iterator使用iterator方法

Iterator只能往一个方向前进

ListIterator仅仅对List类型的类实现了

ListIterator使用listIterator()方法

为什么Collection不能继承Cloneable和Serializable?

Collection表示一个集合,包含了一组对象。如何存储和维护这些对象是由具体实现来决定的。因为集合的具体形式多种多样,例如list允许重复,set则不允许。而克隆(clone)和序列化(serializable)只对于具体的实体,对象有意义,你不能说去把一个接口,抽象类克隆,序列化甚至反序列化。所以具体的collection实现类是否可以克隆,是否可以序列化应该由其自身决定,而不能由其超类强行赋予。

如果collection继承了clone和serializable,那么所有的集合实现都会实现这两个接口,而如果某个实现它不需要被克隆,甚至不允许它序列化(序列化有风险),那么就与collection矛盾了。

能否使用任何类作为Map的key?

可以使用任何类作为Map的key,然而在使用它们之前,需要考虑以下几点:

(1)如果类重写了equals()方法,它也应该重写hashCode()方法。

(2)类的所有实例需要遵循与equals()和hashCode()相关的规则。请参考之前提到的这些规则。

(3)如果一个类没有使用equals(),你不应该在hashCode()中使用它。

(4)用户自定义key类的最佳实践是使之为不可变的,这样,hashCode()值可以被缓存起来,拥有更好的性能。不可变的类也可以确保hashCode()和equals()在未来不会改变,这样就会解决与可变相关的问题了。

是否可以往TreeSet或者HashSet中添加null元素?

可以往HashSet中添加一个null

TreeSet也允许一个null值

ArrayMap和HashMap的对比

ArrayMap是一个< key,value > 映射的数据结构,它设计上更多的是考虑内存的优化,内部是使用两个数组进行数据存储,一个数组记录key的hash值,另外一个数组记录Value值,它和SparseArray一样,也会对key使用二分法进行从小到大排序,在添加、删除、查找数据的时候都是先使用二分查找法得到相应的index,然后通过index来进行添加、查找、删除等操作。

(1)存储方式不同

HashMap内部有一个HashMapEntry<K, V>[]对象,每一个键值对都存储在这个对象里,当使用put方法添加键值对时,就会new一个HashMapEntry对象。

(2)添加数据时扩容时的处理不一样。HashMap重新创建对象,开销很大。ArrayMap用的是copy数据,所以效率相对要高。

(3)ArrayMap提供了数组收缩的功能,在clear或remove后,会重新收缩数组,释放空间。

(4) ArrayMap采用二分法查找

HashMap和HashTable的区别

(1)HashMap允许key和value为null,而HashTable不允许。

(2)HashTable是同步的,而HashMap不是。所以HashMap适合单线程环境,HashTable适合多线程环境。

(3)在Java1.4中引入了LinkedHashMap,HashMap的一个子类,假如你想要遍历顺序,你很容易从HashMap转向LinkedHashMap,但是HashTable不是这样的,它的顺序是不可预知的。

(4)HashMap提供对key的Set进行遍历,因此它是fail-fast的,但HashTable提供对key的Enumeration进行遍历,它不支持fail-fast。

(5)HashTable被认为是个遗留的类,如果你寻求在迭代的时候修改Map,你应该使用CocurrentHashMap。

HashMap与HashSet的区别

(1)HashMap实现了Map接口HashSet实现了Set接口。

(2)HashMap储存键值对,HashSet仅仅存储对象使用put()方法将元素放入map中,使用add()方法将元素放入set中。

(3)HashMap中使用键对象来计算hashcode值 HashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性,如果两个对象不同的话,那么返回false。

(4)HashMap比较快,因为是使用唯一的键来获取对象 HashSet较HashMap来说比较慢

HashSet怎么判断集合元素重复?

HashSet不能添加重复的元素,当调用add(Object)方法时候,首先会调用Object的hashCode方法判hashCode是否已经存在,如不存在则直接插入元素;如果已存在则调用Object对象的equals方法判断是否返回true,如果为true,则说明元素已经存在,如为false,则插入元素。

ArrayList和LinkedList的区别,以及应用场景

(1)ArrayList是由Array所支持的基于一个索引的数据结构,所以它提供对元素的随机访问,复杂度为O(1),但LinkedList存储一系列的节点数据,每个节点都与前一个和下一个节点相连接。所以,尽管有使用索引获取元素的方法,内部实现是从起始点开始遍历,遍历到索引的节点然后返回元素,时间复杂度为O(n),比ArrayList要慢。

(2)与ArrayList相比,在LinkedList中插入、添加和删除一个元素会更快,因为在一个元素被插入到中间的时候,不会涉及改变数组的大小,或更新索引。

(3)LinkedList比ArrayList消耗更多的内存,因为LinkedList中的每个节点存储了前后节点的引用。

哪些集合类是线程安全的?

Vector、HashTable、Properties和Stack是同步类,所以它们是线程安全的,可以在多线程环境下使用。Java1.5并发API包括一些集合类,允许迭代时修改,因为它们都工作在集合的克隆上,所以它们在多线程环境中是安全的。

如何保证一个集合线程安全?

使用Collections.synchronizedList(list)) 方法,可以保证list类是线程安全的。

使用java.util.Collections.synchronizedSet()方法可以保证set类是线程安全的。

并发集合类是什么?

Java1.5并发包(java.util.concurrent)包含线程安全集合类,允许在迭代时修改集合。迭代器被设计为fail-fast的,会抛出ConcurrentModificationException。一部分类为:CopyOnWriteArrayList、 ConcurrentHashMap、CopyOnWriteArraySet。

BlockingQueue是什么?

Java.util.concurrent.BlockingQueue是一个队列,在进行检索或移除一个元素的时候,它会等待队列变为非空;当在添加一个元素时,它会等待队列中的可用空间。BlockingQueue接口是Java集合框架的一部分,主要用于实现生产者-消费者模式。我们不需要担心等待生产者有可用的空间,或消费者有可用的对象,因为它都在BlockingQueue的实现类中被处理了。Java提供了集中BlockingQueue的实现,比如ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue,、SynchronousQueue等。

数组和链表的区别

数组结构 :

数组结构在通过索引进行查询数据时效率比较高,而对于数组插入和删除操作,则效率会比较低,在第一个位置进行插入数据,其余数据就需要依次向后移动,而第一个数据进行删除,则需要所有数据全部向前移,这样的话,就会推出第二种结构,链表结构。

链表:

链表是由一系列节点组成的,每个节点都会有一个链点,这就是next链,而next链则会执行下一个node的引用,所以我们在插入或者删除的时候,需要该表链表next链的指向地址即可,每个节点不需要内存进行连续存储,这样会减小删除和插入的线性开销。

什么是深拷贝和浅拷贝

浅拷贝:

浅拷贝又叫浅复制,将对象中的所有字段复制到新的对象(副本)中。其中,值类型字段(java中8中原始类型)的值被复制到副本中后,在副本中的修改不会影响到源对象对应的值。而引用类型的字段被复制到副本中的还是引用类型的引用,而不是引用的对象,在副本中对引用类型的字段值做修改会影响到源对象本身。

深拷贝:

深拷贝将对象中的所有字段复制到新的对象中。不过,无论是对象的值类型字段,还是引用类型字段,都会被重新创建并赋值,对于副本的修改,不会影响到源对象本身。

好了,本篇文章就分享到这里了。有兴趣的新手伙伴们可以关注收藏起来,以后需要的时候可以多看看。如果有正在学java的程序员,可来我们的java技术学习扣qun哦:59789,1510里面免费送java的视频系统教程!

猜你喜欢

转载自blog.csdn.net/weixin_43660525/article/details/85847318