day02 Collection 泛型笔记 今天我们来学习集合类Collection和泛型的相关知识。
以下是今天的学习目标:
- 能够说出集合与数组的区别
- 说出Collection集合的常用功能
- 能够使用迭代器对集合进行取元素
- 能够使用集合存储自定义类型
- 能够使用foreach循环遍历集合
- 能够使用泛型定义集合对象
- 能够理解泛型上下限
- 能够阐述泛型通配符的作用
以下是今天的详细笔记:
Collection集合概述
集合: 长度可变容器, 可以存储多个对象
集合和数组的区别:
1. 数组长度不可变; 集合长度可变
2. 数组可以存基本类型或引用类型, 只能存同一种类型; 集合只能存储引用类型元素, 可以是多种类型元素
集合框架介绍
Collection接口: 单列集合顶层
|_ List接口: 元素存取有序, 可重复, 有索引
|_ Set接口: 元素存取无序, 不可重复, 无索引
ArrayList<String> add("aaaa") add("bbb") add("ccc") add("ccc")
"aaaa" "bbb" "ccc"
HashSet<String> add("aaaa") add("bbb") add("ccc")
"bbb" "ccc" "aaaa"
学习方法:
学习顶层: 顶层接口/抽象类中共性的方法, 所有子类都可以使用
使用底层: 使用底层子类/实现类创建对象
Collection常用功能
java.util.Collection接口:
// 成员方法(子类都会实现)
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接口中定义的方法, 不会出现子类特有方法
5分钟练习: 测试Collection接口的方法
需求:
通过多态方式创建Collection对象, 实现类是ArrayList
在集合中存储一些字符串
测试以下Collection接口中的方法:
boolean add(E e): 把给定的对象添加到当前集合中
void clear(): 清空集合中所有的元素
boolean remove(E e): 把给定的对象在当前集合中删除
boolean contains(E e): 判断当前集合中是否包含给定的对象
boolean isEmpty(): 判断当前集合是否为空(没有元素)
int size(): 返回集合中元素的个数
Object[] toArray(): 把集合中的元素,存储到数组中
代码:
[Java] 纯文本查看 复制代码 import java.util.ArrayList;
import java.util.Collection;
/*
需求:
通过多态方式创建Collection对象, 实现类是ArrayList
在集合中存储一些字符串
测试以下Collection接口中的方法:
boolean add(E e): 把给定的对象添加到当前集合中
void clear(): 清空集合中所有的元素
boolean remove(E e): 把给定的对象在当前集合中删除
boolean contains(E e): 判断当前集合中是否包含给定的对象
boolean isEmpty(): 判断当前集合是否为空(没有元素)
int size(): 返回集合中元素的个数
Object[] toArray(): 把集合中的元素,存储到数组中
*/
public class Test {
public static void main(String[] args) {
// 多态创建集合对象
Collection<String> coll = new ArrayList<>();
// boolean add(E e): 把给定的对象添加到当前集合中
coll.add("张飞");
coll.add("关羽");
coll.add("赵云");
// void clear(): 清空集合中所有的元素
// coll.clear();
// boolean remove(E e): 把给定的对象在当前集合中删除
boolean b1 = coll.remove("张飞");
System.out.println("删除张飞:" + b1);
boolean b2 = coll.remove("刘备");
System.out.println("删除刘备:" + b2);
// boolean contains(E e): 判断当前集合中是否包含给定的对象
boolean contains1 = coll.contains("张飞");
System.out.println("包含张飞:" + contains1);
boolean contains2 = coll.contains("刘备");
System.out.println("包含刘备:" + contains2);
// boolean isEmpty(): 判断当前集合是否为空(没有元素)
boolean isEmpty = coll.isEmpty();
System.out.println("集合是否为空:" + isEmpty);
// int size(): 返回集合中元素的个数
int size = coll.size();
System.out.println("集合长度:" + size);
// Object[] toArray(): 把集合中的元素,存储到数组中
Object[] arr = coll.toArray();
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
System.out.println(coll);
}
}
迭代器迭代器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();
}
5分钟练习: 使用迭代器遍历集合需求:
通过多态方式创建Collection对象, 实现类是ArrayList
向集合中添加以下人名:
"姚明", "科比", "麦迪", "詹姆斯", "艾弗森"
获取集合的迭代器对象, 并遍历出元素, 打印出来
代码:
[Java] 纯文本查看 复制代码 /*
需求:
通过多态方式创建Collection对象, 实现类是ArrayList
向集合中添加以下人名:
"姚明", "科比", "麦迪", "詹姆斯", "艾弗森"
获取集合的迭代器对象, 并遍历出元素, 打印出来
*/
public class Test {
public static void main(String[] args) {
// 多态创建集合对象
Collection<String> coll = new ArrayList<>();
// 添加元素
coll.add("姚明");
coll.add("科比");
coll.add("麦迪");
coll.add("詹姆斯");
coll.add("艾弗森");
// 获取迭代器对象
Iterator<String> iterator = coll.iterator();
while (iterator.hasNext()) {
String s = iterator.next();
System.out.println(s);
}
}
}
迭代器的实现原理
集合和迭代器对象的关系:
每个集合都有对应的一个迭代器对象
迭代器的原理:
迭代器有一个指针(其实就是个变量保存索引值), 最初指向集合的 -1 索引
hasNext() 方法可以判断下一个索引是否有元素
next() 方法移动指针到下一个索引, 并返回元素
补充:
注意事项:
1. 迭代器对象迭代完毕后, 指针已经指向最后一个元素, 没有下一个元素了. 如果想再次从头遍历集合, 要获取新的迭代器对象
2. 在使用迭代器迭代的过程中, 如果执行了改变集合长度的操作 (如add(), remove(), clear()), 则会抛出 ConcurrentModificationException 并发修改异常. 如果要在迭代的过程中添加, 删除元素, 要使用迭代器自带的方法, 而不能使用集合的方法
增强for循环
增强for: 也称foreach循环, JDK 5出现, 用于遍历集合, 底层采用迭代器
作用:
遍历数组
遍历集合
增强for格式:
[Java] 纯文本查看 复制代码 for(元素的数据类型 变量名 : Collection集合或数组名){
//操作代码
}
// 数组示例
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:
优点: 有索引可以使用, 某些方式可以在遍历过程中增删元素
缺点: 格式繁琐
应用场景: 需要用到索引的场景
迭代器:
优点: 可以使用迭代器对象的方法操作元素
缺点: 格式繁琐
应用场景: 需要在迭代过程中增删元素的场景
5分钟练习: 增强for遍历集合
需求:
通过多态方式创建Collection对象, 实现类是ArrayList
向集合中添加以下人名:
"姚明", "科比", "麦迪", "詹姆斯", "艾弗森"
用增强for遍历出元素, 打印出来
代码:
[Java] 纯文本查看 复制代码 import java.util.ArrayList;
import java.util.Collection;
/*
需求:
通过多态方式创建Collection对象, 实现类是ArrayList
向集合中添加以下人名:
"姚明", "科比", "麦迪", "詹姆斯", "艾弗森"
用增强for遍历出元素, 打印出来
*/
public class Test {
public static void main(String[] args) {
// 多态创建集合对象
Collection<String> coll = new ArrayList<>();
// 添加元素
coll.add("姚明");
coll.add("科比");
coll.add("麦迪");
coll.add("詹姆斯");
coll.add("艾弗森");
// 使用增强for遍历元素
for (String s : coll) {
System.out.println(s);
}
// 增强for的IDEA快捷键: 集合/数组名.for
}
}
泛型泛型的概述以及不使用泛型产生的问题
泛型: Generic. JDK 5 增加. 是一种未知的数据类型
定义集合时, 某些方法不知道使用什么类型时, 就可以使用泛型
创建集合对象时, 需要确定泛型具体的类型
泛型可以看作是一个变量, 用来接收数据类型
泛型的好处
不使用泛型的问题:
集合实际存储的是 Object 类型, 存入的元素无论是什么类型, 都会被提升为 Object, 取出来的也是 Object, 要想调用元素特有方法, 就要向下转型, 有可能发生类型转换异常 ClassCastException
泛型的好处:
1. 避免了类型转换的麻烦
2. 将运行时的类型转换异常, 转移到了编译时期 (有利于程序员提前发现问题)
5分钟练习: 不使用泛型存储元素
需求:
通过多态方式创建Collection对象, 实现类是ArrayList, 不加泛型
添加元素:
123, "你好", true
使用增强for循环遍历出元素并打印出来
代码:
[Java] 纯文本查看 复制代码 /*
需求:
通过多态方式创建Collection对象, 实现类是ArrayList, 不加泛型
添加元素:
123, "你好", true
使用增强for循环遍历出元素并打印出来
*/
public class Test {
public static void main(String[] args) {
// 多态创建集合对象, 不加泛型
Collection coll = new ArrayList();
coll.add(123);
coll.add("你好");
coll.add(true);
for (Object o : coll) {
System.out.println(o);
}
}
}
定义和使用含泛型的类
定义泛型
<泛型名>
泛型的定义和使用
泛型在定义时, 不能是具体的类型, 只是一个变量名
泛型在使用时, 必须是具体的数据类型
// 带有泛型的类定义格式
修饰符 class 类名<代表泛型的名字> { // 泛型的变量一般用一个大写字母表示, 但也可以是多个字母
}
类中泛型在创建对象时, 确定泛型的具体类型
5分钟练习: 定义带有泛型的类
需求:
定义类: GenericClass, 带有泛型 E
私有成员变量: E name;
提供 set/get方法
定义测试类
创建GenericClass对象, 将泛型替换为Integer, 调用setName()设置值并调用getName()获取值并打印
创建GenericClass对象, 将泛型替换为String, 调用setName()设置值并调用getName()获取值并打印
代码:
[Java] 纯文本查看 复制代码 /*
定义类: GenericClass, 带有泛型 E
私有成员变量: E name;
提供 set/get方法
*/
public class GenericClass<E> { // 类上声明了泛型, 类中的所有方法和成员变量都可以使用该泛型
private E name; // 成员变量的类型用泛型
public E getName() { // 成员方法的返回值类型用泛型
return name;
}
public void setName(E name) { // 成员方法的参数类型用泛型
this.name = name;
}
}
/*
需求:
定义类: GenericClass, 带有泛型 E
私有成员变量: E name;
提供 set/get方法
定义测试类
创建GenericClass对象, 将泛型替换为Integer, 调用setName()设置值并调用getName()获取值并打印
创建GenericClass对象, 将泛型替换为String, 调用setName()设置值并调用getName()获取值并打印
*/
public class Test {
public static void main(String[] args) {
// 创建对象, 泛型为Integer
GenericClass<Integer> gc1 = new GenericClass<>();
gc1.setName(1);
Integer name1 = gc1.getName();
System.out.println(name1);
// 创建对象, 泛型为String
GenericClass<String> gc2 = new GenericClass<>();
gc2.setName("这是字符串");
String name2 = gc2.getName();
System.out.println(name2);
}
}
定义含有泛型的方法与使用
方法中的泛型定义位置:
修饰符 和 返回值类型 之间
// 带有泛型的方法定义格式
修饰符 <代表泛型的名字> 返回值类型 方法名(参数){
}
方法中定义泛型后, 返回值类型和参数类型都可以使用泛型
方法泛型的使用:
在调用方法时确定泛型的具体类型
5分钟练习: 在方法中定义泛型并使用
需求:
定义类: GenericMethod
定义成员方法:
public <M> void method(M m) {
System.out.println(m);
}
定义静态方法:
public static <S> void staticMethod(S s) {
System.out.println(s);
}
定义测试类:
创建GenericMethod对象
调用method()方法, 传递10
调用method()方法, 传递"abc"
调用staticMethod()方法, 传递"静态方法"
代码:
[Java] 纯文本查看 复制代码 public class GenericMethod {
public <M> void method(M m) { // 方法上声明的泛型, 只能在方法内部使用
// M m2 = null; // 局部变量也可以
System.out.println(m);
}
public static <S> void staticMethod(S s) {
System.out.println(s);
}
// 方法返回值也可以使用方法声明的泛型
public <P> P getP(P p) {
return p;
}
}
public class Test {
public static void main(String[] args) {
// 创建GenericMethod对象
GenericMethod gm = new GenericMethod();
// 调用方法时, 根据参数类型确定具体的类型
gm.method(10);
gm.method("abc");
GenericMethod.staticMethod("静态方法");
String result = gm.getP("abc");
System.out.println(result);
}
}
定义与使用含有泛型的接口
定义泛型接口与定义泛型类一样
// 带有泛型的类定义格式
修饰符 interface接口名<代表泛型的变量> {
}
实现类实现了泛型接口后可以有2种选择:
1. 定义实现类时就确定泛型的具体类型
2. 定义实现类时仍然沿用泛型, 直到创建该类对象时才确定泛型的具体类型
补充:
[Java] 纯文本查看 复制代码 /*
定义含有泛型的接口
*/
public interface GenericInterface<I> {
public abstract void method(I i);
}
/*
含有泛型的接口,第一种使用方式:定义接口的实现类,实现接口,指定接口的泛型
public interface Iterator<E> {
E next();
}
Scanner类实现了Iterator接口,并指定接口的泛型为String,所以重写的next方法泛型默认就是String
public final class Scanner implements Iterator<String>{
public String next() {}
}
*/
public class GenericInterfaceImpl1 implements GenericInterface<String>{
@Override
public void method(String s) {
System.out.println(s);
}
}
/*
含有泛型的接口第二种使用方式:接口使用什么泛型,实现类就使用什么泛型,类跟着接口走
就相当于定义了一个含有泛型的类,创建对象的时候确定泛型的类型
public interface List<E>{
boolean add(E e);
E get(int index);
}
public class ArrayList<E> implements List<E>{
public boolean add(E e) {}
public E get(int index) {}
}
*/
public class GenericInterfaceImpl2<I> implements GenericInterface<I> {
@Override
public void method(I i) {
System.out.println(i);
}
}
/*
测试含有泛型的接口
*/
public class Demo04GenericInterface {
public static void main(String[] args) {
//创建GenericInterfaceImpl1对象
GenericInterfaceImpl1 gi1 = new GenericInterfaceImpl1();
gi1.method("字符串");
//创建GenericInterfaceImpl2对象
GenericInterfaceImpl2<Integer> gi2 = new GenericInterfaceImpl2<>();
gi2.method(10);
GenericInterfaceImpl2<Double> gi3 = new GenericInterfaceImpl2<>();
gi3.method(8.8);
}
}
泛型定义总结:
定义在类上的泛型:
有效范围: 整个类中都有效
何时确定具体类型: 创建该类对象时确定泛型的具体类型
定义在方法上的泛型:
有效范围: 方法中(包括返回值类型和参数类型)有效
何时确定具体类型: 调用方法传参时确定泛型的具体类型
定义在接口上的泛型:
有效范围: 接口中
何时确定具体类型:
1. 子接口或实现类定义时确定泛型的具体类型
2. 创建实现类对象时确定泛型的具体类型
泛型通配符
泛型通配符:
不知道使用什么类型来接收时, 此时可以使用 <?> 来表示未知通配符
示例: List<?> list 接收泛型是任意类型的List对象
注意: 只能接收数据, 不能调用方法存储元素
List<?> list 这种集合不能调用 add() 添加元素, 只能调用 get() 获取元素
List<?> list 其实是一个变量, 所以可以将一个集合赋值给它
如: List<?> list = new ArrayList<String>();
使用方式:
不能创建对象使用
只能作为方法参数使用. (减少方法重载)
泛型的上限:
格式: 类型名称<? extends 类名> 对象名称
示例: List<? extends Number> list
作用: 只能接收该类型及其子类 (Number及其子类的泛型)
泛型的下限:
格式: 类型名称<? super 类名> 对象名称
示例: List<? super Number> list
作用: 只能接收该类型及其父类型 (Number及其父类的泛型)
5分钟练习: 泛型通配符
需求:
定义Person类, 不用定义成员
定义Student类, 继承Person类, 不用定义成员
定义测试类
在测试类中, 定义方法:
public static void printExtends(Collection<? extends Person> list) {
// 使用增强for遍历集合中的元素打印出来
}
public static void printSuper(Collection<? super Person> list) {
// 使用迭代器遍历集合元素打印出来
}
创建ArrayList<Person>集合, 添加3个Person对象
创建ArrayList<Student>集合, 添加3个Student对象
分别调用2个方法测试能够传入的集合有哪些
代码:
[Java] 纯文本查看 复制代码 public class Person {
}
public class Student extends Person {
}
/*
需求:
定义Person类, 不用定义成员
定义Student类, 继承Person类, 不用定义成员
定义测试类
在测试类中, 定义方法:
public static void printExtends(Collection<? extends Person> list) {
// 使用增强for遍历集合中的元素打印出来
}
public static void printSuper(Collection<? super Person> list) {
// 使用迭代器遍历集合元素打印出来
}
创建ArrayList<Person>集合, 添加3个Person对象
创建ArrayList<Student>集合, 添加3个Student对象
分别调用2个方法测试能够传入的集合有哪些
*/
public class Test {
public static void main(String[] args) {
// 创建泛型为Person的集合
ArrayList<Person> personList = new ArrayList<>();
personList.add(new Person());
personList.add(new Person());
personList.add(new Person());
// 创建泛型为Student的集合
ArrayList<Student> studentList = new ArrayList<>();
studentList.add(new Student());
studentList.add(new Student());
studentList.add(new Student());
// Collection<? extends Person>
printExtends(personList); // 可以接收泛型为Person类型的集合
printExtends(studentList); // 可以接收泛型为Student类型的集合
// Collection<? super Person>
printSuper(personList); // 可以接收泛型为Person类型的集合
printSuper(studentList); // Student不是Person的父类, 不能接收
}
public static void printExtends(Collection<? extends Person> list) {
// 使用增强for遍历集合中的元素打印出来
for (Person person : list) {
System.out.println(person);
}
}
public static void printSuper(Collection<? super Person> list) {
// 使用迭代器遍历集合元素打印出来
Iterator<? super Person> iterator = list.iterator();
while (iterator.hasNext()) {
Object o = iterator.next();
System.out.println(o);
}
}
}
斗地主练习斗地主: 需求分析
斗地主发牌: 使用54张牌打乱顺序,三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张留作底牌
java.util.Collections工具类
// 静态方法
static void shuffle(List<?> list): 随机打乱集合中元素的顺序
斗地主: 代码实现10分钟练习: 实现斗地主发牌
1. 准备牌:
花色: String[] colors 存储4种花色: {"♠","♥","♣","♦"}
面值: String[] numbers 存储13个序号: {"2","A","K","Q","J","10","9","8","7","6","5","4","3"}
牌盒: ArrayList<String> poker 存储拼接好的牌
添加大小王
拼接牌:
每种花色+每种面值
双层for循环嵌套
装入牌盒
poker.add(...)
2. 洗牌
Collections.shuffle(poker);
3. 发牌
创建4个ArrayList集合, 分别表示3个玩家和底牌
for循环遍历poker集合
如果 索引 >= 51, 存入底牌集合
如果 索引 % 3 == 0, 存入玩家1集合
如果 索引 % 3 == 1, 存入玩家2集合
如果 索引 % 3 == 2, 存入玩家3集合
4. 看牌
打印3个玩家集合和底牌集合
代码:
[Java] 纯文本查看 复制代码 /*
模拟斗地主发牌
*/
public class Test {
public static void main(String[] args) {
// 定义花色和序号
String[] colors = {"♤", "♡", "♣", "♢"};
String[] numbers = {"2", "A", "K", "Q", "J", "10", "9", "8", "7", "6", "5", "4", "3"};
// 创建牌盒
ArrayList<String> poker = new ArrayList<>();
// 添加大小王
poker.add("大王");
poker.add("小王");
// 嵌套循环拼接牌并添加
for (String color : colors) {
for (String number : numbers) {
poker.add(color + number);
}
}
// System.out.println(poker);
// 洗牌
Collections.shuffle(poker);
// System.out.println(poker);
// 发牌
ArrayList<String> player1 = new ArrayList<>();
ArrayList<String> player2 = new ArrayList<>();
ArrayList<String> player3 = new ArrayList<>();
ArrayList<String> diPai = new ArrayList<>();
for (int i = 0; i < poker.size(); i++) {
// 取出一张牌
String pai = poker.get(i);
// 从51开始存入底牌
if (i >= 51) {
// 添加到底牌
diPai.add(pai);
} else if (i % 3 == 0) {
// 给玩家1
player1.add(pai);
} else if (i % 3 == 1) {
// 给玩家2
player2.add(pai);
} else if (i % 3 == 2) {
// 给玩家3
player3.add(pai);
}
}
// 看牌
System.out.println("刘德华:" + player1);
System.out.println("周润发:" + player2);
System.out.println("周星驰:" + player3);
System.out.println("底牌:" + diPai);
}
}
今日API
java.util.Collection<E>接口:
// 成员方法(子类都会实现)
boolean add(E e): 把给定的对象添加到当前集合中
void clear(): 清空集合中所有的元素
boolean remove(E e): 把给定的对象在当前集合中删除
boolean contains(E e): 判断当前集合中是否包含给定的对象
boolean isEmpty(): 判断当前集合是否为空(没有元素)
int size(): 返回集合中元素的个数
Object[] toArray(): 把集合中的元素,存储到数组中
Iterator<E> iterator(): 获取集合的迭代器对象
java.util.Iterator<E>接口: 迭代器
// 成员方法
boolean hasNext(): 判断是否有下一个元素
E next(): 获取下一个元素
void remove(): 删除调用完next()指向的元素
java.util.Collections工具类
// 静态方法
static void shuffle(List<?> list): 随机打乱集合中元素的顺序
|
|