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

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 小石姐姐 于 2018-11-16 12:02 编辑

就业班JavaSE高级部分day02 Collection 泛型笔记

Collection集合概述:
集合: 长度可变容器, 可以存储多个对象
集合和数组的区别:
        1. 数组长度不可变; 集合长度可变
        2. 数组可以存基本类型或引用类型, 只能存同一种类型; 集合只能存储引用类型                                                     元素, 可以是多种类型元素

集合框架介绍:
Collection接口: 单列集合顶层
        |_ List接口: 元素存取有序, 可重复, 有索引
        |_ Set接口: 不可重复, 无索引
Collection常用功能:
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接口中定义的方法, 不会出现子类特有方法
迭代器:
迭代:                 类似于遍历, 判断是否有下一个元素, 有则取出下一个, 直到没有
迭代器的作用:    用于遍历集合的对象
使用迭代器遍历集合的3步:
        1. 使用集合对象的 iterator() 获取迭代器对象, 用 Iterator 接口接收.(多态)
        2. 使用 Iterator 接口中的 hasNext() 方法, 判断是否有下一个元素
        3. 使用 Iterator 接口中的 next() 方法, 获取下一个元素
使用迭代器的注意事项:
迭代器对象迭代完毕后, 指针已经指向最后一个元素, 没有下一个元素了. 如        果想再次从头遍历集合, 要获取新的迭代器对象

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

迭代器的原理:
迭代器有一个指针(其实就是个变量, 保存索引值), 最初指向集合的 0 位置
        hasNext() 方法可以判断当前索引是否有元素
        next() 方法返回当前元素, 并移动指针到下一个索引

迭代器的并发修改异常:
并发修改异常的原因:
        在使用迭代器迭代的过程中, 如果执行了改变集合长度的操作 (如 add(), remove(), clear()), 则会抛出 ConcurrentModificationException 并发修改异常.

如何避免:
        1. 迭代过程中不要修改集合长度
        2. 在迭代的过程中添加, 删除元素, 要使用迭代器自带的方法, 而不能使用集合的方法
                Iterator中的 remove() 方法
                ListIterator中的 add(), remove() 方法
增强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:
                优点: 有索引可以使用, 某些方式可以在遍历过程中增删元素
                缺点: 格式繁琐
                应用场景: 需要用到索引的场景
        迭代器:
                优点: 可以使用迭代器对象的方法操作元素
                缺点: 格式繁琐
                应用场景: 需要在迭代过程中增删元素的场景
泛型
泛型: 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){}
}


ArrayList<String> list = new ArrayList<>();
ArrayList<Integer> list = new ArrayList<>();
   
// 如果没有泛型, 想设计添加指定类型的集合, 就不好实现:
// 添加String元素的集合
[Java] 纯文本查看 复制代码
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呢?
}


定义和使用含泛型的类:
泛型的"定义"和"使用"
        泛型在"定义"时, "不能是"具体的类型, 只是一个变量名: public class ArrayList<E> {}
        泛型在"使用"时, "必须是"具体的数据类型 ArrayList<Integer>
    // 带有泛型的类定义格式
    修饰符 class 类名<代表泛型的名字> {  // 泛型的变量一般用一个大写字母表示, 但也可以是多个字母
    }   
类中的泛型, "在创建对象时", 确定泛型的具体类型
定义含有泛型的方法与使用
方法中的泛型定义位置:
        修饰符 和 返回值类型 之间
        // 带有泛型的方法定义格式
    修饰符 <代表泛型的名字> 返回值类型 方法名(参数){
    }
方法中定义泛型后, 返回值类型和参数类型都可以使用泛型
方法泛型的使用:
        "在调用方法时"确定泛型的具体类型

定义与使用含有泛型的接口:
定义泛型接口与定义泛型类一样
    // 带有泛型的类定义格式
    修饰符 interface 接口名<代表泛型的变量> {
    }
    public interface GenericInterface<I> {
                void method(I i);
    }
实现类实现了泛型接口后可以有2种选择:
        1. "定义实现类时", 确定泛型的具体类型  
      
[Java] 纯文本查看 复制代码
 public class A implements GenericInterface<String> {
                        @Override
                        public void method(String i) {}  // 所有使用泛型的地方都会确定具体类型
        }

        2. 定义实现类时仍然沿用泛型, 直到"创建该实现类对象时"才确定泛型的具体类型
     
[Java] 纯文本查看 复制代码
   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");

补充:
/*
    定义含有泛型的接口
*/
[Java] 纯文本查看 复制代码
public interface GenericInterface<I> {
    public abstract void method(I i);
}


/*
    含有泛型的接口,第一种使用方式:定义接口的实现类,实现接口,指定接口的泛型
   
[Java] 纯文本查看 复制代码
 public interface Iterator<E> {
        E next();
    }

    Scanner类实现了Iterator接口,并指定接口的泛型为String,所以重写的next方法泛型默认就是String
   
[AppleScript] 纯文本查看 复制代码
 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);
    }
}

/*

    含有泛型的接口第二种使用方式:接口使用什么泛型,实现类就使用什么泛型,类跟着接口走
    就相当于定义了一个含有泛型的类,创建对象的时候确定泛型的类型
[AppleScript] 纯文本查看 复制代码
   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对象
       
[AppleScript] 纯文本查看 复制代码
 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及其父类的泛型)

0 个回复

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