1. Collection集合的常用功能
java.util.Collection<E>接口:
[Java] 纯文本查看 复制代码
// 成员方法(子类都会实现)
boolean add(E e): 把给定的对象添加到当前集合中
void clear(): 清空集合中所有的元素
boolean remove(E e): 把给定的对象在当前集合中删除
boolean contains(E e): 判断当前集合中是否包含给定的对象
boolean isEmpty(): 判断当前集合是否为空(没有元素)
int size(): 返回集合中元素的个数
Object[] toArray(): 把集合中的元素,存储到数组中
Iterator<E> iterator(): 获取集合的迭代器对象 (后面讲到)
Iterator接口: 迭代器
测试Collection接口的方式:
使用多态方式创建对象: Collection c = new ArrayList();
编译看左边, 这样只能调用Collection接口中定义的方法, 不会出现子类特有方法
2. 迭代器Iterator接口介绍和迭代步骤
迭代:
类似于遍历, 判断是否有下一个元素, 有则取出下一个, 直到没有
迭代器:
用于遍历集合的对象
java.util.Collection<E>接口:
// 成员方法(子类都会实现)
Iterator<E> iterator(): 获取集合的迭代器对象
java.util.Iterator<E>接口: 迭代器
// 成员方法
boolean hasNext(): 判断是否有下一个元素
E next(): 获取下一个元素
void remove(): 删除next指向的元素
使用迭代器遍历集合的3步:
1. 使用集合对象的 iterator() 获取迭代器对象, 用 Iterator 接口接收.(多态)
2. 使用 Iterator 接口中的 hasNext() 方法, 判断是否有下一个元素
3. 使用 Iterator 接口中的 next() 方法, 获取下一个元素
迭代器遍历集合的标准写法:
[Java] 纯文本查看 复制代码
Iterator<元素类型> iterator = 集合对象.iterator();
while (iterator.hasNext()) {
元素类型 变量名 = iterator.next();
}
迭代器的实现原理
集合和迭代器对象的关系:
每个集合都有对应的一个迭代器对象
迭代器的原理:
迭代器有一个指针(其实就是个变量, 保存索引值), 最初指向集合的 0 位置
hasNext() 方法可以判断当前索引是否有元素
next() 方法返回当前元素, 并移动指针到下一个索引
迭代器的并发修改异常
并发修改异常的原因:
在使用迭代器迭代的过程中, 如果执行了改变集合长度的操作 (如 add(), remove(), clear()), 则会抛出 ConcurrentModificationException 并发修改异常.
如何避免:
1. 迭代过程中不要修改集合长度
2. 在迭代的过程中添加, 删除元素, 要使用迭代器自带的方法, 而不能使用集合的方法
Iterator中的 remove() 方法
ListIterator中的 add(), remove() 方法
3. 增强for循环
也称foreach循环, JDK 5出现, 用于遍历集合, 底层采用迭代器
作用:
遍历数组
遍历集合
增强for格式:
for(元素的数据类型 变量名 : Collection集合或数组名){
//操作代码
}
[Java] 纯文本查看 复制代码
// 数组示例
int[] arr = {1,2,3};
for (int i : arr) {
System.out.println(i);
}
// 集合示例
Collection<Integer> c = new ArrayList<>();
for (int i : c) {
System.out.println(i);
}
增强for对于数组和集合的操作:
对数组只是写法上的优化, 底层还是普通for循环
对集合是通过迭代器实现的
增强for, 普通for, 迭代器的区别:
增强for:
优点: 获取元素很方便, 格式简单
缺点: 没有普通for中的索引, 没有迭代器对象可以进行元素的增删
应用场景: 适用于遍历获取数组和集合元素的场景
普通for:
优点: 有索引可以使用, 某些方式可以在遍历过程中增删元素
缺点: 格式繁琐
应用场景: 需要用到索引的场景
迭代器:
优点: 可以使用迭代器对象的方法操作元素
缺点: 格式繁琐
应用场景: 需要在迭代过程中增删元素的场景
4. 泛型的概述以及不使用泛型产生的问题
Generic Type. JDK 5 增加. 是一种未知的数据类型
定义集合时, 某些方法不知道使用什么类型时, 就可以使用泛型
创建集合对象时, 需要确定泛型具体的类型
泛型可以看作是一个"变量", 用来接收数据类型
定义:
<E>: 声明了一个泛型 (声明了一个变量)
E: 使用E这种类型作为数据类型 (参数数据类型, 返回值类型)
使用:
ArrayList<Integer>: 确定了泛型的具体类型
[Java] 纯文本查看 复制代码
public class ArrayList<E> { // E可以表示任意的引用类型, 但是只能表示某一种
String name;
int age;
E e;
public boolean add(E e){} String
public E get(int index){}
}
泛型的好处
1. 避免了类型转换的麻烦
2. 将运行时的类型转换异常, 转移到了编译时期 (有利于程序员提前发现问题)
定义和使用含泛型的类
定义泛型
<泛型名>
泛型的"定义"和"使用"
泛型在"定义"时, "不能是"具体的类型, 只是一个变量名: public class ArrayList<E> {}
泛型在"使用"时, "必须是"具体的数据类型 ArrayList<Integer>
// 带有泛型的类定义格式
修饰符 class 类名<代表泛型的名字> { // 泛型的变量一般用一个大写字母表示, 但也可以是多个字母
}
类中的泛型, "在创建对象时", 确定泛型的具体类型
定义含有泛型的方法与使用
方法中的泛型定义位置:
修饰符 和 返回值类型 之间
// 带有泛型的方法定义格式
修饰符 <代表泛型的名字> 返回值类型 方法名(参数){
}
方法中定义泛型后, 返回值类型和参数类型都可以使用泛型
方法泛型的使用:
"在调用方法时"确定泛型的具体类型
定义与使用含有泛型的接口
定义泛型接口与定义泛型类一样
// 带有泛型的类定义格式
修饰符 interface 接口名<代表泛型的变量> {
}
public interface GenericInterface<I> {
void method(I i);
}
泛型定义总结:
定义在类上的泛型:
有效范围: 整个类中都有效
何时确定具体类型: 创建该类对象时确定泛型的具体类型
定义在方法上的泛型:
有效范围: 方法中(包括返回值类型和参数类型)有效
何时确定具体类型: 调用方法传参时确定泛型的具体类型
定义在接口上的泛型:
有效范围: 接口中
何时确定具体类型:
1. 子接口或实现类定义时确定泛型的具体类型
2. 创建实现类对象时确定泛型的具体类型
5. 泛型通配符
不知道使用什么类型来接收时, 此时可以使用 <?> 来表示未知通配符
示例: List<?> list 接收泛型是任意类型的List对象
[Java] 纯文本查看 复制代码
public void addAll(List<?> list){
// ...
}
ArrayList<Integer> list1 = new ArrayList<>();
ArrayList<String> list2 = new ArrayList<>();
addAll(list1);
addAll(list2);
注意: 只能接收数据, 不能调用方法存储元素
List<?> list 这种集合不能调用 add() 添加元素, 只能调用 get() 获取元素
List<?> list 其实是一个变量, 所以可以将一个集合赋值给它
如: List<?> list = new ArrayList<String>(); // 正确
但是不能直接new 泛型为?的集合对象:
如: List<?> list = new ArrayList<?>(); // 错误
使用方式:
不能创建对象使用
只能作为方法参数使用. (减少方法重载)
泛型的上限:
格式: 类型名称<? extends 类名> 对象名称
示例: List<? extends Number> list
作用: 只能接收泛型为该类型及其子类的对象 (Number及其子类的泛型)
泛型的下限:
格式: 类型名称<? super 类名> 对象名称
示例: List<? super Number> list
作用: 只能接收泛型为该类型及其父类型的对象 (Number及其父类的泛型)