目录
一、集合(Collection)
1.1、概述
集合是Java中提供的一种容器,用来存储多个数据
1.2、集合继承关系图
- ArrayList继承了抽象类AbstractList,同时实现了List接口,List接口继承了Collection接口。
- Collection接口为最顶层集合接口
- Collection接口的子类:List接口、Set接口
- List接口常用的子类有:ArrayList类、LinkedList类
- Set接口常用的子类有:HashSet类、LinkedHashSet类
1.3、集合的方法
- boolean.add(E e) 添加元素到集合
- int size() 返回集合中元素的个数
- Object[] toArray() 将集合转成数组
- boolean contains(Object o) 判断集合中是否有该元素
- void clear() 清空集合
public static void main(String[] args) {
Collection<String> collection = new ArrayList<String>();
collection.add("abc");
collection.add("a");
collection.add("b");
collection.add("c");
collection.add("c");
collection.add("abc"); // 集合中可以存储重复元素
// 返回集合中元素的个数
System.out.println(collection.size());
// 转换成数组形式
Object[] objects = collection.toArray();
System.out.println(Arrays.toString(objects));
// 是否包含abc
System.out.println(collection.contains("abc"));
// 删除某个元素
collection.remove("abc"); // 移除第一个为“abc”的元素
System.out.println(collection);
Collection<String> c = new ArrayList<>();
c.add("c");
collection.removeAll(c); // 移除所有为“c”的元素
System.out.println(collection);
// 清空集合
collection.clear();
System.out.println(collection.size()); // 清空后集合元素个数为0
}
二、迭代器
2.1、迭代器概述
- Java提供了很多个集合,这些集合采取的存储方式不同,我们要取这些集合中的元素,可以通过一种通用的获取方式来完成
- Collection集合的通用获取方式,在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,然后继续判断,直到集合中的元素全部取出,这种方式就叫迭代
- 每种集合的底层的数据结构不同,比如ArrayList是数组,LinkedList底层是链表。但无论哪种集合,都会有判断是否有元素及取出元素的动作,所以Java提供了一个迭代器定义了统一判断元素和取元素的方法。
2.1、迭代器的实现原理
- 集合中的迭代器:获取集合中元素的方式
- Iterator接口:两个方法
- hasNext() 判断集合中是否还有元素,有则返回true
- next() 取出集合中下一个元素
- 找Iterator接口的实现类
Collection定义了一个方法:Iterator iterator();
以ArrayList为例:
public static void main(String[] args) {
Collection<String> collection = new ArrayList();
collection.add("a");
collection.add("b");
collection.add("c");
Iterator<String> iterator = collection.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
// for循环写法
// 集合在前面就被取了,集合中元素就就没有了
for(;iterator.hasNext();){
System.out.println(iterator.next());
}
}
2.3、集合迭代中的转型
- 在使用集合时,需要注意:
- 集合中存储的其实都是对象的地址
- 集合中也可以存储基本数值,是因为出现了基本类型的包装类,它提供了自动装箱的操作,本质上,集合中的元素都是基本数值的包装类对象
- 存储时提升了Object。取出时要使用元素的特有内容,必须向下转型
Collection<String> collection = new ArrayList();
collection.add("a");
collection.add("b");
collection.add("c");
Iterator iterator = collection.iterator();
while (iterator.hasNext()){
String next =(String)iterator.next();
System.out.println(next);
System.out.println(next.length());
}
注意:如果集合中存储了多种类型对象,这时向下转型会发生类型转换异常
- Iterator接口也可以使用<>,来控制迭代元素的类型
三、增强for循环
3.1、增强for循环遍历数组
- 增强for循环是JDK1.5新特性,JDK1.5版本后,出现新的接口java.lang.Iterable
- Iterable作用就是实现增强for循环
- 格式:
for(数据类型 变量名:数组或集合){}
- 代码示例:
String[] strArr = {"1","2","a","b"};
for(String str : strArr){
System.out.println(str);
}
- 增强for循环的好处和弊端
好处:代码减少,方便对容器的遍历
弊端:没有索引,不能操作容器里面的元素
3.2、增强for循环遍历集合
遍历集合的方式与数组一样
List<Person> list = new ArrayList();
list.add(new Person("小镭",20));
list.add(new Person("小黑",18));
for(Person p : list){
System.out.println(p.toString());
}
四、泛型
4.1、泛型的定义和使用
- 定义:泛型就是定义之初用符号表示不具体的类型,在使用的时候才动态地指定类型。
- 作用:是一种安全机制,是一种书写规范
- 使用:指明集合中存储的数据类型
Collection<String> collection = new ArrayList<String>();
4.2、 擦拭法
- 泛型是一种类似于“模板代码”的技术。Java语言的泛型实现方式是擦拭法(Type Erasure)
- 泛型只在编译时存在,编译后就被擦除。在编译之前可以限制集合类型起到作用。
Collection<String> collection = new ArrayList<String>();
编译后:
Collection collection = new ArrayList();
4.3、泛型类
- 定义格式
修饰符 class 类名<代表泛型的变量> {
}
例如:API中的ArrayList
public class ArrayList<E> {
public boolean add(E e) {}
public E get(int index) {}
}
- 使用格式:创建对象的时候,确定泛型的类型
ArrayList<String> list = new ArrayList<String>();
// 此时,变量E的值就是String类型
public class ArrayList<String> {
public boolean add(String e) {}
public String get(int index) {}
}
ArrayList<Integer> list = new ArrayList<Integer>();
// 此时,变量E的值就是Integer类型
public class ArrayList<Integer> {
public boolean add(Integere) {}
public Integer get(int index) {}
}
4.4、泛型方法
- 定义方法
修饰符 <代表泛型的变量> 返回值类型 方法名(参数){}
- 泛型方法的使用
ArrayList集合中的方法:
public <T> T[] toArray(T[] a) {}
- 使用格式:调用方法时,确定泛型的类型
ArrayList<String> list = new ArrayList<String>();
String[] arr = new String[10];
String[] result = list.toArray(arr);
// 此时变量T为String类型
public <String> String[] toArray(String[] a) {}
// 变量T可以与定义集合的泛型不同
ArrayList<String> list = new ArrayList<String>();
Integer[] arr = new Integer[10];
Integer[] result= list.toArray(arr);
// 此时,变量T的类型为Integer类型
public <Integer> Integer[] toArray(Integer[] a) {}
4.5、泛型的接口
- 泛型接口的格式
修饰符 interface 接口名<代表泛型的变量>{
// 抽象方法
}
例如:
public interface List<E>{
boolean add(E e);
}
- 泛型类的使用
- 实现类实现接口,同时指定泛型类型
- 实现类实现接口,但不指定泛型,那么这个类也变成了泛型
比如 ArrayList实现了List接口,但没有指定泛型
public class ArrayList<E> implements List<E>{}
4.6、泛型的好处
- 将运行时期的异常(ClassException),转移到了编译时期,使编译失败
- 避免了类型强转的麻烦
Collection<String> collection = new ArrayList();
collection.add("a");
collection.add("b");
collection.add("c");
collection.add(1); // 会编译失败 当即和明确类型后,存放不同类型编译会报错
// 当集合明确了类型时,迭代器就会知道具体遍历的元素类型
Iterator<String> iterator = collection.iterator();
while (iterator.hasNext()){
Stringnext = iterator.next();
System.out.println(next);
}
4.7、泛型的通配符
需求:封装一个方法,可以遍历参数为任意类型的ArrayList
public static void method(ArrayList<?> list){
// 遍历逻辑
for(Object o : list){
System.out.println(o);
}
}
当不知道使用什么类型来接收元素时,此时使用 ? (?表示通配符)
4.8、泛型的限定
- <? extends XXX> 限定的是父类,上限限定,表示可以传递XXX及其子类
- <? super XXX> 限定的是子类,下限限定,表示可以传递XXX及其父类
五、List接口
5.1、List接口介绍
特点:
- List是一个元素存取有序的集合 例如,存元素的顺序是11、22、33,那么集合中元素的存储顺序就是11、22、33
- List是带有索引的集合,通过索引就可以精确的操作集合中的元素
- 集合中可以存重复的元素,通过元素equals方法,来比较是否为重复元素。
List接口的常用子类
- ArrayList
- LinkedList
5.2、List接口的常用方法
- 添加元素
- add(Object o) 向集合末尾处,添加指定元素
- add(int index, Object o); 向集合指定索引处,添加指定元素,原有元素依次后移
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add(1,"c");
System.out.println(list);
- 删除元素
- remove(Object o) 指定元素对象从集合中删除,返回值为布尔(boolean)类型
- remove(int index) 将指定索引处的元素从集合中删除,返回值为被删除的元素
boolean result1 = list.remove("a");
System.out.println(result1);
String result2 = list.remove(0);
System.out.println(result2);
- 替换元素
- set(int index, Object o); 将指定索引处的元素替换成指定元素,返回值为替换前的元素
String result3 = list.set(0, "java");
System.out.println(result3);
System.out.println(list);
- 查询元素
- get(int index); 获取指定索引处的元素,并返回查询元素
System.out.println(list.get(0));
六、迭代器的并发修改
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
System.out.println(list);
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
String next = iterator.next();
System.out.println(next);
if("a".equals(next)){
list.add("xiaolei"); // 会报错 并发修改异常
}
}
}
运行上述代码,会发生 java.util.ConcurrentModificationException
原因是在迭代过程中,使用了集合的方法对集合元素进行了操作,导致迭代器并不知道集合的变化而引发数据的不确定性。
并发修改异常的解决办法:
- 在迭代过程中,不适用集合的方法操作元素
- 若一定要在迭代中操作元素,可以通过ListIterator迭代器来完成。