java程序员面试(七)

容器

  1. Java Collections框架是什么?

    • Java Collections 框架中包含了大量集合接口以及这些接口的实现类和操作它们的算法。
    • 具体而言,主要提供了List(列表)、Queue(队列)、Set(集合)、Stack(栈)、和Map(隐射表)等数据结构。
    • 其中List、Queue、Set、Stack都继承自Collection接口。
    • Collection接口是整个集合框架的基础,它里面存储一组对象,表示不同类型的Collection,它的作用只是提供维护一组对象的接口而已。
    • Set 表示数学意义上的集合概念。 其最主要的特点是元素不能重复,因此存入Set的每个元素都必须定义equals()方法来确保对象的唯一性。该接口有两个实现类:HashSet和TreeSet。其中TreeSet实现了SortedSet接口,因此TreeSet容器中的元素是有序的。
    • List 又称为有序的Collection。它按对象进入的顺序保存对象,所以它能对列表中的每个元素的插入和删除位置进行精确的控制。同时,它可以保存重复的对象。LinkedList、ArrayList和Vector都实现了List接口。
    • Map提供了一个从键映射到值的数据结构。它可以保存键值对,其中值可以重复,但键是唯一的,不能重复。Java类库中有多个实现接口的类:HashMap、TreeMap、LInkedHashMap、WeakHashMap、IdentityHashMap。虽然他们都实现了相同的接口,但执行效率却不是完全相同的。具体而言,HashMap是基于散列表实现的,采用对象的HashCode可以进行快速查询。LinkedHashMap采用列表来 维护内部的顺序。TreeMap基于红黑树的数据结构来实现的,内部元素是按需排序的。
  2. 什么是迭代器

    • 迭代器(Iterator)是一个对象,它的工作是遍历并选择序列中的对象,它提供了一种访问一个容器对象中的各个元素,而又不必暴露该对象内部细节的方法。通过迭代器,开发人员不需要了解容器底层的结构,就可以实现对容器的遍历。由于创建迭代器的代价小,因此迭代器通常被称为轻量级的容器。
    • 迭代器使用注意事项:

      • 使用容器的iterator()方法返回一个Iterator,然后通过Iterator的next()方法返回第一个元素。
      • 使用Iterator的hasNext()方法判断容器中是否还有元素,如果有,可以使用next()方法获取下一个元素。
      • 可以通过remove()方法删除迭代器返回的元素。
    • Iterator支持派生的兄弟成员。ListIterator只存在于List中,支持在迭代期间向List中添加或删除元素,并且可以在List中双向滚动。
    • 在使用iterator()方法时,经常会遇到ConcurrentModificationException异常,这通常是由于在使用Iterator遍历容器的同时又对容器做增加或删除操作所导致的,或者由于多线程操作导致。当一个线程使用迭代器遍历容器的同时,另外一个线程对这个容器进行增加或删除操作。
    • 抛出上述异常的主要原因是当调用iterator()方法返回Iterator对象时,把容器中包含对象的个数赋值给了一个变量expectedModCount,在调用next()方法时会比较变量expectedModCount与容器中实际对象的个数modCount的值是否相等,若二者不相等,则会抛出ConcurrentModificationException异常。
    • 因此使用Iterator遍历容器的过程中,如果对容器进行增加或删除操作,就会改变容器中对象的数量,从而导致抛出异常。
    • 解决办法如下:在遍历的过程中把需要删除的对象保存到一个集合中,等遍历结束后在调用removeAll()方法来删除,或者使用iter.remove()方法。
    • 引申:Iterator与ListIterator有什么区别?

      • Iterator只能正向遍历集合,适用于获取移除元素。ListIterator继承自Iterator,专门针对List,可以从两个方向来遍历List,同时支持元素的修改。
    • 以上主要介绍了单线程的解决方案,那么多线程访问容器的过程中抛出ConcurrentModificationException异常又该如何解决?

      • 在JDK1.5版本引入了线程安全的容器,比如ConcurrenHashMap和CopyOnWriteArrayList等。可以使用这些线程安全的容器来代替非线程安全的容器。
      • 在使用迭代器遍历容器时对容器的操作放到synchronized代码块中,但是当引用程序并发程度比较高时,这会严重影响程序的性能。
  3. ArrayList、Vector和LinkedList有什么区别?

    • ArrayList、Vector、LinkedList类均在java.util包中,均为可伸缩数组,即可以动态改变长度的数组。
    • ArrayList和Vector都是基于存储元素的Object[] array来实现的,它们会在内存中开辟一块连续的空间来存储,由于数据存储时连续的,因此,它们支持用序号(下标)来访问元素。同时索引数据的速度比较快。但是在插入元素时需要移动容器中的元素,所以对数据的插入操作执行比较慢。
    • ArrayList和Vector都有一个初始化的容量的大小,当里面存储的元素超过这个大小时就需要动态地扩充它们的存储空间。为了提高程序的效率,每次扩充容量,不是简单的扩充一个存储单元,而是一次层架多个存储单元。Vector默认扩种为原来的两倍(可以设置),ArrayList默认扩充为原来的1.5倍(没有提供方法设置)
    • ArrayList与Vector最大的区别就是synchronization的使用,没有ArrayList的方法是同步的,而Vector的绝大多数方法都是直接或间接同步的,所以Vector是线程安全的,ArrayList是线程不安全的。正是由于Vector提供了线程安全的机制,其性能上也要略低于ArrayList。
    • LinkedList是采用双向列表来实现的,对数据的索引需要从列表头开始遍历,因此用于随机访问则效率比较低,但是插入元素时不需要对数据进行移动,因此插入效率较高。同时,LinkedList是非线程安全的容器。
    • 实际使用如何选择:对数据的主要操作为索引或旨在集合的末端增加、删除元素时,用ArrayList或Vector效率比较高;当对数据的操作主要为指定位置的插入或删除操作时,使用LinkedList效率比较高;当在多线程中使用容器时,选用Vector较为安全。
  4. HashMap、Hashtable、TreeMap和WeakHashMap有哪些区别?

    • java为数据结构中的映射定义了一个接口java.util.Map,它包括3个实现类:HashMap、Hashtable和TreeMap。Map是用来存储键值对的数据结构,在数组中通过数组下标来对其内容索引的,而在Map中,则是通过对象来进行索引,用来索引的对象叫做key,其对应的对象叫做value。
    • HashMap是一个最常用的Map,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度。由于HashMap与Hashtable都采用了hash法进行索引,因此二者具有许多相似之处。
    • HashMap是Hashtable的轻量级实现(非线程安全的实现),它们都完成了Map接口,主要区别在于HashMap允许空(null)键值,只允许一条键。 而Hashtable是不允许的。
    • HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey,因为contains方法容易让人引起误解。Hashtable继承自Dictionary类,而HashMap是java1.2引进的Map interface的一个实现。
    • Hashtable的方法是线程安全的,而HashMap不支持线程的同步,所以它不是线程安全的。在多个线程访问Hashtable时,不需要开发人员对它进行同步,而对HashMap,开发人员必须提供额外的同步机制。所以,就效率而言,HashMap可能高于Hashtable。
    • Hashtable使用Enumeration,HashMap使用Iterator.
    • Hashtable和HashMap采用的hash/rehash算法几乎一样,所以性能不会有很大的差距。
    • 在Hashtable中hash数组默认大小是11,增加的方式是old*2+1。在HashMap中,hash数组的默认大小是16,而且一定是2的指数。
    • hash值的使用不同,Hashtable直接使用对象的hashCode.
    • 以上几种类型中,使用最多的是HashMap。HashMap里面存入的键值对在取出时没有固定的顺序,是随机的。一般而言,在Map中插入、删除和定位元素,HashMap是最好的选择。由于TreeMap实现了SortMap接口,能够把它保存的记录根据键排序,因此取出来的是排序后的键值对,如果需要按自然顺序或自定义顺序遍历键,那么TreeMap更好。LinkedHashMap是HashMap的一个子类,如果需要输出的顺序和输入的相同,那么LinkedHashMap可以实现。它还可以按读取顺序来排序。
    • WeakHashMap与HashMap类似,二者不同之处在于WeakHashMap中key采用的是“弱引用”方式,只要WeakHashMap中的key不再被外部引用,它就可以被垃圾回收器回收。而HashMap中key采用的是“强引用的方式”,当HashMap中的key没有外部引用时,只有在这个key从HashMap中删除后,才可以被垃圾回收器回收。
    • 在Hashtable上下文中,同步指的是什么?

      • 同步意味着在一个时间点只能有一个线程可以修改hash表,任何线程再执行Hashtable的更新操作前都需要获取对象锁,其他线程则等待锁的释放。
    • 如何实现HashMap的同步?

      • HashMap可以通过Map m = Collections.synchronizedMap(new HashMap())来达到同步的效果。具体而言,该方法返回一个同步的Map,该Map封装了底层的HashMap的所有方法,使得底层的HashMap即使是在多线程的环境中也是安全的。
  5. 用自定义类型作为Hashtable或HashMap的key需要注意哪些问题?

    • HashMap和Hashtable是用来存放键值对的一种容器,在使用容器是有一个限制:不能用来存储重复的键。也就是说,每个键只能唯一映射一个值,当有重复的键时,不会创建新的映射关系,而会使用先前的键值。
    • HashMap添加元素的操作过程:首先,调用key的hashCode()方法生成一个hash值h1,如果这个h1在HashMap中不存在,那么直接将<key,value>添加到HashMap中;如果这个h1值存在,那么在为h1的key中调用equals方法。如果equals方法返回值为true,说明当前需要添加的key已经存在,那么HashMap会使用新的value覆盖旧的value;如果equals返回为false,说明新增的key在HashMap中不存在,因此会在HashMap中创建新的映射关系。
    • 当新增加的key的hash值已经在HashMap中存在时,就会产生冲突。一般而言,对于不同的key值可能会得到相同的hash值,因此就需要对冲突进行处理。
    • 一般而言,处理冲突的方法有开放地址法、再Hash法、链地址法等。HashMap使用的是链地址法来解决冲突。
  6. Collection和Collections有什么区别?

    • Collection是一个集合接口。它提供了对集合对象进行基本操作的通用接口方法。实现该接口类主要有List和Set,该接口的设计目标是为各种具体的集合提供最大化的统一的操作方式。
    • Collections是针对集合类的一个包装类,它提供了一系列静态方法以实现对各种集合的搜索、排序、线程安全等操作,其中大多数方法都是用来处理线性表。
    • Collections类不能实例化,如同一个工具类,服务于Collection框架。若使用Collections类的方法时,对应的Collection的对象为null,则这些方法都会抛出NullPointerException.

本文链接:

https://heyzen.club/index.php/J/305.html
1 + 8 =
快来做第一个评论的人吧~