Java基础面试题
ArrayList 和 Vector 的区别。
ArrayList是线程不安全的,Vector是线程安全的。Veator中的方法是同步方法(Synchronized修饰),因此ArrayList执行效率相对较高。
说说 ArrayList,Vector, LinkedList 的存储性能和特性。
ArrayList和Vector底层使用数组进行数据存储,查询快,增删慢,区别在于ArrayList是线程不安全的。当需要扩容时,Vector增加一倍,而ArrayList增加原来的一半。
LinkedList底层基于链表进行数据存储,查询慢,但增删快,主要适用增删较多的场景。
快速失败 (fail-fast) 和安全失败 (fail-safe) 的区别是什么?
fail-fast和fail-safe的区别:
fail-safe允许在遍历的过程中对容器中的数据进行修改,而fail-fast则不允许。
fail-fast ( 快速失败 )
fail-fast:直接在容器上进行遍历,在遍历过程中,一旦发现容器中的数据被修改了,会立刻抛出ConcurrentModificationException异常导致遍历失败。java.util包下的集合类都是快速失败机制的, 常见的的使用fail-fast方式遍历的容器有HashMap和ArrayList等。
在使用迭代器遍历一个集合对象时,比如增强for,如果遍历过程中对集合对象的内容进行了修改(增删改),会抛出ConcurrentModificationException 异常.
fail-fast的出现场景
在我们常见的java集合中就可能出现fail-fast机制,比如ArrayList,HashMap。在多线程和单线程环境下都有可能出现快速失败。
fail-safe ( 安全失败 )
fail-safe:这种遍历基于容器的一个克隆。因此,对容器内容的修改不影响遍历。java.util.concurrent包下的容器都是安全失败的,可以在多线程下并发使用,并发修改。常见的的使用fail-safe方式遍历的容器有ConcerrentHashMap和CopyOnWriteArrayList等。
原理:
采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发Concurrent Modification Exception。
缺点:
基于拷贝内容的优点是避免了Concurrent Modification Exception,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。
hashmap 的数据结构。
HashMap基于哈希表的Map接口实现,是以key-value存储形式存在,即主要用来存放键值对。HashMap的实现不是同步的,这意味着它不是线程安全的。它的key、value都可以为null。此外,HashMap中的映射不是有序的。
JDK1.8之前的HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了节解决哈希碰撞(两个对象调用的hashCode方法计算的哈希码值一致导致计算的数组索引值相同)而存在的(“拉链法”解决冲突)。
JDK1.8之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(或者红黑树的边界值,默认为8)并且当前数组的长度大于64时,此时此索引位置上的所有数据改为使用红黑树存储。
数组里面都是key-value的实例,在JDK1.8之前叫做Entry,在JDK1.8之后叫做Node。
HashMap什么时候进行扩容?
当hashmap中的元素个数超过数组大小*loadFactor时,就会进行数组扩容,loadFactor的默认值为0.75,也就是说,默认情况下,数组大小为16,那么当hashmap中元素个数超过16*0.75=12的时候,就把数组的大小扩展为2*16=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知hashmap中元素的个数,那么预设元素的个数能够有效的提高hashmap的性能
List、Map、Set 三个接口,存取元素时,各有什么特点?
List与Set都是单列元素的集合,它们有一个功共同的父接口Collection。
Set里面不允许有重复的元素,
存元素:add方法有一个boolean的返回值,当集合中没有某个元素,此时add方法可成功加入该元素时,则返回true;当集合含有与某个元素equals相等的元素时,此时add方法无法加入该元素,返回结果为false。
取元素:没法说取第几个,只能以Iterator接口取得所有的元素,再逐一遍历各个元素。
List表示有先后顺序的集合,
存元素:多次调用add(Object)方法时,每次加入的对象按先来后到的顺序排序,也可以插队,即调用add(int index,Object)方法,就可以指定当前对象在集合中的存放位置。
取元素:方法1:Iterator接口取得所有,逐一遍历各个元素
方法2:调用get(index i)来明确说明取第几个。
Map是双列的集合:
存放用put方法:put(obj key,obj value),每次存储时,要存储一对key/value,不能存储重复的key,这个重复的规则也是按equals比较相等。
取元素:用get(Object key)方法根据key获得相应的value。
也可以获得所有的key的集合,还可以获得所有的value的集合,
还可以获得key和value组合成的Map.Entry对象的集合。
Set 里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用 == 还是 equals()? 它们有何区别?
==是判断对象的内存地址,s1==s2,s2的引用的对象跟s1是同一个。
Object类的equals 也是判断对象的内存地址。底层也是用的==。
有一些类复写了equals(),判断的是此对象的具体内容
两个对象值相同 (x.equals(y) == true),但却可有不同的 hash code,这句话对不对?
不对,值相同hashcode一定相同,hashcode相同值不一定相同。
heap 和 stack 有什么区别。
1、heap是堆,stack是栈。
2、stack的空间由操作系统自动分配和释放,heap的空间是手动申请和释放的,heap常用new关键字来分配。
3、stack空间有限,heap的空间是很大的自由区。
Java 集合类框架的基本接口有哪些?
Collection 和Map ,一个元素集合,一个是键值对集合;
其中List和Set接口继承了Collection接口,一个是有序元素集合,一个是无序元素集合;
而ArrayList和 LinkedList 实现了List接口,HashSet实现了Set接口,这几个都比较常用;
HashMap 和HashTable实现了Map接口,并且HashTable是线程安全的,但是HashMap性能更好
Java集合类里最基本的接口有:Collection:单列集合的根接口List:元素有序 可重复ArrayList:类似一个长度可变的数组 。适合查询,不适合增删LinkedList:底层是双向循环链表。适合增删,不适合查询。Set:元素无序,不可重复HashSet:根据对象的哈希值确定元素在集合中的位置TreeSet: 以二叉树的方式存储元素,实现了对集合中的元素排序Map:双列集合的根接口,用于存储具有键(key)、值(value)映射关系的元素。HashMap:用于存储键值映射关系,不能出现重复的键keyTreeMap:用来存储键值映射关系,不能出现重复的键key,所有的键按照二叉树的方式排列
HashSet和TreeSet的区别?
Hashset 的底层是由哈希表实现的,Treeset 底层是由红黑树实现的。 如果需要在Treeset 中插入对象,需要实现Comparable 接口,为其指定比较策略。
HashSet 的底层实现是什么?
HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持。它不保证set 的迭代顺序;特别是它不保证该顺序恒久不变,此类允许使用null元素。
在HashSet中,元素都存到HashMap键值对的Key上面,而Value时有一个统一的值private static final Object PRESENT = new Object();
(定义一个虚拟的Object对象作为HashMap的value,将此对象定义为static final。)
LinkedHashMap 的实现原理?
1. LinkedHashMap概述:
LinkedHashMap是Map接口的哈希表和链接列表实现,具有可预知的迭代顺序。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
LinkedHashMap实现与HashMap的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序。
注意,此实现不是同步的。如果多个线程同时访问链接的哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须保持外部同步。2. LinkedHashMap的实现:
对于LinkedHashMap而言,它继承与HashMap、底层使用哈希表与双向链表来保存所有元素。其基本操作与父类HashMap相似,它通过重写父类相关的方法,来实现自己的链接列表特性。
LinkedHashMap采用的hash算法和HashMap相同,但是它重新定义了数组中保存的元素Entry,该Entry除了保存当前对象的引用外,
还保存了其上一个元素before和下一个元素after的引用,从而在哈希表的基础上又构成了双向链接列表。
为什么集合类没有实现 Cloneable 和 Serializable 接口?
集合的接口
List
中并没有定义继承序列化和克隆接口, 并且其他的集合接口也都没有实现。克隆(cloning)或者是序列化(serialization)的语义和含义是跟具体的实现相关的。因此,应该由集合类的具体实现来决定如何被克隆或者是序列化。
克隆分为深拷贝和浅拷贝, 具体的拷贝实现需要用户自己根据对象的特性来实现, 集合类又是一个容器, 会装载不同的对象(各种各样的). 不可能每个对象都实现克隆,
序列化也是同样的道理。
虽然List没有继承序列化和克隆接口,但是它的子类ArrayList都继承了这两个接口。 并且根据自己的特性对这两个接口的实现都进行了自己的优化。
什么是迭代器 (Iterator)?
Iterator接口提供了很多对集合元素进行迭代的方法。
每一个集合类都包括了可以返回迭代器实例的迭代方法。
迭代器可以在迭代过程中删除底层集合的元素,但是不可以直接调用集合的remove(Object obj)删除,可以通过迭代器的remove()方法删除
Iterator 和 ListIterator 的区别是什么?
1、遍历
使用Iterator,可以遍历所有集合,如Map,List,Set;但只能在向前方向上遍历集合中的元素。
使用ListIterator,只能遍历List实现的对象,但可以向前和向后遍历集合中的元素。
2、添加元素
Iterator无法向集合中添加元素;而,ListIteror可以向集合添加元素。
3、修改元素
Iterator无法修改集合中的元素;而,ListIterator可以使用set()修改集合中的元素。
4、索引
Iterator无法获取集合中元素的索引;而,使用ListIterator,可以获取集合中元素的索引。
数组 (Array) 和列表 (ArrayList) 有什么区别?什么时候应该使用 Array 而不是 ArrayList?
区别:
Array可以包含基本类型和对象类型,ArrayList只能包含对象类型。
Array大小是固定的,ArrayList的大小是动态变化的。
ArrayList提供了更多的方法和特性,比如:addAll(),removeAll(),iterator()等等。
适用:
如果想要保存一些在整个程序运行期间都会存在而且不变的数据,我们可以将它们放进一个全局数组里,
但是如果我们单纯只是想要以数组的形式保存数据,而不对数据进行增加等操作,只是方便我们进行查找的话,那么,我们就选择ArrayList。
而且还有一个地方是必须知道的,就是如果我们需要对元素进行频繁的移动或删除,或者是处理的是超大量的数据,
那么,使用ArrayList就真的不是一个好的选择,因为它的效率很低,使用数组进行这样的动作就很麻烦,那么,我们可以考虑选择LinkedList。
Comparable 和 Comparator 接口是干什么的?列出它们的区别
Comparable & Comparator 都是用来实现集合中元素的比较、排序的,只是 Comparable 是在集合内部定义的方法实现的排序,
Comparator 是在集合外部实现的排序,所以,如想实现排序,就需要在集合外定义 Comparator 接口的方法或在集合内实现 Comparable 接口的方法。
两种方式,各有各的特点:使用Comparable方式比较时,我们将比较的规则写入了比较的类型中,其特点是高内聚。
但如果哪天这个规则需要修改,那么我们必须修改这个类型的源代码。如果使用Comparator方式比较,那么我们不需要修改比较的类,
其特点是易维护,但需要自定义一个比较器,后续比较规则的修改,仅仅是改这个比较器中的代码即可。
简而言之:Comparable 是和类绑定一起,Comparator 只是一个比较器。
Collection 和 Collections 的区别。
collection是集合接口,collections是集合工具类。