Java常见面试题整理【3】

来源:互联网 发布:cms开源系统 编辑:程序博客网 时间:2024/06/16 12:42

15.Java集合类框架的基本接口有哪些?

总共有两大接口:Collection 和Map

(1).collection和map集合位于java.util包内,是一个接口,set,list接口都继承于collection接口。
collection接口定义了存取一组对象的方法,其子接口set,list分别定义了存取方式:
set中无顺序且不可重复,
list中有顺序,可重复,是线程不同步的,即线程不安全。
hashset类继承于set接口,linkedlist和arraylist类继承于list接口
linkedlist类中元素组织方式是链表形式,便于插入删除,不便于查找
arraylist类中元素组织方式是以数组形式,便于查找,不便于插入删除
需要注意的是:collection,set,list都是接口,linkedlist,arraylist,hashset都是类。
(2).Map:可以把键(key)映射到值(value)的对象,键不能重复。hashmap和treemap类都是继承于map接口,hashmap是线程不安全的。

java.util.Collection [I]

|—java.util.List [I]    |—java.util.ArrayList [C]    |—java.util.LinkedList [C]    |—java.util.Vector [C]        |—java.util.Stack [C]|—java.util.Set [I]    |—java.util.HashSet [C]    |—java.util.SortedSet [I]        |—java.util.TreeSet [C]

java.util.Map [I]

|—java.util.SortedMap [I]    |—java.util.TreeMap [C]|—java.util.Hashtable [C]|—java.util.HashMap [C]    |—java.util.LinkedHashMap [C]|—java.util.WeakHashMap [C]

16.什么是迭代器(Iterator)?

Iterator接口提供了很多对集合元素进行迭代的方法。每一个集合类都包含了可以返回迭代器实例的迭代方法。迭代器可以在迭代的过程中删除底层集合的元素,但是不可以直接调用集合的remove(Object Obj)删除,可以通过迭代器的remove()方法删除。
迭代器有如下基本用法:
1.使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。
2.使用next()获得序列中的下一个元素。
3. 使用hasNext()检查序列中是否还有元素。
4. 使用remove()将迭代器新返回的元素删除。
    Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。
使用Iterator的简单例子import  java.util.*;public class  TestIterator {public   static   void  main(String[] args) {List list=new  ArrayList();Map map=new  HashMap();for ( int  i= 0 ;i< 10 ;i++){list.add(new  String( "list" +i) );map.put(i, new  String( "map" +i));}Iterator iterList= list.iterator();//List接口实现了Iterable接口while (iterList.hasNext()){String strList=(String)iterList.next();System.out.println(strList.toString());}Iterator iterMap=map.entrySet().iterator();while (iterMap.hasNext()){Map.Entry strMap=(Map.Entry)iterMap.next();System.out.println(strMap.getValue());}

17.Iterator和ListIterator的区别是什么?

Iterator可用来遍历Set和List集合,但是ListIterator只能用来遍历List。Iterator对集合只能是前向遍历,ListIterator既可以前向也可以后向。
ListIterator实现了Iterator接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等。

18.快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?

一:快速失败(fail—fast)

 在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出Concurrent Modification Exception。

 原理:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变modCount的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出异常,终止遍历。

注意:这里异常的抛出条件是检测到 modCount!=expectedmodCount 这个条件。如果集合发生变化时修改modCount值刚好又设置为了expectedmodCount值,则异常不会抛出。因此,不能依赖于这个异常是否抛出而进行并发操作的编程,这个异常只建议用于检测并发修改的bug。

场景:java.util包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程中被修改)。

二:安全失败(fail—safe)

采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。

原理:由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发Concurrent Modification Exception

缺点:基于拷贝内容的优点是避免了Concurrent Modification Exception,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。

 场景:java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改。

19.Java中的HashMap的工作原理是什么?

Java中的HashMap是以键值对(key-value)的形式存储元素的。HashMap需要一个hash函数,它使用hashCode()和equals()方法来向集合/从集合添加和检索元素。当调用put()方法的时候,HashMap会计算key的hash值,然后把键值对存储在集合中合适的索引上。如果key已经存在了,value会被更新成新值。HashMap的一些重要的特性是它的容量(capacity),负载因子(load factor)和扩容极限(threshold resizing)。
详解见:http://blog.csdn.net/m0_37955444/article/details/78770281

20.hashCode()和equals()方法的重要性体现在什么地方?

Java中的HashMap使用hashCode()和equals()方法来确定键值对的索引,当根据键获取值的时候也会用到这两个方法。如果没有正确的实现这两个方法,两个不同的键可能会有相同的hash值,因此,可能会被集合认为是相等的。而且,这两个方法也用来发现重复元素。所以这两个方法的实现对HashMap的精确性和正确性是至关重要的。

HashMap的很多函数要基于equal()函数和hashCode()函数。hashCode()用来定位要存放的位置,equal()用来判断是否相等。  那么,相等的概念是什么?  Object版本的equal只是简单地判断是不是同一个实例。但是有的时候,我们想要的的是逻辑上的相等。比如有一个学生类student,有一个属性studentID,只要studentID相等,不是同一个实例我们也认为是同一学生。当我们认为判定equals的相等应该是逻辑上的相等而不是只是判断是不是内存中的同一个东西的时候,就需要重写equal()。而涉及到HashMap的时候,重写了equals(),就需要重写hashCode()

我们总结一下几条基本原则  1. 同一个对象(没有发生过修改)无论何时调用hashCode()得到的返回值必须一样。  如果一个key对象在put的时候调用hashCode()决定了存放的位置,而在get的时候调用hashCode()得到了不一样的返回值,这个值映射到了一个和原来不一样的地方,那么肯定就找不到原来那个键值对了。   2. hashCode()的返回值相等的对象不一定相等,通过hashCode()和equals()必须能唯一确定一个对象  不相等的对象的hashCode()的结果可以相等。hashCode()在注意关注碰撞问题的时候,也要关注生成速度问题,完美的hash不太现实 3. 一旦重写了equals()函数(重写equals的时候还要注意要满足自反性、对称性、传递性、一致性),就必须重写hashCode()函数。而且hashCode()的生成哈希值的依据应该是equals()中用来比较是否相等的字段  如果两个由equals()规定相等的对象生成的hashCode不等,对于hashMap来说,他们很可能分别映射到不同位置,没有调用equals()比较是否相等的机会,两个实际上相等的对象可能被插入不同位置,出现错误。其他一些基于哈希方法的集合类可能也会有这个问题。

21.HashMap和Hashtable有什么区别?

1、HashMap是非线程安全的,HashTable是线程安全的。

2、HashMap的键和值都允许有null值存在,而HashTable则不行。

3、因为线程安全的问题,HashMap效率比HashTable的要高。
4、Hashtable是同步的,而HashMap不是。因此,HashMap更适合于单线程环境,而Hashtable适合于多线程环境。
一般现在不建议用HashTable, ①是HashTable是遗留类,内部实现很多没优化和冗余。②即使在多线程环境下,现在也有同步的ConcurrentHashMap替代,没有必要因为是多线程而用HashTable。
补充几点: 1.扩容的参数不一样,HashMap要保证每次扩容后是2的次方倍,Hashtable是扩大一倍。 2.散列计算不同。HashMap因为容量是2的次方倍,所以使用减1与(具体百度,手机不好解释)的散列方式而非取余,优化了散列速度。Hashtable我记得是直接取余。

22.数组(Array)和列表(ArrayList)有什么区别?什么时候应该使用Array而不是ArrayList?

ArrayList可以算是Array的加强版,(对array有所取舍的加强)。

存储内容比较:

  • Array数组可以包含基本类型和对象类型,
  • ArrayList却只能包含对象类型。
但是需要注意的是:Array数组在存放的时候一定是同种类型的元素。ArrayList就不一定了,因为ArrayList可以存储Object。
    空间大小比较:        
  • Array它的空间大小是固定的,空间不够时也不能再次申请,所以需要事前确定合适的空间大小。
  • ArrayList的空间是动态增长的,如果空间不够,它会创建一个空间比原空间大一倍的新数组,然后将所有元素复制到新数组中,接着抛弃旧数组。而且,每次添加新的元素的时候都会检查内部数组的空间是否足够。(比较麻烦的地方)。

方法上的比较: ArrayList作为Array的增强版,当然是在方法上比Array更多样化,比如添加全部addAll()、删除全部removeAll()、返回迭代器iterator()等。

适用场景: 如果想要保存一些在整个程序运行期间都会存在而且不变的数据,我们可以将它们放进一个全局数组里,但是如果我们单纯只是想要以数组的形式保存数据,而不对数据进行增加等操作,只是方便我们进行查找的话,那么,我们就选择ArrayList。而且还有一个地方是必须知道的,就是如果我们需要对元素进行频繁的移动或删除,或者是处理的是超大量的数据,那么,使用ArrayList就真的不是一个好的选择,因为它的效率很低,使用数组进行这样的动作就很麻烦,那么,我们可以考虑选择LinkedList。


23.ArrayList和LinkedList有什么区别?

1)因为Array是基于索引(index)的数据结构,它使用索引在数组中搜索和读取数据是很快的。Array获取数据的时间复杂度O(1),但是要删除数据却是开销很大的,因为这需要重排数组中的所有数据。

2)相对于 ArrayListLinkedList插入是更快的。因为LinkedList不像ArrayList一样,不需要改变数组的大小,也不需要在数组装满的时候要将所有的数据重新装入一个新的数组,这是ArrayList最坏的一种情况,时间复杂度是O(n)LinkedList中插入或删除的时间复杂度仅为O(1)ArrayList在插入数据时还需要更新索引(除了插入数组的尾部)。

3)类似于插入数据,删除数据时,LinkedList也优于ArrayList

4) LinkedList需要更多的内存,因为ArrayList的每个索引的位置是实际的数据,而LinkedList中的每个节点中存储的是实际的数据和前后节点的位置(一个LinkedList实例存储了两个值:Node<E> firstNode<E>last分别表示链表的其实节点和尾节点,每个Node实例存储了三个值:E item,Node next,Node pre)

什么场景下更适宜使用LinkedList,而不用ArrayList

1)你的应用不会随机访问数据。因为如果你需要LinkedList中的第n个元素的时候,你需要从第一个元素顺序数到第n个数据,然后读取数据。

2)你的应用更多的插入和删除元素,更少的读取数据。因为插入和删除元素不涉及重排数据,所以它要比ArrayList要快。

以上就是关于ArrayList和LinkedList的差别。你需要一个不同步的基于索引的数据访问时,请尽量使用ArrayList。ArrayList很快,也很容易使用。但是要记得要给定一个合适的初始大小,尽可能的减少更改数组的大小


24.Comparable和Comparator接口是干什么的?列出它们的区别。

    Comparable & Comparator 都是用来实现集合中元素的比较、排序的,只是 Comparable是在集合内部定义的方法实现的排序,Comparator 是在集合外部实现的排序,所以,如想实现排序,就需要在集合外定义 Comparator接口的方法或在集合内实现 Comparable 接口的方法。 Comparator位于包java.util下,而Comparable位于包java.lang下 Comparable 是一个对象本身就已经支持自比较所需要实现的接口(如 String、Integer自己就可以完成比较大小操作,已经实现了Comparable接口)自定义的类要在加入list容器中后能够排序,可以实现Comparable接口,在用Collections类的sort方法排序时,如果不指定Comparator,那么就以自然顺序排序,这里的自然顺序就是实现Comparable接口设定的排序方式。 而 Comparator是一个专用的比较器,当这个对象不支持自比较或者自比较函数不能满足你的要求时,你可以写一个比较器来完成两个对象之间大小的比较。可以说一个是自已完成比较,一个是外部程序实现比较的差别而已。 用 Comparator 是策略模式(strategy designpattern),就是不改变对象自身,而用一个策略对象(strategy object)来改变它的行为。比如:你想对整数采用绝对值大小来排序,Integer 是不符合要求的,你不需要去修改 Integer类(实际上你也不能这么做)去改变它的排序行为,只要使用一个实现了 Comparator 接口的对象来实现控制它的排序就行了。 

25. 什么是Java优先级队列(Priority Queue)?

PriorityQueue是个基于优先级堆的极大优先级队列。
此队列按照在构造时所指定的顺序对元素排序,既可以根据元素的自然顺序来指定排序(参阅 Comparable),
也可以根据 Comparator 来指定,这取决于使用哪种构造方法。优先级队列不允许 null 元素。
依靠自然排序的优先级队列还不允许插入不可比较的对象(这样做可能导致 ClassCastException)
队列检索操作 poll、remove、peek 和 element 访问处于队列头的元素。
优先级队列是无界的,但是有一个内部容量,控制着用于存储队列元素的数组的大小。
它总是至少与队列的大小相同。随着不断向优先级队列添加元素,其容量会自动增加。无需指定容量增加策略的细节。
注意1:该队列是用数组实现,但是数组大小可以动态增加,容量无限。
注意2:此实现不是同步的。不是线程安全的。如果多个线程中的任意线程从结构上修改了列表,则这些线程不应同时访问riorityQueue 实例,这时请使用线程安全的PriorityBlockingQueue 类。
注意3:不允许使用 null 元素。
注意4:此实现为插入方法(offer、poll、remove()和add 方法)提供O(log(n))时间;为 remove(Object) 和 contains(Object) 方法提供线性时间;为检索方法(peek、element 和 size)提供固定时间。
注意5:方法iterator()中提供的迭代器并不保证以有序的方式遍历优先级队列中的元素。至于原因可参考下面关于PriorityQueue的内部实现,如果需要按顺序遍历,请考虑使用 Arrays.sort(pq.toArray())。
注意6:可以在构造函数中指定如何排序。
如:
PriorityQueue()使用默认的初始容量(11)创建一个 PriorityQueue,并根据其自然顺序来排序其元素(使用 Comparable)。
PriorityQueue(int initialCapacity)使用指定的初始容量创建一个 PriorityQueue,并根据其自然顺序来排序其元素(使用 Comparable)。
PriorityQueue(int initialCapacity, Comparator comparator)使用指定的初始容量创建一个 PriorityQueue,并根据指定的比较器comparator来排序其元素。
注意7:此类及其迭代器实现了 Collection 和 Iterator 接口的所有可选 方法。
PriorityQueue的内部实现PriorityQueue对元素采用的是堆排序,头是按指定排序方式的最小元素。堆排序只能保证根是最大(最小),整个堆并不是有序的。
方法iterator()中提供的迭代器可能只是对整个数组的依次遍历。也就只能保证数组的第一个元素是最小的。
26.Enumeration接口和Iterator接口的区别有哪些?
Enumeration速度是Iterator的2倍,同时占用更少的内存。但是,Iterator远远比Enumeration安全,因为其他线程不能够修改正在被iterator遍历的集合里面的对象。同时,Iterator允许调用者删除底层集合里面的元素,这对Enumeration来说是不可能的。
package java.util;
public interface Enumeration<E> {
    boolean hasMoreElements();
    E nextElement();
}
public interface Iterator<E> {
    boolean hasNext();
    E next();
    void remove();
}
(01)函数接口不同
Enumeration只有2个函数接口。通过Enumeration,我们只能读取集合的数据,而不能对数据进行修改。 Iterator只有3个函数接口。Iterator除了能读取集合的数据之外,也能数据进行删除操作。

(02) Iterator支持fail-fast机制,而Enumeration不支持Enumeration是JDK1.0添加的接口。使用到它的函数包括Vector、Hashtable等类,这些类都是JDK 1.0中加入的,Enumeration存在的目的就是为它们提供遍历接口。Enumeration本身并没有支持同步,而在Vector、Hashtable实现Enumeration时,添加了同步。 而Iterator 是JDK 1.2才添加的接口,它也是为了HashMap、ArrayList等集合提供遍历接口。Iterator是支持fail-fast机制的:当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。

27.HashSet和TreeSet有什么区别?

1、HashSet对速度进行了优化,提供了最快的查找速度,无特殊说明一般默认是用这个Set
放到HashSet中的元素要保证唯一,应该重写hashCode方法和equals方法,但是不能保证元素有序
底层实现是哈希结构
2、TreeSet底层实现是红黑树(自平衡二叉树),不但能保证元素唯一,还能元素保证有序,
存放到TreeSet中的元素应该实现Comparable接口,重写compareTo方法,否则会抛出ClassCastException
按照该方法指定的规则维持元素的顺序
3、LinkedHashSet,底层实现是哈希表和链表,保持了HashSet的速度,还能按照插入元素的顺序维持元素顺序。
阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 三秒钟去除铜锈 铜号的拼音 以铜为镜 氧化铜什么颜色 十二铜表法 铜绿菌 氧化亚铜 铜的拼音 国际铜价格 铜英文 现货铜交易所 期货 铜 现货铜怎么开户 贵金属现货铜 铸铜厂家 熔铜炉价格 铜抗氧化剂 铜套生产厂家 环烷酸铜 铜包铝线价格 铜狮子厂家 古代铜锁价格 溴化亚铜 电缆铜价格 铜件加工厂 回收铜多少钱一斤 铜狮子铸造厂 铜母线价格 擦铜膏 石墨铜套厂家 铜腐蚀 云南铜业 长江铜业网 长江铜业 铜业英才网 铜业公司 铜业招聘 创佳铜业 云南铜业股票 江西铜业股票 600362江西铜业