爲何要使用泛型和迭代器

爲何要使用泛型和迭代器 + 面試題

泛型

1)爲何要用泛型?

在泛型沒有誕生以前,咱們常常會遇到這樣的問題,如如下代碼所示:java

ArrayList arrayList = new ArrayList();
arrayList.add("Java");
arrayList.add(24);
for (int i = 0; i < arrayList.size(); i++) {
    String str = (String) arrayList.get(i);
    System.out.println(str);
}

看起來好像沒有什麼大問題,也能正常編譯,但真正運行起來就會報錯:面試

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

at xxx(xxx.java:12)設計模式

類型轉換出錯,當咱們給 ArrayList 放入不一樣類型的數據,卻使用一種類型進行接收的時候,就會出現不少相似的錯誤,可能更多的時候,是由於開發人員的不當心致使的。那有沒有好的辦法能夠杜絕此類問題的發生呢?這個時候 Java 語言提供了一個很好的解決方案——「泛型」。數組

2)泛型介紹

泛型:泛型本質上是類型參數化,解決了不肯定對象的類型問題。
泛型的使用,請參考如下代碼:安全

ArrayList<String> arrayList = new ArrayList();
arrayList.add("Java");

這個時候若是給 arrayList 添加非 String 類型的元素,編譯器就會報錯,提醒開發人員插入相同類型的元素。編碼

這樣就能夠避免開頭示例中,類型不一致致使程序運行過程當中報錯的問題了。spa

3)泛型的優勢

泛型的優勢主要體如今如下三個方面。設計

  • 安全:不用擔憂程序運行過程當中出現類型轉換的錯誤。
  • 避免了類型轉換:若是是非泛型,獲取到的元素是 Object 類型的,須要強制類型轉換。
  • 可讀性高:編碼階段就明確的知道集合中元素的類型。

迭代器(Iterator)

1)爲何要用迭代器?

咱們回想一下,在迭代器(Iterator)沒有出現以前,若是要遍歷數組和集合,須要使用方法。code

數組遍歷,代碼以下:對象

String[] arr = new String[]{"Java", "Java虛擬機", "Java中文社羣"};
for (int i = 0; i < arr.length; i++) {
    String item = arr[i];
}

集合遍歷,代碼以下:

List<String> list = new ArrayList<String>() {{
    add("Java");
    add("Java虛擬機");
    add("Java中文社羣");
}};
for (int i = 0; i < list.size(); i++) {
    String item = list.get(i);
}

而迭代器的產生,就是爲不一樣類型的容器遍歷,提供標準統一的方法。

迭代器遍歷,代碼以下:

Iterator iterator = list.iterator();
while (iterator.hasNext()) {
    Object object = iterator.next();
    // do something
}

總結:使用了迭代器就能夠不用關注容器的內部細節,用一樣的方式遍歷不一樣類型的容器。

2)迭代器介紹

迭代器是用來遍歷容器內全部元素對象的,也是一種常見的設計模式。

迭代器包含如下四個方法。

  • hasNext():boolean —— 容器內是否還有能夠訪問的元素。
  • next():E —— 返回下一個元素。
  • remove():void —— 刪除當前元素。
  • forEachRemaining(Consumer):void —— JDK 8 中添加的,提供一個 lambda 表達式遍歷容器元素。

迭代器使用以下:

List<String> list = new ArrayList<String>() {{
    add("Java");
    add("Java虛擬機");
    add("Java中文社羣");
}};
Iterator iterator =  list.iterator();
// 遍歷
while (iterator.hasNext()){
    String str = (String) iterator.next();
    if (str.equals("Java中文社羣")){
        iterator.remove();
    }
}
System.out.println(list);

程序執行結果:

[Java, Java虛擬機]

forEachRemaining 使用以下:

List<String> list = new ArrayList<String>() {{
    add("Java");
    add("Java虛擬機");
    add("Java中文社羣");
}};
// forEachRemaining 使用
list.iterator().forEachRemaining(item -> System.out.println(item));

相關面試題

1.爲何迭代器的 next() 返回的是 Object 類型?

答:由於迭代器不須要關注容器的內部細節,因此 next() 返回 Object 類型就能夠接收任何類型的對象。

2.HashMap 的遍歷方式都有幾種?

答:HashMap 的遍歷分爲如下四種方式。

  • 方式一:entrySet 遍歷
  • 方式二:iterator 遍歷
  • 方式三:遍歷全部的 key 和 value
  • 方式四:經過 key 值遍歷

以上方式的代碼實現以下:

Map<String, String> hashMap = new HashMap();
hashMap.put("name", "老王");
hashMap.put("sex", "你猜");
// 方式一:entrySet 遍歷
for (Map.Entry item : hashMap.entrySet()) {
  System.out.println(item.getKey() + ":" + item.getValue());
}
// 方式二:iterator 遍歷
Iterator<Map.Entry<String, String>> iterator = hashMap.entrySet().iterator();
while (iterator.hasNext()) {
  Map.Entry<String, String> entry = iterator.next();
  System.out.println(entry.getKey() + ":" + entry.getValue());
}
// 方式三:遍歷全部的 key 和 value
for (Object k : hashMap.keySet()) {
  // 循環全部的 key
  System.out.println(k);
}
for (Object v : hashMap.values()) {
  // 循環全部的值
  System.out.println(v);
}
// 方式四:經過 key 值遍歷
for (Object k : hashMap.keySet()) {
  System.out.println(k + ":" + hashMap.get(k));
}

3.如下關於泛型說法錯誤的是?

A:泛型能夠修飾類
B:泛型能夠修飾方法
C:泛型不能夠修飾接口
D:以上說法全錯

答:選 C,泛型能夠修飾類、方法、接口、變量。
例如:

public interface Iterable\<T\> {
}

4.如下程序執行的結果是什麼?

List<String> list = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
System.out.println(list.getClass() == list2.getClass());

答:程序的執行結果是 true
題目解析:Java 中泛型在編譯時會進行類型擦除,所以 List<String> listList<Integer> list2 類型擦除後的結果都是 java.util.ArrayLis ,進而 list.getClass() == list2.getClass() 的結果也必定是 true。

5. List<Object>List<?> 有什麼區別?

答:List<?> 能夠容納任意類型,只不過 List<?> 被賦值以後,就不容許添加和修改操做了;而 List<Object>List<?> 不一樣的是它在賦值以後,能夠進行添加和修改操做,以下圖所示:

6.能夠把 List<String> 賦值給 List<Object> 嗎?

答:不能夠,編譯器會報錯,以下圖所示:

List 和 List<Object> 的區別是什麼?

答: ListList<Object> 都能存儲任意類型的數據,但 ListList<Object> 的惟一區別就是,List 不會觸發編譯器的類型安全檢查,好比把 List<String> 賦值給 List 是沒有任何問題的,但賦值給 List<Object> 就不行,以下圖所示:

List<String> list = new ArrayList<>();
list.add("Java");
list.add("Java虛擬機");
list.add("Java中文社羣");
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
    String str = (String) iterator.next();
    if (str.equals("Java中文社羣")) {
        iterator.remove();
    }
}
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}
System.out.println("Over");

答:程序打印結果是 Over
題目解析:由於第一個 while 循環以後,iterator.hasNext() 返回值就爲 false 了,因此不會進入第二個循環,以後打印最後的 Over。

9.泛型的工做原理是什麼?爲何要有類型擦除?

答:泛型是經過類型擦除來實現的,類型擦除指的是編譯器在編譯時,會擦除了全部類型相關的信息,好比 List<String> 在編譯後就會變成 List 類型,這樣作的目的就是確保能和 Java 5 以前的版本(二進制類庫)進行兼容。

總結

經過本文知道了泛型的優勢:安全性、避免類型轉換、提升了代碼的可讀性。泛型的本質是類型參數化,但編譯以後會執行類型擦除,這樣就能夠和 Java 5 以前的二進制類庫進行兼容。本文也介紹了迭代器(Iterator)的使用,使用迭代器的好處是不用關注容器的內部細節,用一樣的方式遍歷不一樣類型的容器。

_

歡迎關注個人公衆號,回覆關鍵字「Java」 ,將會有大禮相送!!! 祝各位面試成功!!!

相關文章
相關標籤/搜索