A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 849618121 于 2018-11-26 15:01 编辑

Collection接口单列集合顶层
List接口: 元素存取有序, 可重复, 有索引
Set接口: 不可重复, 无索引

学习方法:
        学习顶层: 顶层接口/抽象类中共性的方法, 所有子类都可以使用
        使用底层: 使用底层子类/实现类创建对象

ArrayList<String> list
list.add("b");
list.add("a");
list.add("c");
list.add("c");
{"b", "a", "c", "c"}

HashSet<String> set
set.add("b");
set.add("a");
set.add("c");
set.add("c");
{"a", "b", "c"}

ArrayList<Integer> list = new ArrayList<>();
Collection<Integer> col = new ArrayList<>();  // 多态, 只在学习时用这种方式创建对象// 为了调用接口中共性的方法, 避免让你调用子类特有方法


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(): 获取集合的迭代器对象 (后面讲到)
    Iterator接口: 迭代器
测试Collection接口的方式:
使用多态方式创建对象: Collection c = new ArrayList();
编译看左边, 这样只能调用Collection接口中定义的方法, 不会出现子类特有方法

迭代器
集合和迭代器对象的关系:
每个集合都有对应的一个迭代器对象

      hasNext() 方法可以判断当前索引是否有元素
      next() 方法返回当前元素, 并移动指针到下一个索引

迭代:
类似于遍历, 判断是否有下一个元素, 有则取出下一个, 直到没有
迭代器:
用于遍历集合的对象
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() 方法, 获取下一个元素

    使用迭代器遍历集合的标准写法:
Iterator<元素类型> iterator = 集合对象.iterator();
while (iterator.hasNext()) {
  元素类型 变量名 = iterator.next();
}
List集合
  List集合体系的特点:
1. 元素存取有序 (存入和取出元素的顺序一致) 321->321  排序: 从小到大
2. 元素可以重复  1 1 1 1
3. 有索引
List子体系中的实现类都具有上述特点
java.util.List接口:
// 常用特有成员方法 (都是按照索引来操作的)
void add(int index, E element): 将指定的元素, 添加到该集合中的指定位置上
E get(int index): 返回集合中指定位置的元素
E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素
E set(int index, E element): 用指定元素替换集合中指定位置的元素, 返回值的更新前的元素
ArrayList集合的特点
ArrayList底层的数据结构:
数组
ArrayList的特点:
查询快
增删慢
线程不安全, 效率高
ArrayList适用场景:
存储的数据"查询多, 增删少"的场景. 如用一个ArrayList存储中国城市名称
LinkedList集合的特点和特有方法
LinkedList底层的数据结构:
链表
LinkedList的特点:
查询慢
增删快
线程不安全, 效率高
LinkedList适用场景:
存储的数据"查询少, 增删多"的场景. 如用LinkedList实现栈或队列
java.util.LinkedList<E>类: 链表结构, 查询慢, 增删快
// 特有成员方法(主要操作开头和末尾元素)
void addFirst(E e): 将指定元素插入此列表的开头
void addLast(E e): 将指定元素添加到此列表的结尾
E getFirst(): 返回此列表的第一个元素
E getLast(): 返回此列表的最后一个元素
E removeFirst(): 移除并返回此列表的第一个元素
E removeLast(): 移除并返回此列表的最后一个元素
E pop(): (其实就是removeFirst())从此列表所表示的栈中弹出一个元素
void push(E e): (其实就是addFirst())将元素添加到此列表所表示的栈中
Vector集合
Vector底层的数据结构:
数组
Vector的特点:
查询慢
增删快
(同步)线程安全, 效率低
Vector目前几乎没人使用



Set集合
Set集合体系特点:
1. 元素不可重复
2. 没有索引
HashSet特点:
1. 元素不可重复
2. 没有索引
3. 元素存取无序 (存入和取出顺序有可能不一致)
4. 底层采用 哈希表 结构. (查询快)
哈希表 = 数组 + 链表或红黑树
java.util.HashSet类:
// 常用方法
boolean add(E e): 添加元素, 根据元素的 hashCode() 和 equals() 方法判断是否重复. 重复则不添
加并返回false, 不重复则添加并返回true
HashSet原理: 哈希值
哈希值:
一个十进制数值, 一般是通过将该对象的内部地址转换成一个整数来实现的
public native int hashCode();
可以调用系统本地代码(C/C++)计算出一个对象地址的哈希值
hashCode()方法的作用
方法内部的算法用于将对象计算为一个哈希值, 便于根据哈希值比较对象是否"相等"
哈希值主要是为了提高对象存储在 哈希表 中的效率
注意:
1. 如果我们不满意Object中的哈希值计算方法, 可以重写hashCode()方法.
   但在Java代码中不能直接重写带有 native 的方法, 重写时应该将 native 去掉
@Override
public int hashCode() {}
2. hashCode() 方法有可能将"不同的对象"计算出"相同的哈希值", 这称为"哈希冲突", 在出现冲突后, 一
般再通过 equals() 方法来继续判断对象是否"相等"

哈希表:
JDK 8以前  : 哈希表 = 数组 + 链表
JDK 8及之后 : 哈希表 = 数组 + 链表或红黑树
数组中存储的每个元素, 是哈希值相同的一组节点的链表或红黑树

HashSet集合保证添加元素不重复的原理:
调用 add(E e) 添加元素时, 先调用 hashCode() 获取哈希值, 和当前HashSet集合中的元素比较
如果哈希值不同, 则认为元素不重复, 添加, 并返回true
如果哈希值相同, 则可能是哈希冲突, 所以继续调用元素的 equals() 方法和所有哈希值相同的元素比较
如果 equals() 比较所有元素都没有相同的, 则认为元素不重复, 添加, 并返回true
如果 equals() 比较出有相同的元素, 则认为元素重复, 不添加, 并返回false

自定义JavaBean对象实现在HashSet中去重:
JavaBean默认继承Object类中的 hashCode() 和 equals() 方法, 都是根据对象地址值判断是否重复的
要根据属性值判断是否重复, 应该在JavaBean类中重写 hashCode() 和 equals() 方法, 使其按照属性值
比较

LinkedHashSet集合
LinkedHashSet特点:
1. 元素存取有序 (存入和取出顺序一致)
2. 元素不可重复
3. 没有索引
LinkedHashSet底层数据结构:
哈希表 + 链表  (也就是: 数组 + 链表或红黑树 + 链表)
其中, 哈希表用于存储数据, 额外的链表用于记录元素添加时的先后顺序, 以便在获取元素时保持顺序一致
总结: 什么时候用List, 什么时候用Set?
要存储的元素可以重复的, 用List集合:
增删少, 用ArrayList
增删多, 用LinkedList
要存储的数据要求不重复, 或者相对一个集合去重, 用Set集合:
不要求存取顺序一致, 用HashSet
要求存取顺序一致, 用LinkedHashSet
可变参数




可变参数:
JDK 5 出现. 指同一个类型的参数, "个数可变"
可变参数的本质就是一个"数组"
格式: 用在方法的参数中
修饰符 返回值类型 方法名(int... 变量名) {
// 可以直接将 变量名 当作 数组名 使用
}
方法名();
注意事项:
1. 可变参数可以传递的参数个数, 可以是 0个, 1个, 多个
2. 一个方法的参数列表中, 只能有一个可变参数
3. 如果方法的参数有多个, 可变参数必须写在参数列表的最后


Collections集合工具类addAll(), shuffle()
java.util.Collections类: 操作集合的工具类
// 静态方法
static <T> boolean addAll(Collection<? super T> c, T... elements):往集合中添加一些元素
static void shuffle(List<?> list): 打乱集合顺序
static <T> void sort(List<T> list): 将集合中元素按照默认规则排序
static <T> void sort(List<T> list,Comparator<? super T> c):将集合中元素按照指定规则排序
Collections集合工具类: sort(List list)
sort(List<T> list): 默认按照"升序"将元素排序
数字, 字母, 都可以按照升序排序
自定义JavaBean对象默认不能排序, 因为不知道如何比较哪个对象大, 哪个对象小
自定义JavaBean对象要想排序, 需要实现 Comparable<E> 接口, 重写 int compareTo(E e) 方法
规则:
this-参数: 升序(从小到大)
参数-this: 降序(从大到小)
Collections集合工具类: sort(List list,Comparator<? super T> )
Comparable接口和Comparator接口区别
Comparable: 让JavaBean自身具有可比较性 (自己和其他人比)
Comparator: 定义一个比较器类, 用比较器对象比 (让第三个人来帮两个人比较)
Comparator使用方式:
1. 定义类实现Comparator<E>接口, 重写 int compare(E o1, E o2) 方法, 泛型为比较元素的类型
规则:
      o1-o2: 升序(从小到大)
      o2-o1: 降序(从大到小)
2. 在Collections.sort(List<T> list,Comparator<? super T> c)方法中传入自定义比较器对象

今日API
java.util.List<E>接口:
// 常用特有成员方法 (都是按照索引来操作的)
public void add(int index, E element): 将指定的元素, 添加到该集合中的指定位置上
public E get(int index): 返回集合中指定位置的元素
public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素
public E set(int index, E element): 用指定元素替换集合中指定位置的元素, 返回值的更新前的元素
java.util.LinkedList<E>类: 链表结构, 查询慢, 增删快
// 特有成员方法(主要操作开头和末尾元素)
void addFirst(E e): 将指定元素插入此列表的开头
void addLast(E e): 将指定元素添加到此列表的结尾
E getFirst(): 返回此列表的第一个元素
E getLast(): 返回此列表的最后一个元素
E removeFirst(): 移除并返回此列表的第一个元素
E removeLast(): 移除并返回此列表的最后一个元素
E pop(): (其实就是removeFirst())从此列表所表示的堆栈处弹出一个元素 栈stack
void push(E e): (其实就是addFirst())将元素推入此列表所表示的堆栈
java.util.Set<E>接口:
// 成员方法
boolean add(E e): 添加元素. 元素不重复则添加成功, 返回true; 否则返回false
java.util.Collections类: 操作集合的工具类
// 静态方法
static <T> boolean addAll(Collection<T> c, T... elements):往集合中添加一些元素
static void shuffle(List<?> list) :打乱集合顺序
static <T> void sort(List<T> list) :将集合中元素按照默认规则排序
static <T> void sort(List<T> list,Comparator<? super T> c):将集合中元素按照指定规则排序





day02 Collection 泛型笔记 今日内容介绍
Collection单列集合
        共性方法
        迭代器
        增强for
泛型
斗地主发牌练习
Collection
集合概述知识点:
什么是集合
集合和数组有哪些区别
总结:
集合: 长度可变容器, 可以存储多个对象集合和数组的区别:
        1. 数组长度不可变; 集合长度可变
        2. 数组可以存基本类型或引用类型, 只能存同一种类型; 集合只能存储引用类型元素, 可以是多种类型元素
补充: 集合框架介绍知识点:
集合的整个框架大致如何分类
如何学习集合框架
总结:
Collection接口: 单列集合顶层
        |_ List接口: 元素存取有序, 可重复, 有索引
        |_ Set接口: 不可重复, 无索引ArrayList<String> list
        list.add("b");
        list.add("a");
        list.add("c");
        list.add("c");
        {"b", "a", "c", "c"}

HashSet<String> set
        set.add("b");
        set.add("a");
        set.add("c");
        set.add("c");
        {"a", "b", "c"}学习方法:
        学习顶层: 顶层接口/抽象类中共性的方法, 所有子类都可以使用
        使用底层: 使用底层子类/实现类创建对象
       
       
        ArrayList<Integer> list = new ArrayList<>();
        Collection<Integer> col = new ArrayList<>();  // 多态, 只在学习时用这种方式创建对象
        // 为了调用接口中共性的方法, 避免让你调用子类特有方法
![](./img/01_集合框架介绍.bmp)![](./img/单列集合体系.bmp)补充: Collection常用功能知识点:
Collection接口中定义了哪些方法, 分别有什么作用// 多态创建对象: 父类引用指向子类对象
ArrayList list = new ArrayList();
list.共有方法();
list.特有方法();Collection<String> coll = new ArrayList<>();
// 多态调用方法
coll.共有方法();
总结:
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(): 获取集合的迭代器对象 (后面讲到)
        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(): 把集合中的元素,存储到数组中
代码:
public class Test {
    public static void main(String[] args) {
        // 使用多态创建Collection对象, 帮助我们区分哪些方法是Collection接口中的共性方法
        Collection<String> coll = new ArrayList<>();        // boolean add(E e): 把给定的对象添加到当前集合中
        coll.add("张三");
        coll.add("李四");
        coll.add("王五");
        coll.add("赵六");
        System.out.println(coll);        // void clear(): 清空集合中所有的元素
//        coll.clear();
        System.out.println(coll);        // boolean remove(E e): 把给定的对象在当前集合中删除
        boolean b = coll.remove("李四");
        System.out.println("remove删除李四的返回值:" + b);
        System.out.println(coll);        // boolean contains(E e): 判断当前集合中是否包含给定的对象
        boolean b2 = coll.contains("王五");
        System.out.println("包含王五的返回值:" + b2);        // boolean isEmpty(): 判断当前集合是否为空(没有元素)
        boolean isEmpty = coll.isEmpty();
        System.out.println("集合isEmpty的结果:" + 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);
        }
    }
}
迭代器
迭代器Iterator接口介绍和迭代步骤知识点:
什么是迭代
什么是迭代器
如何获取一个集合的迭代器对象
使用迭代器对象遍历出集合中的元素有哪3步
总结:
迭代:
        类似于遍历, 判断是否有下一个元素, 有则取出下一个, 直到没有
迭代器:
        用于遍历集合的对象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() 方法, 获取下一个元素
补充: 使用迭代器的代码实现知识点:
如何按照迭代器的步骤编写代码遍历集合
总结:
// 使用迭代器遍历集合的标准写法:
Iterator<元素类型> iterator = 集合对象.iterator();
while (iterator.hasNext()) {
    元素类型 变量名 = iterator.next();
}
补充:
注意事项:
        迭代器对象迭代完毕后, 指针已经指向最后一个元素, 没有下一个元素了. 如果想再次从头遍历集合, 要获取新的迭代器对象
5分钟练习: 使用迭代器遍历集合
需求:
通过多态方式创建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()) {
            // 通过next()方法获取元素
            String s = iterator.next();
            System.out.println(s);
        }
    }
}
迭代器的实现原理知识点:
集合和迭代器有什么关系
迭代器的 hasNext(), next() 为什么要获取"下一个"元素?
总结:
集合和迭代器对象的关系:
        每个集合都有对应的一个迭代器对象
       
迭代器的原理:
        迭代器有一个指针(其实就是个变量, 保存索引值), 最初指向集合的 0 位置
        hasNext() 方法可以判断当前索引是否有元素
        next() 方法返回当前元素, 并移动指针到下一个索引
![](./img/02_迭代器的实现原理.bmp)补充: 迭代器的并发修改异常知识点:
使用迭代器进行迭代的过程中, 能否执行修改集合的长度的操作?
如何避免出现 ConcurrentModificationException 并发修改异常?
总结:
并发修改异常的原因:
        在使用迭代器迭代的过程中, 如果执行了改变集合长度的操作 (如 add(), remove(), clear()), 则会抛出 ConcurrentModificationException 并发修改异常. 如何避免:
        1. 迭代过程中不要修改集合长度
        2. 在迭代的过程中添加, 删除元素, 要使用迭代器自带的方法, 而不能使用集合的方法
                Iterator中的 remove() 方法
                ListIterator中的 add(), remove() 方法
补充: 增强for循环知识点:
增强for循环有什么作用
增强for循环底层使用什么实现
增强for循环的格式是什么
总结:
增强for:
        也称foreach循环, JDK 5出现, 用于遍历集合, 底层采用迭代器作用:
        遍历数组
        遍历集合增强for格式:    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遍历出元素, 打印出来
代码:
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
        for (String s : coll) {
            System.out.println(s);
        }
    }
}
泛型
泛型的概述以及不使用泛型产生的问题知识点:
什么是泛型
总结:
泛型: Generic Type. JDK 5 增加. 是一种未知的数据类型
        定义集合时, 某些方法不知道使用什么类型时, 就可以使用泛型
        创建集合对象时, 需要确定泛型具体的类型
       
泛型可以看作是一个"变量", 用来接收数据类型int a = 10;
a = 20;定义:
<E>: 声明了一个泛型 (声明了一个变量)
E: 使用E这种类型作为数据类型  (参数数据类型, 返回值类型)
使用:
ArrayList<Integer>: 确定了泛型的具体类型public class ArrayList<E> {   // E可以表示任意的引用类型, 但是只能表示某一种
        String name;
        int age;
        E e;
        public boolean add(E e){}  String
        public E get(int index){}
}ArrayList<String> list = new ArrayList<>();
ArrayList<Integer> list = new ArrayList<>();

// 如果没有泛型, 想设计添加指定类型的集合, 就不好实现:
// 添加String元素的集合
public class ArrayList1 {
        public boolean add(String e){}
        public String get(int index){}
}
// 添加Student元素的集合
public class ArrayList2 {
        public boolean add(Student e){}
        public Student get(int index){}
}
// 即便可以使用Object作为参数, 但这样任意类型元素都能添加, 却无法限制只能添加某一种特定类型的元素
public class ArrayList {  
        public boolean add(Object e){}
        public Object get(int index){}
}
// 遍历的时候强转类型就可能出问题
for (int i = 0; i < list.size(); i++) {
    Object o = list.get(i);
    String s = (String)o;  // 万一取出的元素不是String呢?
}
补充: 泛型的好处知识点:
如果集合不使用泛型, 存储的是什么类型的元素
集合不使用泛型会有什么安全隐患
总结:
不使用泛型的问题:
        集合实际存储的是 Object 类型, 存入的元素无论是什么类型, 都会被提升为 Object, 取出来的也是 Object, 要想调用元素特有方法, 就要向下转型, 有可能发生类型转换异常 ClassCastException泛型的好处:
        1. 避免了类型转换的麻烦
        2. 将运行时的类型转换异常, 转移到了编译时期 (有利于程序员提前发现问题)
补充: 定义和使用含泛型的类知识点:
如何在类的声明上定义泛型
如何在类中使用泛型
何时确定类中泛型的具体类型
总结:
定义泛型
        <泛型名> 泛型的"定义"和"使用"
        泛型在"定义"时, "不能是"具体的类型, 只是一个变量名: public class ArrayList<E> {}
        泛型在"使用"时, "必须是"具体的数据类型 ArrayList<Integer>
       
    // 带有泛型的类定义格式
    修饰符 class 类名<代表泛型的名字> {  // 泛型的变量一般用一个大写字母表示, 但也可以是多个字母    }

类中的泛型, "在创建对象时", 确定泛型的具体类型
补充: 5分钟练习: 定义带有泛型的类
需求:
定义类: GenericClass, 带有泛型 <E>
        私有成员变量: E name;
        提供 set/get方法
定义测试类
        创建GenericClass对象, 将泛型替换为Integer, 调用setName()设置值并调用getName()获取值并打印
        创建GenericClass对象, 将泛型替换为String, 调用setName()设置值并调用getName()获取值并打印
代码:
public class GenericClass<E> {  // 在类名后面声明泛型, 定义在类上的泛型, 整个类中都可以使用

    private E name;                // 泛型声明后, 可以用于成员变量的类型    public E getName() {           // 泛型声明后, 可以用于方法返回值的类型
        return name;
    }    public void setName(E name) {  // 泛型声明后, 可以用于方法参数的类型
        this.name = name;
    }
}public class Test {
    public static void main(String[] args) {
        // 带有泛型的类, 泛型要在"创建该类对象"时, 确定具体的类型
        GenericClass<Integer> gc1 = new GenericClass<>();
        // // 因为泛型确定为了Integer, 所以方法的参数也只能是Integer
        gc1.setName(1);
        // 因为泛型确定为了Integer, 所以方法的返回值也只能是Integer
        Integer name = gc1.getName();
        System.out.println(name);
        GenericClass<String> gc2 = new GenericClass<>();
        gc2.setName("你好");
        String name1 = gc2.getName();
        System.out.println(name1);        // 泛型的好处, 只需要定义一个类, 就可以实现:
        // 让该类只能使用"某一种"具体的类型
    }
}
定义含有泛型的方法与使用知识点:
如何在方法中定义泛型
方法中的泛型何时确定具体类型
总结:
方法中的泛型定义位置:
        修饰符 和 返回值类型 之间
       
        // 带有泛型的方法定义格式
    修饰符 <代表泛型的名字> 返回值类型 方法名(参数){    }

方法中定义泛型后, 返回值类型和参数类型都可以使用泛型方法泛型的使用:
        "在调用方法时"确定泛型的具体类型
补充: 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()方法, 传递"静态方法"
代码:
public class GenericMethod {    // 定义成员方法:
    public <M> void method(M m) {  // 方法中定义的泛型, 只在方法内部有效
        System.out.println(m);
    }    // 定义静态方法:
    public static <S> void staticMethod(S s) {
        System.out.println(s);
    }
}public class Test {
    public static void main(String[] args) {
        // 创建对象调用方法
        GenericMethod gm = new GenericMethod();        // 调用成员方法
        // 方法的泛型, 在调用方法传递参数时, 确定具体类型
        gm.method(1);    // 确定为Integer
        gm.method("a");  // 确定为String
        // 调用静态方法
        // 也是在调用方法传递参数时, 确定具体类型
        GenericMethod.staticMethod(1);
        GenericMethod.staticMethod("a");
    }
}
定义与使用含有泛型的接口知识点:
如何在接口中定义泛型
接口中的泛型何时确定具体类型
总结:
定义泛型接口与定义泛型类一样    // 带有泛型的类定义格式
    修饰符 interface 接口名<代表泛型的变量> {    }

    public interface GenericInterface<I> {
                void method(I i);
    }实现类实现了泛型接口后可以有2种选择:
        1. "定义实现类时", 确定泛型的具体类型  
        public class A implements GenericInterface<String> {
                        @Override
                        public void method(String i) {}  // 所有使用泛型的地方都会确定具体类型
        }
        2. 定义实现类时仍然沿用泛型, 直到"创建该实现类对象时"才确定泛型的具体类型
        public class A<I> implements GenericInterface<I> {
                        @Override
                        public void method(I i) {}  // 泛型不需要确定具体类型, 当创建该类对象时再确定
        }

                A<Integer> a = new A<>();
                a.method(1);
                A<String> a2 = new A<>();
                a2.method("dsfdsaf");
补充:
/*
    定义含有泛型的接口
*/
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. 创建实现类对象时确定泛型的具体类型
泛型通配符知识点:
什么是泛型通配符? 泛型通配符如何使用?
什么是泛型的上限
什么是泛型的下限public void method(ArrayList<? extends Cat> list) {  
        // ArrayList<?> list = new ArrayList<String>();
    // ...
}
ArrayList<Animal> list = new ...;
ArrayList<Cat> list2 = new ...;
ArrayList<String> list3 = new ...;
method(list);
method(list2);
总结:
泛型通配符:
        不知道使用什么类型来接收时, 此时可以使用 <?> 来表示未知通配符
        示例: List<?> list  接收泛型是任意类型的List对象
        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及其父类的泛型)
补充: 斗地主练习
斗地主: 需求分析知识点:
斗地主发牌: 使用54张牌打乱顺序,三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张留作底牌
        装牌:
                牌盒: 有牌 String   
                        "♣A"      ♤ ♡ ♣ ♢   A 2 3 ... 9 10 J Q K
                        大王 小王
        洗牌: 把牌盒集合元素打乱顺序
        发牌:
                遍历牌盒集合
                3个人: 3个集合ArrayList
                        发牌: 取模%  
                                11 % 3 = 2
                                10 % 3 = 1
                                9 % 3 = 0
                                8 % 3 = 2
                                7 % 3 = 1
                                6 % 3 = 0
                3张底牌: 底牌集合
        看牌: 打印3个人的集合和底牌集合
总结:
Collection接口
java.util.Collections类: 操作集合的工具类
        // 静态方法
        static void shuffle(List<?> list): 随机打乱集合中元素的顺序
![](./img/04_斗地主案例的需求分析.bmp)补充: 斗地主: 代码实现
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个玩家集合和底牌集合
代码:
public class Test {
    public static void main(String[] args) {
        /*
            准备牌
         */
        // 创建牌盒集合
        ArrayList<String> poker = new ArrayList<>();        // 定义2个数组, 分别存储花色和数值
        String[] colors = {"♤", "♡", "♣", "♢"};
        String[] numbers = {"2", "A", "K", "Q", "J", "10", "9", "8", "7", "6", "5", "4", "3"};        // 添加大小王
        poker.add("大王");
        poker.add("小王");
        // 嵌套循环生成牌, 并装牌
        for (String color : colors) {
            for (String number : numbers) {
                // 添加到牌盒
                poker.add(color + number);
            }
        }        /*
            洗牌
         */
        Collections.shuffle(poker);        /*
            发牌
         */
        // 定义玩家集合和底牌集合
        ArrayList<String> player01 = new ArrayList<>();
        ArrayList<String> player02 = new ArrayList<>();
        ArrayList<String> player03 = new ArrayList<>();
        ArrayList<String> diPai = new ArrayList<>();
        // 轮流发牌
        for (int i = 0; i < poker.size(); i++) {
            // 获取一张牌
            String pai = poker.get(i);
            // 判断
            if (i >= 51) {
                // 给底牌
                diPai.add(pai);
            } else if (i % 3 == 0) {
                // 给玩家1
                player01.add(pai);
            } else if (i % 3 == 1) {
                // 给玩家2
                player02.add(pai);
            } else if (i % 3 == 2) {
                // 给玩家3
                player03.add(pai);
            }
        }        /*
            看牌
         */
        System.out.println("刘备:" + player01);
        System.out.println("关羽:" + player02);
        System.out.println("张飞:" + player03);
        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): 随机打乱集合中元素的顺序
今日目标 能够说出集合与数组的区别
        1. 集合长度可变, 数组长度一旦创建不可改变
        2. 集合能存多种引用类型, 数组可以存一种基本和引用类型
说出Collection集合的常用功能
        添加: boolean add(E e)
        删除: boolean remove(E e)
        清空: void clear()
        长度: int size()
        判断是否包含: boolean contains(E e)
        判断集合是否为空: boolean isEmpty()
        转数组: Object[] toArray()
        获取迭代器对象: Iterator<E> iterator()
能够使用迭代器对集合进行取元素
        Iterator<元素类型> it = 集合对象.iterator();
        while (it.hasNext()) {
                元素类型 变量名 = it.next();
        }
能够使用foreach循环遍历集合
        for (元素类型 变量名 : 集合名) {
                // 使用变量
        }for (元素类型 变量名 : 数组名) {
                // 使用变量
        }集合.for
能够使用泛型定义集合对象
        ArrayList<String> list = new ArrayList<>();好处:
                1. 避免了类型转换的麻烦
                2. 将运行时可能产生的类型转换异常, 提前到了编译时期
能够理解泛型上下限
        上限: <? extends E>  可以接收泛型为E类型或E类型的子类类型
        下限: <? super E> 可以接收泛型为E类型或E类型的父类类型
能够阐述泛型通配符的作用
        <?>
        <? extends E>
        <? super E>

        扩展泛型的限制范围





0 个回复

您需要登录后才可以回帖 登录 | 加入黑马