目录
提示:这里介绍Collection、LIst和泛型的基本知识点
提示:以下是本篇文章正文内容,下面案例可供参考
一、Collection集合
1、集合的概述
1.1、概述:
集合其实就是一种容器,可以用来存储多个引用类型的数据
1.2、分类: 单列集合,双列集合:
- 单列集合: 以单个单个元素进行存储
- 双列集合: 以键值对的方式进行存储
1.3、集合与数组的区别:
- 长度:
- 数组长度是固定的
- 集合长度是不固定的
- 存储范围:
- 数组可以存储基本类型+引用类型 eg; int[],String[]
- 集合只能存储引用类型,如果要存储基本类型,需要存储基本类型对应的包装类类型 eg;
- ArrayList<String> ,ArrayList<Integer>
2、单列集合常用的继承体系
2.1、单列集合: 以单个单个元素进行存储
- 单列集合继承体系:
2.2、Collection接口是所有单列集合的根接口,也就意味着所有的单列集合都实现了Collection接口
- List接口继承Collection接口: List集合元素有索引,元素存取有序,元素可重复
- ArrayList类: 数组存储结构, 查询快,增删慢
- LinkedList类: 链表存储结构,查询慢,增删快
- ......
- Set接口继承Collection接口: Set集合元素没有索引 , 元素不可重复(唯一)
- HashSet类: 哈希表结构,由哈希表保证元素唯一,元素存取无序,不可以排序
- LinkedHashSet类:: 链表+哈希表结构,由哈希表保证元素唯一,由链表保证元素存取有序,不可以排序
- TreeSet类: 二叉树结构,可以对元素进行排序
- ......
3、Collection的常用功能
3.1、Collection是接口,只能通过其子类创建对象
3.2、Collection是所有单列集合的顶层父接口,所以所有单列集合都拥有Collection中的方法
3.3、常用方法:
- `public boolean add(E e)`: 把给定的对象添加到当前集合中 。
- `public void clear()` :清空集合中所有的元素。
- `public boolean remove(E e)`: 把给定的对象在当前集合中删除。
- `public boolean contains(Object obj)`: 判断当前集合中是否包含给定的对象。
- `public boolean isEmpty()`: 判断当前集合是否为空。
- `public int size()`: 返回集合中元素的个数。
- `public Object[] toArray()`: 把集合中的元素,存储到数组中
public class Test {
public static void main(String[] args) {
// 创建Collection集合对象,限制集合元素类型为String类型
Collection<String> col = new ArrayList<>();
// - public boolean add(E e): 把给定的对象添加到当前集合中 。
col.add("王宝强");
col.add("贾乃亮");
col.add("谢霆锋");
col.add("陈羽凡");
System.out.println("col:" + col);// col:[王宝强, 贾乃亮, 谢霆锋, 陈羽凡]
// - public void clear() :清空集合中所有的元素。
// col.clear();
// System.out.println("col:" + col);// col:[]
// - public boolean remove(E e): 把给定的对象在当前集合中删除。
boolean res1 = col.remove("谢霆锋");
System.out.println("col:" + col);// col:[王宝强, 贾乃亮, 陈羽凡]
System.out.println("res1:" + res1);// res1: true
// - public boolean contains(Object obj): 判断当前集合中是否包含给定的对象。
boolean res2 = col.contains("贾乃亮");
boolean res3 = col.contains("谢霆锋");
System.out.println("res2:" + res2);// res2: true
System.out.println("res3:" + res3);// res3: false
// - public boolean isEmpty(): 判断当前集合是否为空。如果集合中没有元素返回true,否则返回false
boolean res4 = col.isEmpty();
System.out.println("res4:" + res4);// res4: false
// 清空集合中的所有元素
// col.clear();
// boolean res5 = col.isEmpty();
// System.out.println("res5:" + res5);// res5: true
// - public int size(): 返回集合中元素的个数。
System.out.println("集合元素的个数:" + col.size());// 3
// - public Object[] toArray(): 把集合中的元素,存储到数组中
Object[] arr = col.toArray();
System.out.println("arr:"+ Arrays.toString(arr));// arr:[王宝强, 贾乃亮, 陈羽凡]
}
}
二、List
1.List接口的介绍
- List接口的概述
- java.util.List接口继承自Collection接口,是单列集合的一个重要分支
- List接口的特点
- 它是一个元素存取有序的集合
- 它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素
- 集合中可以有重复的元素
2、List中的常用方法
List作为Collection集合的子接口,不但继承了Collection接口中的全部方法,
而且还增加了一些根据元素索引来操作集合的特有方法,如下:
public void add(int index, E element)
: 将指定的元素,添加到该集合中的指定位置上。public E get(int index)
:返回集合中指定位置的元素。public E remove(int index)
: 移除列表中指定位置的元素, 返回的是被移除的元素。public E set(int index, E element)
:用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
List集合特有的方法都是跟索引相关;
List接口新增常用方法的使用:
public class Test {
public static void main(String[] args) {
// 创建List集合对象,限制集合元素的类型为String
List<String> list = new ArrayList<>();
// 往集合中添加元素的方法
list.add("马蓉");
list.add("李小璐");
list.add("张柏芝");
list.add("白百何");
System.out.println("list:" + list);// list:[马蓉, 李小璐, 张柏芝, 白百何]
// - public void add(int index, E element): 将指定的元素,添加到该集合中的指定位置上。
list.add(1, "潘金莲");
System.out.println("list:" + list);// list:[马蓉, 潘金莲, 李小璐, 张柏芝, 白百何]
// - public E get(int index):返回集合中指定位置的元素。
String e = list.get(1);
System.out.println("e:" + e);// e:潘金莲
// - public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。
String removeE = list.remove(1);
System.out.println("被删除的元素:"+removeE);// 被删除的元素:潘金莲
System.out.println("list:" + list);// list:[马蓉, 李小璐, 张柏芝, 白百何]
// - public E set(int index, E element):用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
String setE = list.set(2, "董洁");
System.out.println("被替换的元素:"+setE);// 被替换的元素:张柏芝
System.out.println("list:" + list);// list:[马蓉, 李小璐, 董洁, 白百何]
}
}
注意: 如果集合元素为Integer类型,那么删除的时候优先根据索引删除:
public class Test2 {
public static void main(String[] args) {
/*
public boolean remove(Object obj): 删除指定的元素。
public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。
*/
// 创建List集合,限制集合元素的类型为Integer
List<Integer> list = new ArrayList<>();
// 往集合中添加元素
list.add(1);
list.add(2);
list.add(3);
list.add(4);
// 删除元素
list.remove(2);// 优先根据索引删除
System.out.println("list:" + list);// list:[1, 2, 4]
}
}
3、List的子类
- ArrayList集合: 底层采用的是数组结构,查询快,增删慢
- 方法: 来自Collection,List
- LinkedList集合; 底层采用的是链表结构,查询慢,增删快
- 方法: 来自Collection,List,LinkedList特有的方法
- 特有的方法:
public void addFirst(E e):将指定元素插入此列表的开头
public void addLast(E e):将指定元素添加到此列表的结尾
public E getFirst():返回此列表的第一个元素
public E getLast():返回此列表的最后一个元素
public E removeFirst():移除并返回此列表的第一个元素
public E removeLast():移除并返回此列表的最后一个元素
public E pop():从此列表所表示的堆栈处弹出一个元素
public void push(E e):将元素推入此列表所表示的堆栈
public class Test {
public static void main(String[] args) {
// 创建LinkedList集合,限制集合元素类型为String
LinkedList<String> list = new LinkedList<>();
// 往集合中添加一些元素
list.add("苍老师");
list.add("小泽老师");
list.add("波老师");
System.out.println("list:"+list);// list:[苍老师, 小泽老师, 波老师]
// public void addFirst(E e):将指定元素插入此列表的开头
list.addFirst("吉泽老师");// list:[吉泽老师, 苍老师, 小泽老师, 波老师]
System.out.println("list:"+list);// list:[吉泽老师, 苍老师, 小泽老师, 波老师]
// public void addLast(E e):将指定元素添加到此列表的结尾
list.addLast("三村老师");
System.out.println("list:"+list);// list:[吉泽老师, 苍老师, 小泽老师, 波老师, 三村老师]
// public E getFirst():返回此列表的第一个元素
System.out.println("第一个元素: "+list.getFirst());// 吉泽老师
// public E getLast():返回此列表的最后一个元素
System.out.println("最后一个元素: "+list.getLast());// 三村老师
// public E removeFirst():移除并返回此列表的第一个元素
String removeFirstE = list.removeFirst();
System.out.println("removeFirstE:"+removeFirstE);// 吉泽老师
// public E removeLast():移除并返回此列表的最后一个元素
String removeLastE = list.removeLast();
System.out.println("removeLastE:"+removeLastE);// 三村老师
System.out.println("list:"+list);// list:[苍老师, 小泽老师, 波老师]
// public E pop():从此列表所表示的堆栈处弹出一个元素(removeFirst方法)
String popE = list.pop();
System.out.println("popE:"+popE);// 苍老师
// public void push(E e):将元素推入此列表所表示的堆栈(addFirst)
list.push("明日花老师");
System.out.println("list:"+list);// list:[明日花老师, 小泽老师, 波老师]
}
}
三、Iterator迭代器
1、Iterator接口
1.1、迭代的概念
1.1.1、概述:迭代即Collection集合元素的通用获取方式。
在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续再判断,
如果还有就再取出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。
1.1.2、迭代的步骤:
- 获取迭代器对象
- 使用迭代器对象判断集合中是否有元素可以取出
- 如果有元素可以取出,就直接取出来该元素,如果没有元素可以取出,就结束迭代
1.1.3、获取迭代器对象
Collection集合提供了一个获取迭代器的方法:
public Iterator iterator()
: 获取集合对应的迭代器,用来遍历集合中的元素的。
Iterator迭代器对象的常用方法
public boolean hasNext()
:如果仍有元素可以迭代,则返回 true。public E next()
:返回迭代的下一个元素。void remove()
删除当前迭代出来的元素
案例:
public class Test {
public static void main(String[] args) {
/*
获取迭代器对象
Collection集合提供了一个获取迭代器的方法:
- public Iterator iterator(): 获取集合对应的迭代器,用来遍历集合中的元素的。
Iterator迭代器对象的常用方法
- public boolean hasNext():如果仍有元素可以迭代,则返回 true。
- public E next():返回迭代的下一个元素。
- void remove()删除当前迭代出来的元素
*/
Collection<String> col = new ArrayList<>();
// 往集合中添加元素
col.add("王宝强");
col.add("贾乃亮");
col.add("谢霆锋");
col.add("陈羽凡");
// 通过集合对象获取对应的迭代器对象
Iterator<String> it = col.iterator();
// 循环判断是否有元素可以迭代
while (it.hasNext()) {
// 如果有,就在循环中取出可以迭代的元素
String e = it.next();
System.out.println("元素:"+e);
// 需求:如果迭代出来的元素是谢霆锋,就删除该元素
if ("谢霆锋".equals(e)){
it.remove();
}
}
System.out.println("col:"+col);// col:[王宝强, 贾乃亮, 陈羽凡]
}
}
2、Iterator实现原理
2.1、 迭代器的实现原理:
我们在之前案例已经完成了Iterator遍历集合的整个过程。当遍历集合时,首先通过调用集合的iterator()方法获得迭代器对象,然后使用hashNext()方法判断集合中是否存在下一个元素,如果存在,则调用next()方法将元素取出,否则说明已到达了集合末尾,停止遍历元素。
Iterator迭代器对象在遍历集合时,内部采用指针的方式来跟踪集合中的元素。在调用Iterator的next方法之前,迭代器的索引位于第一个元素之前,不指向任何元素,当第一次调用迭代器的next方法后,迭代器的索引会向后移动一位,指向第一个元素并将该元素返回,当再次调用next方法时,迭代器的索引会指向第二个元素并将该元素返回,依此类推,直到hasNext方法返回false,表示到达了集合的末尾,终止对元素的遍历。
2.2、原理图:
3、Itrator的常见问题
3.1、常见问题1:
在进行集合元素获取时,如果集合中已经没有元素可以迭代了,还继续使用迭代器的next方法,
将会抛出java.util.NoSuchElementException没有集合元素异常。
案例:
public class Test2 {
public static void main(String[] args) {
/*
问题一:在进行集合元素获取时,如果集合中已经没有元素可以迭代了,
还继续使用迭代器的next方法,将会抛出java.util.NoSuchElementException没有集合元素异常。
*/
Collection<String> col = new ArrayList<>();
// 往集合中添加元素
col.add("王宝强");
col.add("贾乃亮");
col.add("谢霆锋");
col.add("陈羽凡");
// 获取迭代器对象
Iterator<String> it = col.iterator();
// 迭代--->快捷键:itit
while (it.hasNext()) {
String e = it.next();
System.out.println("元素:" + e);
}
System.out.println("--------");
// 调用next方法
// String next = it.next();// 报NoSuchElementException没有找到元素异常
// System.out.println("next:" + next);
// 需求:再迭代col集合中所有的元素
// 获取迭代器对象
Iterator<String> it1 = col.iterator();
// 迭代--->快捷键:itit
while (it1.hasNext()) {
String e = it1.next();
System.out.println("元素:" + e);
}
}
}
解决方法:使用集合从新获取一个新的迭代器对象来使用
3.2、常见问题2:
在进行集合元素迭代时,如果添加或移除集合中的元素 , 将无法继续迭代 ,
将会抛出ConcurrentModificationException并发修改异常.
案例:
public class Test3 {
public static void main(String[] args) {
/*
问题: 在进行集合元素迭代时,如果添加或移除集合中的元素,将无法继续迭代,将会抛出ConcurrentModificationException并发修改异常.
*/
Collection<String> col = new ArrayList<>();
// 往集合中添加元素
col.add("王宝强");
col.add("贾乃亮");
col.add("谢霆锋");
col.add("陈羽凡");
// 获取迭代器对象
Iterator<String> it = col.iterator();
// 迭代
while (it.hasNext()) {
String e = it.next();
// 问题一: 迭代一个元素,就使用集合对象往集合中添加一个元素
// col.add("itheima");// 报并发修改异常
// 问题二: 迭代一个元素,就使用集合对象删除一个元素
// col.remove(e);// 报并发修改异常
// 迭代器可以一边迭代一边删除
it.remove();
}
System.out.println("col:" + col);// col:[]
}
}
解决办法:
- 使用CopyOnWriteArrayList集合,就可以迭代的时候,往集合中添加或删除元素
- 使用迭代器的remove方法实现一边迭代,一边删除
4、增强for循环
4.1、概述:
增强for循环(foreach循环),是JDK1.5以后出来的一个高级for循环,专门用来遍历数组和Collection集合
4.2、原理:
内部基于Iterator迭代器实现,所以在遍历的过程中,不能对集合中的元素进行增删操作,
否则抛出ConcurrentModificationException并发修改异常
4.3、格式:
for(数据类型 变量名 : 数组名\集合名){
}
案例:
public class Test4 {
public static void main(String[] args) {
/*
for(数据类型 变量名 : 数组名\集合名){
}
*/
// 需求1: 增强for循环遍历集合元素
Collection<String> col = new ArrayList<>();
// 往集合中添加元素
col.add("王宝强");
col.add("贾乃亮");
col.add("谢霆锋");
col.add("陈羽凡");
// 快捷键: 集合名.for
for (String e : col) {
System.out.println("e:" + e);
}
System.out.println("--------");
// 需求1: 增强for循环遍历数组元素
String[] arr = {"王宝强",
"贾乃亮",
"谢霆锋",
"陈羽凡"};
// 快捷键: 数组名.for
for (String e : arr) {
System.out.println("e:" + e);
}
}
}
四、泛型
1、泛型的概述
1.1、概述:
JDK5之后,新增了泛型(Generic)语法,可以在类、接口或方法中预支地使用未知的类型
简而言之: 泛型其实就是表示一种未知的数据类型,在使用的时候确定其具体的数据类型
表示方式: <泛型变量>
1.2、泛型的好处:
- 将运行时期的ClassCastException,转移到了编译时期变成了编译失败
- 避免了类型转换的麻烦
1.3、集合不使用泛型:
- 可能会发生类型转换异常
- 避免类型转换异常,就需要先做类型判断,再转型--->比较麻烦
public class Test1 {
public static void main(String[] args) {
// 注意: 不指定集合元素的类型,元素的类型默认是Object类型
// 创建ArrayList集合对象,不指定泛型的具体数据类型
ArrayList list = new ArrayList();
// 往集合中添加元素
list.add("itheima");
list.add(666);
list.add(3.14);
list.add(true);
list.add("itcast");
System.out.println("list:"+list);// list:[itheima, 666, 3.14, true, itcast]
// 获取集合中所有字符串元素的长度
for (Object obj : list) {
// String str = (String) obj;// 发生类型转换异常
// System.out.println(str.length());
// 转换类型之前先判断
if (obj instanceof String){
String str = (String) obj;
System.out.println(str.length());
}
}
}
}
1.4、集合使用泛型
- 概述:指定泛型的具体数据类型----->(只能是引用数据类型)
public class Test2{
public static void main(String[] args) {
// 注意: 使用集合的时候,建议通过泛型来限制集合元素类型一致
// 创建ArrayList集合对象,指定泛型的具体数据类型为String
ArrayList<String> list = new ArrayList<>();
// 往集合中添加元素
list.add("itheima");
// list.add(666);// 编译报错,把运行时的类型转换异常,转移到了编译时期变成了编译失败
// list.add(3.14);// 编译报错,把运行时的类型转换异常,转移到了编译时期变成了编译失败
// list.add(true);// 编译报错,把运行时的类型转换异常,转移到了编译时期变成了编译失败
list.add("itcast");
System.out.println("list:"+list);// list:[itheima, itcast]
// 获取集合中所有字符串元素的长度
for (String s : list) {
System.out.println(s.length());
}
}
}
2、定义和使用泛型的类
2.1、定义含有泛型的类
格式:
public class 类名<泛型变量>{
}
泛型变量: 可以是任意字母,eg: A,B,E,...a,b,....;一般写E
// 含有泛型的类
public class MyGenericClass1<E> {
E e;
public void method1(E e) {
System.out.println("e的类型:" + e.getClass());
}
public E method2(E e){
return e;
}
}
2.2、使用含有泛型的类
public class Test {
public static void main(String[] args) {
// 使用含有泛型的类
// 创建含有泛型的类的对象
MyGenericClass1<String> mc1 = new MyGenericClass1<>();
mc1.method1("itheima");
// 创建含有泛型的类的对象
MyGenericClass1<Integer> mc2 = new MyGenericClass1<>();
mc2.method1(100);
}
}
3、定义和使用泛型的方法
3.1、定义含有泛型的方法
- 格式:
修饰符 <泛型变量> 返回值类型 方法名(形参列名){
方法体
}
// 泛型变量:可以写任意字母,一般写T
3.2、使用含有泛型的方法
- 调用含有泛型方法的时候,确定泛型的具体数据类型
- 案例:
public class Test {
public static void main(String[] args) {
// 使用含有泛型的方法:调用含有泛型的方法,指定泛型的具体数据类型
MyGenericMethod mg = new MyGenericMethod();
// 调用method1,指定泛型的具体类型为Integer
Integer i = mg.method1(100);
System.out.println("i:"+i);// i:100
// 调用method1,指定泛型的具体类型为String
String str = mg.method1("itheima");
System.out.println("str:"+str);// str:itheima
}
}
4、定义和使用泛型的接口
4.1、定义含有泛型的接口
- 格式:
- public interface 接口名<泛型变量>{
}
// 泛型变量:可以是任意字母,一般写E
// 含有泛型的接口
public interface MyGenericInterface<E> {
void method1(E e);
public default E method2(E e){
return e;
}
}
4.2、使用含有泛型的接口**
- 方式一: 实现类实现接口的时候,确定接口泛型的具体数据类型
- 格式:
- public class 类名 implements 接口名<具体的引用数据类型>{}
// 实现接口的时候,确定接口泛型的具体数据类型
class Imp implements MyGenericInterface<String>{
@Override
public void method1(String s) {
System.out.println("method1.."+s);
}
@Override
public String method2(String s) {
System.out.println("method2..."+s);
return s;
}
}
方式二:
实现类实现接口的时候,不确定接口泛型的具体数据类型,而是创建实现类对象的时候确定泛型的具体数据类型
- 格式:
- public class 类名<泛型变量> implements 接口名<泛型变量>{}
// 实现类实现接口的时候,不确定接口泛型的具体数据类型,而是创建实现类对象的时候确定泛型的具体数据类型
class Imp2<E> implements MyGenericInterface<E>{
@Override
public void method1(E e) {
System.out.println("e的类型:"+e.getClass());
}
@Override
public E method2(E e) {
System.out.println("e的类型:"+e.getClass());
return e;
}
}
public class Test {
public static void main(String[] args) {
// 接口: 被实现
// 创建实现类对象的时候确定泛型的具体数据类型
Imp2<String> i1 = new Imp2<>();
i1.method1("itheima");
String res1 = i1.method2("itcast");
System.out.println("-----");
Imp2<Integer> i2 = new Imp2<>();
i2.method1(100);
Integer res2 = i2.method2(200);
}
}
5、泛型通配符
5.1、 概述: 泛型通配符用问号表示(?)
5.2、为什么需要泛型通配符:
- 泛型本身不存在继承关系,不可以给已指定泛型的变量接收有其他泛型类型的对象
- `Collection<Object> list = new ArrayList<String>(); //错误格式,泛型不存在继承关系`
- 如果想要使变量在未来接收有泛型定义的对象,又不确定泛型要定义的类型可以使用泛型通配符
- `Collection<?> list 变量接收`
5.3、通配符基本使用
- 格式: `数据类型<?> 变量`
- 注意:
- 如果使用了泛型通配符,那么该集合变量元素类型默认是Object类型
- 如果使用了泛型通配符,那么该集合变量只能取元素,无法增删元素
public class Test {
public static void main(String[] args) {
// Integer继承Number,Number继承Object,String继承Object
ArrayList<Object> list1 = new ArrayList<>();
ArrayList<String> list2 = new ArrayList<>();
ArrayList<Integer> list3 = new ArrayList<>();
ArrayList<Number> list4 = new ArrayList<>();
// 泛型本身不存在继承关系,不可以给已指定泛型的变量接收有其他泛型类型的对象
// method1(list1);// 编译报错
// method1(list2); // 正确
// method1(list3);// 编译报错
// method1(list4);// 编译报错
method2(list1);
method2(list2);
method2(list3);
method2(list4);
}
// 定义一个方法,可以接收任意ArrayList集合对象
// 如果想要使变量在未来接收有泛型定义的对象,又不确定泛型要定义的类型可以使用泛型通配符
public static void method2(ArrayList<?> list) {// 已指定泛型的变量
System.out.println("执行了");
// 注意: 如果使用了泛型通配符,那么该集合元素类型默认是Object类型
for (Object obj : list) {
}
// 注意: 如果使用了泛型通配符,那么该集合变量只能取元素,无法增加元素
// list.add(1000);// 编译报错
list.remove(1000);
}
public static void method1(ArrayList<String> list) {// 已指定泛型的变量
}
}
5.4、通配符高级使用----受限泛型
- 上限:
- 格式: `<? extends 类名>`
- 表示: 只接受该类类型或者其子类类型
- 下限:
- 格式: `<? super 类名>`
- 表示: 只接受该类类型或者其父类类型
public class Test {
public static void main(String[] args) {
// Integer继承Number,Number继承Object,String继承Object
ArrayList<Object> list1 = new ArrayList<>();
ArrayList<String> list2 = new ArrayList<>();
ArrayList<Integer> list3 = new ArrayList<>();
ArrayList<Number> list4 = new ArrayList<>();
method1(list1);
//method1(list2);// 编译报错
method1(list3);
method1(list4);
//method2(list1);// 编译报错
//method2(list2);// 编译报错
method2(list3);
method2(list4);
}
// 定义一个方法,只可以接收泛型是Integer或者其父类类型的ArrayList集合对象
public static void method1(ArrayList<? super Integer> list){
list.add(100);
}
// 定义一个方法,只可以接收泛型是Number或者其子类类型的ArrayList集合对象
public static void method2(ArrayList<? extends Number> list){
}
}