乐趣区

关于java:Java泛型和迭代器

泛型

1. 为什么要用泛型?

在泛型没有诞生之前,咱们常常会遇到这样的问题,如以下代码所示:

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 类型的元素,编译器就会报错,揭示开发人员插入雷同类型的元素。

报错信息如下图所示:

这样就能够防止结尾示例中,类型不统一导致程序运行过程中报错的问题了。

3. 泛型的长处

泛型的长处次要体现在以下三个方面。

  • 平安:不必放心程序运行过程中呈现类型转换的谬误。
  • 防止了类型转换:如果是非泛型,获取到的元素是 Object 类型的,须要强制类型转换。
  • 可读性高:编码阶段就明确的晓得汇合中元素的类型。

迭代器(Iterator)

1. 为什么要用迭代器?

咱们回忆一下,在迭代器(Iterator)没有呈现之前,如果要遍历数组和汇合,须要应用办法。

数组遍历,代码如下:

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", "subsistent");
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> list 和 List<Integer> list2 类型擦除后的后果都是 java.util.ArrayLis,进而 list.getClass() == list2.getClass() 的后果也肯定是 true。

5. List<Object> 和 List<?> 有什么区别?

答:List<?> 能够包容任意类型,只不过 List<?> 被赋值之后,就不容许增加和批改操作了;而 List<Object> 和 List<?> 不同的是它在赋值之后,能够进行增加和批改操作,如下图所示:

6. 能够把 List<String> 赋值给 List<Object> 吗?

答:不能够,编译器会报错,如下图所示:

7. List 和 List<Object> 的区别是什么?

答:List 和 List<Object> 都能存储任意类型的数据,但 List 和 List<Object> 的惟一区别就是,List 不会触发编译器的类型安全检查,比方把 List<String> 赋值给 List 是没有任何问题的,但赋值给 List<Object> 就不行,如下图所示:

8. 以下程序执行的后果是?

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)的应用,应用迭代器的益处是不必关注容器的外部细节,用同样的形式遍历不同类型的容器。

退出移动版