面试专题之Java基础:容器

题目

  1. List、Set、Map 是否继承自 Collection 接口?
  2. 阐述 ArrayList、Vector、LinkedList 的存储性能和特性。
  3. Collection 和 Collections 的区别?
  4. List、Map、Set 三个接口存取元素时,各有什么特点?
  5. TreeMap 和 TreeSet 在排序时如何比较元素?Collections 工具类中的 sort()方法如何比较元素?
  6. HashMap 和 HashTable 的区别是什么?
  7. ConcurrentHashMap和HashMap的区别是什么?ConcurrentHashMap为什么线程安全?
  8. HashMap 和 HashSet 的区别?HashSet 是如何检查重复的?
  9. poll() 方法和 remove() 方法的区别?
  10. Java 中 LinkedHashMap 和 PriorityQueue 的区别是什么?
  11. ArrayList 与 LinkedList 的不区别?
  12. 用哪两种方式来实现集合的排序?
  13. Java 中怎么打印数组?
  14. Java 中的 LinkedList 是单向链表还是双向链表?
  15. Java 中的 TreeMap 是采用什么树实现的?
  16. Java 中的 HashSet,内部是如何工作的?
  17. 写一段代码在遍历 ArrayList 时移除一个元素?
  18. 我们能自己写一个容器类,然后使用 for-each 循环码?
  19. ArrayList 和 HashMap 的默认大小是多数?
  20. 说出几点 Java 中使用 Collections 的最佳实践

答案

  1. List、Set、Map 是否继承自 Collection 接口?
    List、Set 是 ,Map 不是。Map 是键值对映射容器,与 List 和 Set 有明显的区别,而 Set 存储的零散的元素且不允许有重复元素(数学中的集合也是如此),List是线性结构的容器,适用于按数值索引访问元素的情形。

  2. 阐述 ArrayList、Vector、LinkedList 的存储性能和特性。
    ArrayList 和 Vector 都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector 中的方法由于添加了 synchronized 修饰,因此 Vector 是线程安全的容器,但性能上较ArrayList 差,因此已经是 Java 中的遗留容器。LinkedList 使用双向链表实现存储(将内存中零散的内存单元通过附加的引用关联起来,形成一个可以按序号索引的线性结构,这种链式存储方式与数组的连续存储方式相比,内存的利用率更高),按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。Vector 属于遗留容器(Java 早期的版本中提供的容器,除此之外,Hashtable、Dictionary、BitSet、Stack、Properties都是遗留容器),已经不推荐使用,但是由于 ArrayList 和 LinkedListed 都是非线程安全的,如果遇到多个线程操作同一个容器的场景,则可以通过工具类Collections 中的 synchronizedList 方法将其转换成线程安全的容器后再使用(这是对装潢模式的应用,将已有对象传入另一个类的构造器中创建新的对象来增强实现)。

补充:遗留容器中的 Properties 类和 Stack 类在设计上有严重的问题,Properties是一个键和值都是字符串的特殊的键值对映射,在设计上应该是关联一个Hashtable 并将其两个泛型参数设置为 String 类型,但是 Java API 中的Properties 直接继承了 Hashtable,这很明显是对继承的滥用。这里复用代码的方式应该是 Has-A 关系而不是 Is-A 关系,另一方面容器都属于工具类,继承工具类本身就是一个错误的做法,使用工具类最好的方式是 Has-A 关系(关联)或Use-A 关系(依赖)。同理,Stack 类继承 Vector 也是不正确的。Sun 公司的工程师们也会犯这种低级错误,让人唏嘘不已。

  1. Collection 和 Collections 的区别?
    Collection 是一个接口,它是 Set、List 等容器的父接口;Collections 是个一个工具类,提供了一系列的静态方法来辅助容器操作,这些方法包括对容器的搜索、排序、线程安全化等等。

  2. List、Map、Set 三个接口存取元素时,各有什么特点?
    List 以特定索引来存取元素,可以有重复元素。Set 不能存放重复元素(用对象的equals()方法来区分元素是否重复)。Map 保存键值对(key-value pair)映射,映射关系可以是一对一或多对一。Set 和 Map 容器都有基于哈希存储和排序树的两种实现版本,基于哈希存储的版本理论存取时间复杂度为 O(1),而基于排序树版本的实现在插入或删除元素时会按照元素或元素的键(key)构成排序树从而达到排序和去重的效果。

  3. TreeMap 和 TreeSet 在排序时如何比较元素?Collections 工具类中的 sort()方法如何比较元素?
    TreeSet 要求存放的对象所属的类必须实现 Comparable 接口,该接口提供了比较元素的 compareTo()方法,当插入元素时会回调该方法比较元素的大小。
    TreeMap 要求存放的键值对映射的键必须实现 Comparable 接口从而根据键对元素进 行排 序。
    Collections 工具类的 sort 方法有两种重载的形式,第一种要求传入的待排序容器中存放的对象比较实现 Comparable 接口以实现元素的比较;第二种不强制性的要求容器中的元素必须可比较,但是要求传入第二个参数,参数是Comparator 接口的子类型(需要重写 compare 方法实现元素的比较),相当于一个临时定义的排序规则,其实就是通过接口注入比较元素大小的算法,也是对回调模式的应用(Java 中对函数式编程的支持)。

  4. HashMap 和 HashTable 的区别是什么?

(1)继承的父类不同
Hashtable继承自Dictionary类,而HashMap继承自AbstractMap类。但二者都实现了Map接口。而且Hashtable 是 JDK 1 遗留下来的类,而 HashMap 是后来增加的。

(2)线程安全性不同
Hashtable 是同步的,比较慢,但 HashMap 没有同步策略,所以会更快。
javadoc中关于hashmap的一段描述如下:此实现不是同步的。如果多个线程同时访问一个哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须保持外部同步。
Hashtable 中的方法是Synchronize的,而HashMap中的方法在缺省情况下是非Synchronize的。在多线程并发的环境下,可以直接使用Hashtable,不需要自己为它的方法实现同步,但使用HashMap时就必须要自己增加同步处理。

(3)Hashtable 不允许有个空的 key,但是 HashMap 允许出现一个 null key。

推荐阅读:HashTable和HashMap的区别详解

  1. ConcurrentHashMap和HashMap的区别是什么?ConcurrentHashMap为什么线程安全?
    最大的区别就是ConcurrentHashMap是线程安全的,hashMap不是线程安全的。
    为什么线程安全呢:
    ConcurrentHashMap代码中可以看出,它引入了一个“分段锁”的概念,具体可以理解为把一个大的Map拆分成N个小的HashTable,根据key.hashCode()来决定把key放到哪个HashTable中。
    在ConcurrentHashMap中,就是把Map分成了N个Segment,put和get的时候,都是现根据key.hashCode()算出放到哪个Segment中:

  2. HashMap 和 HashSet 的区别?HashSet 是如何检查重复的?
    在这里插入图片描述
    当把对象加入到HashSet时,它会用对象的hashcode值来判断加入位置。同时它也会拿这个新值与已经加入的值作比较。如果没有相符的hashcode,HashSet就会假设新对象没有重复出现。如果hashcode相同,也不能代表两个对象相等。需要进一步调用equals()来对比是否两个对象相同。
    推荐阅读JAVA基础——HashSet怎样判断元素重复

  3. poll() 方法和 remove() 方法的区别?
    poll() 和 remove() 都是从队列中取出一个元素,但是 poll() 在获取元素失败的时候会返回空,但是 remove() 失败的时候会抛出异常。

  4. Java 中 LinkedHashMap 和 PriorityQueue 的区别是什么?
    PriorityQueue 保证最高或者最低优先级的的元素总是在队列头部,但是LinkedHashMap 维持的顺序是元素插入的顺序。当遍历一个 PriorityQueue时,没有任何顺序保证,但是 LinkedHashMap 课保证遍历顺序是元素插入的顺序。

  5. ArrayList 与 LinkedList 的不区别?
    最明显的区别是 ArrrayList 底层的数据结构是数组,支持随机访问,而LinkedList 的底层数据结构书链表,不支持随机访问。使用下标访问一个元素,ArrayList 的时间复杂度是 O(1),而 LinkedList 是 O(n)。

  6. 用哪两种方式来实现集合的排序?
    你可以使用有序集合,如 TreeSet 或 TreeMap,你也可以使用有顺序的的集合,如 list,然后通过 Collections.sort() 来排序。

  7. Java 中怎么打印数组?
    你可以使用 Arrays.toString() 和 Arrays.deepToString() 方法来打印数组。由于数组没有实现 toString() 方法,所以如果将数组传递给 System.out.println()方法,将无法打印出数组的内容,但是 Arrays.toString() 可以打印每个元素。

  8. Java 中的 LinkedList 是单向链表还是双向链表?
    是双向链表,你可以检查 JDK 的源码。在 Eclipse,你可以使用快捷键 Ctrl + T,直接在编辑器中打开该类。

  9. Java 中的 TreeMap 是采用什么树实现的?
    Java 中的 TreeMap 是使用红黑树实现的。

  10. Java 中的 HashSet,内部是如何工作的?
    HashSet 的内部采用 HashMap 来实现。由于 Map 需要 key 和 value,所以所有 key 的都有一个默认 value。类似于 HashMap,HashSet 不允许重复的key,只允许有一个 null key,意思就是 HashSet 中只允许存储一个 null 对象。

  11. 写一段代码在遍历 ArrayList 时移除一个元素?
    该问题的关键在于面试者使用的是 ArrayList 的 remove() 还是 Iterator 的remove()方法。这有一段示例代码,是使用正确的方式来实现在遍历的过程中移除元素,而不会出现 ConcurrentModificationException 异常的示例代码。
    推荐阅读:写一段代码在遍历 ArrayList 时移除一个元素?

  12. 我们能自己写一个容器类,然后使用 for-each 循环码?
    可以,你可以写一个自己的容器类。如果你想使用 Java 中增强的循环来遍历,你只需要实现 Iterable 接口。如果你实现 Collection 接口,默认就具有该属性。

  13. ArrayList 和 HashMap 的默认大小是多数?
    在 Java 7 中,ArrayList 的默认大小是 10 个元素,HashMap 的默认大小是16 个元素(必须是 2 的幂)。这就是 Java 7 中 ArrayList 和 HashMap 类的代码片段:

// from ArrayList.java JDK 1.7private static final int DEFAULT_CAPACITY = 10;
//from HashMap.java JDK 7static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;// aka 1
  1. 说出几点 Java 中使用 Collections 的最佳实践
    a)使用正确的集合类,例如,如果不需要同步列表,使用 ArrayList 而不是Vector。
    b)优先使用并发集合,而不是对集合进行同步。并发集合提供更好的可扩展性。
    c)使用接口代表和访问集合,如使用 List 存储 ArrayList,使用 Map 存储HashMap 等等。
    d)使用迭代器来循环集合。
    e)使用集合的时候使用泛型。

附:集合总结

  1. 容器简单分类图
    在这里插入图片描述
    由图可以看到,实际上只有四种容器:Map,List,Set,Queue;
    常用的容器用红色粗线表示;
    点线框表示接口;
    实线框表示普通的类(具体的类);
    带有空心箭头的虚线表示一个特定的类实现了一个接口;
    带有实心箭头的虚线表示某个类可以生成箭头所指方向类的对象(如hashMap的values()方法,返回 Collection<map的值>)。
  2. 区别比较
    在这里插入图片描述
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值