Java相关知识点梳理(二)

容器相关

Posted by Haiming on September 25, 2019

忙里偷闲,扎实基础才是正经事。 8说了,开冲!

18. Java 容器都有哪些?

Java 容器分为 Collection 和 Map 两大类,其下又有很多子类,如下所示:

  • Collection
    • List
      • ArrayList
      • LinkedList
    • Vector
      • Stack
    • Set
      • HashSet
        • LinkedHashSet
      • TreeSet
  • Map
    • HashMap
    • LinkedHashMap
    • TreeMap
    • ConcurrentHashMap
    • HashTable

19. Collection 和 Collections 有什么区别?

  • Collection 是一个集合 Interface,其提供了对集合对象进行基本操作的 通用接口方法, 所有集合都是其子类,例如 List, Set 等等。
  • Collections 是一个包装类,包含了很多静态方法,不能被实例化,就像一个工具类,比如排序方法:Collections.sort(list)

Java 之中的数据分为 原始类型包装类, 例如 int 是原始类型,但是 Integer 是包装类。

包装类的作用在于:

  • 方便数据类型之间的转换
  • 提供一些该类型之中的方法,比如 Integer.MIN_VALUE 就会输出其能包含的最小值。

20. List,Set,Map 之间的区别是什么?

List,Set,Map 三者之间的区别主要在两个方面体现:元素是否有序,是否允许元素重复。其中有序,我认为不只是狭义上的“元素已经按照某种规则排列”,也包含“元素的下标是有意义”的这样一个含义(在List之中)。下面是理解的表格:

  元素有序 允许元素重复
List 有序:可以通过元素下标访问 允许
Set(AbstractSet) 无序 不允许
Set(HashSet) 无序:使用 Hash 实现 不允许
Set(TreeSet) 有序:使用二叉树排序 不允许
Map(AbstractMap) 无序 Key必须唯一,但是Value允许重复
Map(HashMap) 无序 Key必须唯一,但是Value允许重复
Map(TreeMap) 有序:使用二叉树排序 Key必须唯一,但是Value允许重复

21. HashMap 和 HashTable 有何区别

在之前的博文之中有写到过,包括 HashTable, HashMap 的源码实现以及其他部分。下面是简要介绍:

  • 存储:HashMap 允许 key 和 value 为 null,但是 HashTable 不可以。

    但是这一点也是有前提的:只有一个 key 可以为null。 实际上是直接将其置作第一个位置。

  • 线程安全:HashTable 是线程安全的,但是 HashMap 不是线程安全的。

  • 推荐使用:首先,在 HashTable 的注释之中可以看到, HashTable 是保留类,不建议使用。单线程下面推荐使用 HashMap, 多线程下面推荐使用 ConcurrentHashMap。原因是 ConcurrentHashMap 使用的是 SegmentLock,效率比 HashTable 的 Lock 多很多。

22. 如何决定使用 HashMap 还是 TreeMap

由于HashMap 是使用 Hash 来对元素进行操作,因此在 插入, 删除, 定位等等都可以做到 O(1) 的复杂度,在这些操作时候 HashMap 是最好的选择。

但是如果想要对一个 Key 集合进行有序的便利,那么 TreeMap 可以做到有序,是更好的选择。

23. 说一下 HashMap 的实现原理

之前在 Blog 的 浅谈Java之中的HashMap 已经将整体全部仔细叙述过一遍,因此在这里只是进行一点浅要的解析。

HashMap 是基于 Hash 算法实现的, 通过 put(key,value) 存储,get(key) 来获取。当传入一个值的时候,HashMap 会根据 key.hashCode() 计算出 Hash 值, 根据 Hash 值来决定将 value 保存在哪个 bucket 里面。当计算的 Hash 值相同的时候,使用链表和红黑树来存储 hash 相同的 value(在 Java8 之中是自动转换,当链表长度达到一定数值的时候,自动将链表转换成红黑树)。

24. 说一下 HashSet 的实现原理

HashSet 是基于 HashMap 实现的,但是在 Set 之中,只需要保存值,而不需要保存其索引。那么怎么办呢?

HashSet 采用了一种存储 Object 的方法来进行这个操作。

private transient HashMap<E,Object> map;

上面这个代码之中可以看到,其实 HashSet 就是 value 之处存储 Object 的 HashMap。

HashSet 不允许重复的值。

25. ArrayList 和 LinkedList 的区别是什么?

  • 数据结构实现: 首先从底层的数据结构来讲,ArrayList 是动态数组,而 LinkedList 是双向链表。
  • 随机访问效率:ArrayList 比 LinkedList 在随机访问的时候效率要高很多。因为 LinkedList 需要不停的移动指针向后找才能发现其想要的 index 所对应的 element
  • 增加和删除效率: 在非首尾的增加和删除操作,LinkedList 比 ArrayList 的效率要高,因为 ArrayList 的增删操作要影响其他数据的下标。ArrayList 在扩容的时候需要先 new 一块内存空间,然后将之前的 data 全部 copy 过去。而 LinkedList 则不需要。

所以,综合来说,当需要频繁的读写集合之中的元素的时候, ArrayList 更推荐,但是若业务之中的删除和插入比较多,更推荐使用 ArrayList

26. 如何实现数组和 List 之间的转换

  • 数组转换 List: 使用 Arrays.asList()进行转换
  • List 转换数组:使用List 自带的 toArray()方法。

27. ArrayList 和 Vector 的区别是什么?

  • 线程安全性:Vector 使用了 Synchronized 来实现线程同步,是线程安全的,但是 ArrayList 是非线程安全的。但是不得不说,Vector 之中的 Synchronized 关键字的锁实在是性能不咋地……
  • 性能:ArrayList 的性能优于 Vector
  • 扩容: ArrayList 和 Vector 都会动态扩容,只是 Vector 每次扩容增加一倍容量,而 ArrayList 每次只会增加 50%

28. Array 和 ArrayList 有何区别?

相同点这里就不赘述了,下面叙述一下不同点。

  • Array 可以存储基本数据类型和对象,ArrayList 只能存储对象
  • Array 的大小是固定不变的,但是 ArrayList 的大小是自动扩展的。
  • Array 内置的方法没有 ArrayList 多,比如 addAll,removeAll, iteration 等等方法只有 ArrayList 才有。

29. 在 Queue 之中,poll() 和 remove() 有什么区别?

  • 相同点:其都是返回第一个元素,并且返回在 Queue 之中删除的对象。
  • 不同点:在 Queue 为空的时候二者的返回不同。 没有元素, poll() 会返回 null, 但是 remove() 会抛出 NoSuchElementException 异常。
Queue<String> queue = new LinkedList<String>();
queue.offer("Element");
System.out.println(queue.poll());//Will return "Element"
System.out.println(queue.remove());//Will throw NoSuchElementException
System.out.println(queue.size());

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

Vector, HashTable, Stack 都是线程安全的(注意其线程安全的 @Synchronize 关键字),而 HashMap 是非线程安全的。 不过在 JDK 1.5 之后,各种类型都有了自己的对应的线程安全类, 比如 HashMap 的线程安全类就是 ConcurrentHashMap

31. 迭代器 Iterator 是什么?

Iterator 接口提供 遍历任何 Collection 的接口。 Iterator 允许**快速失败(Fail-fast) **机制。

32. Iterator 怎么使用?有什么特点?

Iterator 代码如下:

List<String> list = new ArrayList<>();
Iterator<String> it = list. iterator();
while(it.hasNext()){
    String obj = it.next();
    System.out.println(obj);
}

其特点是使用更加安全,因为之前提到过,Iterator 有 快速失败 机制,其原理为:

维护一个变量 modCount ,在每次对数组进行改变的时候, modCount 加一,当 modCount != expectedModCount 的时候,直接抛出异常。

33. IteratorListIterator 有何区别?

下面是参考文章:

https://blog.csdn.net/longshengguoji/article/details/41551491

首先,从细节上而言:

Iterator 包含的方法有:

  • hasNext():如果迭代器指向位置后面还有元素,则返回 true,否则返回false

  • next():返回集合中Iterator指向位置后面的元素

  • remove():删除集合中Iterator指向位置后面的元素

ListIterator 包含的方法有:

  • add(E e): 将指定的元素插入列表,插入位置为迭代器当前位置之前
  • hasNext():以正向遍历列表时,如果列表迭代器后面还有元素,则返回 true,否则返回false
  • hasPrevious():如果以逆向遍历列表,列表迭代器前面还有元素,则返回 true,否则返回false
  • next():返回列表中ListIterator指向位置后面的元素
  • nextIndex():返回列表中ListIterator所需位置后面元素的索引
  • previous():返回列表中ListIterator指向位置前面的元素
  • previousIndex():返回列表中ListIterator所需位置前面元素的索引
  • remove():从列表中删除next()或previous()返回的最后一个元素(有点拗口,意思就是对迭代器使用hasNext()方法时,删除ListIterator指向位置后面的元素;当对迭代器使用hasPrevious()方法时,删除ListIterator指向位置前面的元素)
  • set(E e):从列表中将next()或previous()返回的最后一个元素返回的最后一个元素更改为指定元素e

既然细节都有了,那么下面是概括总结:

一、相同点

都是迭代器,当只满足迭代要求的时候,这二者都适用。

二、不同点

  1. 使用范围不同: Iterator 可以应用于所有集合,但是 ListIterator 只适用于 List 及其子类型。
  2. ListIterator 有 add() 方法,可以向 List 之中添加对象,但是 Iterator 不可以。
  3. ListIterator 除了顺序向后遍历之外,还有 hasPrevious()previous() 两种方法,可以实现逆向遍历,但是 Iterator 不可以。
  4. ListIterator 可以定位当前索引的位置,使用 nextIndex()previousIndex() 可以实现,但是 Iterator 不可以。
  5. ListIterator 可以实现对象的修改,使用 set() 可以实现,但是 Iterator 只可以遍历,没法修改。

34. 怎样确保一个集合不会被修改?

可以使用 Collections.unmodifiableCollection(Collection c) 的方法来创建一个只读集合,这样改变集合的任何操作都会抛出 Java.lang.UnsupportedOperationException 异常。

下面是示例代码:

List<String> list = new ArrayList<>();
list.add("element");
Collection<String> clist = Collections.unmodifiableCollection(list);
clist.add("element2");//Here will throw new Exception
System.out.println(list.size());