Collection+泛型

一.Collection集合

1.1 集合概述

  1. 集合:集合是java中提供的一种容器,可以用来存储多个数据。
  2. 集合和数组既然都是容器,它们有什么区别呢?
    • 数组的长度是固定的。集合的长度是可变的。
    • 数组中存储的是同一类型的元素,可以存储任意类型数据。集合存储的都是引用数据类型。如果想存储基本类型数据需要存储对应的包装类型。

1.2 集合常用类的继承体系

  1. Collection:单列集合类的根接口,用于存储一系列符合某种规则的元素,它有两个重要的子接口,分别是java.util.List 和 java.util.Set 。其中, List 的特点是元素有序、元素可重复。 Set 的特点是元素不可重复。 List 接口的主要实现类有 java.util.ArrayList 和 java.util.LinkedList , Set 接口的主要实现类有
    java.util.HashSet 和 java.util.LinkedHashSet 。

  2. 集合常用类的继承体系原理:
    在这里插入图片描述

1.3 Collection 常用功能

  1. Collection是所有单列集合的父接口,因此在Collection中定义了单列集合(List和Set)通用的一些方法,这些方法可用于操作所有的单列集合。方法如下:

    • 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() : 把集合中的元素,存储到数组中

      //创建集合对象,多态
              //Collection<String> coll = new ArrayList<>();//有序的,可重复的
              Collection<String> coll = new HashSet<>();//无重复的,无序的
              System.out.println(coll);//[]
              //public boolean add(E e):  把给定的对象添加到当前集合中 。
              coll.add("张三");
              coll.add("李四");
              coll.add("王五");
              coll.add("张三");
              coll.add("赵六");
              coll.add("田七");
      //集合重写了toString方法 [张三, 李四, 王五, 张三, 赵六, 田七]
              System.out.println(coll);
              /*
                  public boolean remove(E e): 把给定的对象在当前集合中删除。
                      1.如果集合中有对应的元素,会移除成功,返回true
                      2.如果集合中没有有对应的元素,对集合没有影响,返回false
                      3.如果被移除的元素在集合中有多个,只会移除第一个
               */
              boolean b1 = coll.remove("张三");
              System.out.println("b1:"+b1); //b1:true
              boolean b2 = coll.remove("赵四");
              System.out.println("b2:"+b2); //b2:false
              System.out.println(coll);//[李四, 王五, 张三, 赵六, 田七]
              /*
                 public boolean contains(Object obj): 判断当前集合中是否包含给定的对象。
                      1.包含,返回true
                      2.不包含,返回false
               */
              boolean b3 = coll.contains("王五");
              System.out.println("b3:"+b3);//b3:true
              boolean b4 = coll.contains("老王");
              System.out.println("b4:"+b4);//b4:false
              /*
                  public int size(): 返回集合中元素的个数。
               */
              System.out.println(coll.size());//5
              /*
                  public Object[] toArray(): 把集合中的元素,存储到数组中
               */
              Object[] arr = coll.toArray();
              for (int i = 0; i < arr.length; i++) {
                  System.out.println(arr[i]);
              }
              /*
                  public boolean isEmpty(): 判断当前集合是否为空。
                      1.集合为空,返回true
                      2.集合不为空,返回false
               */
              System.out.println(coll.isEmpty());//false
              /*
                  public void clear() :清空集合中所有的元素。 集合还存在,还能继续使用
               */
              coll.clear();
              System.out.println(coll);//[]
              System.out.println(coll.isEmpty());//true
      

二.Iterator迭代器

2.1 Iterator接口

  1. 在程序开发中,经常需要遍历集合中的所有元素。针对这种需求,JDK专门提供了一个接口
    java.util.Iterator 。

  2. 由来:集合有很多种,每种集合存储数据的原理不同,取出元素的方式也不同,所以我们可以使用迭代器,来取出集合中的元素,是一种通用的取出集合中元素的方式。

  3. 迭代器是一个接口:java.util.Iterator:迭代器接口 对 collection 进行迭代的迭代器。

  4. 迭代器的使用:在Collection集合中有一个方法叫iterator()

    • 它返回Iterator接口的实现类对象(了解:迭代器的实现类是每个集合的内部类)

    • Iterator iterator() 返回在此 collection 的元素上进行迭代的迭代器。

    • 我们无需关注Iterator接口的实现类是谁,我们只需要知道Iterator接口中的方法如何使用会使用

    • Iterator接口来接收这个实现类对象即可,这种编程方式(只关注接口)叫面向接口编程。

    • 迭代器的使用步骤:(重点):

      • 创建集合对象,往集合中添加元素
      • 使用集合中的方法iterator获取迭代器的对象
      • 使用迭代器Iterator中的方法hasNext和next依次取出集合中的元素
  5. 想要遍历Collection集合,那么就要获取该集合迭代器完成迭代操作,下面介绍一下获取迭代器的方法:

    • public Iterator iterator() : 获取集合对应的迭代器,用来遍历集合中的元素的。

    • 迭代的原理:即Collection集合元素的通用获取方式。在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。

    • Iterator接口的常用方法如下:

      • public E next() :返回迭代的下一个元素。

      • public boolean hasNext() :如果仍有元素可以迭代,则返回 true。

      • 接下来我们通过案例学习如何使用Iterator迭代集合中元素:

         //1.创建集合对象,往集合中添加元素
                Collection<String> coll = new ArrayList<>();
                //Collection<String> coll = new HashSet<>();
                coll.add("詹姆斯");
                coll.add("姚明");
                coll.add("科比");
                coll.add("库里");
                coll.add("艾弗森");
                /*
                    2.使用集合中的方法iterator获取迭代器的对象
                    迭代器的泛型跟着集合走,集合是什么泛型,迭代器就是什么泛型
                 */
                Iterator<String> it = coll.iterator();
                /*
                    3.使用迭代器Iterator中的方法hasNext和next依次取出集合中的元素
           1.boolean hasNext():判断集合中还有没有下一个元素,有就返回true,没有就返回false
                    2.E next():取出集合中的下一个元素
                    发现迭代器取出元素是一个重复的过程,所以可以使用循环优化
                    不知道集合中有多少元素,使用while循环
                    while循环结束条件,hasNext方法返回false
                 */
                while (it.hasNext()){
                    String s = it.next();
                    System.out.println(s);
                }
                System.out.println("---------------------------");
                for(Iterator<String> it2 = coll.iterator();it2.hasNext();){
                    String s = it2.next();
                    System.out.println(s);
                }
        
    • 注意事项:

      • 在进行集合元素获取时,如果集合中已经没有元素了,还继续使用迭代器的next方法,将会抛出java.util.NoSuchElementException没有集合元素异常。

      • 在进行集合元素获取时,如果添加或移除集合中的元素 , 将无法继续迭代 , 将会抛出
        ConcurrentModificationException并发修改异常.–>并发(遍历和修改同时进行)

        /*并发修改异常:并发修改异常:在使用迭代器遍历集合的过程中,对集合的长度进行了修改(增加元素,删除元素)并发:遍历和修改同时进行-->ConcurrentModificationException
        解决方案:
                1.迭代就是迭代,不要对集合进行修改
                2.想要在遍历的过程中修改,可以使用Iterator接口的子接口ListIterator
                	public interface ListIterator<E>extends Iterator<E>{
                    	里边定义了往集合中添加元素和删除元素的方法
                   	 	void add(E e):将指定的元素插入列表
                   	 	void remove():从列表中移除由next或previous返回最后一个元素。
                    可以使用迭代器的add/remove方法对集合进行修改,就相当于迭代器和集合已经商量好了,可以增删元素
                }
                注意:
                    ListIterator接口只能遍历List接口下边的集合
             */
         ArrayList<String> list = new ArrayList<>();
                list.add("詹姆斯");
                list.add("姚明");
                list.add("科比");
                list.add("库里");
                list.add("艾弗森");
                //使用集合中的方法iterator获取迭代器对象
                Iterator<String> it = list.iterator();
                //使用迭代中的方法hasNext和next依次取出集合中的元素
                while (it.hasNext()){
                    String s = it.next();
                    System.out.println(s);
                    /*
                        1.增加一个判断,如果存在科比
                        2.增加一个奥尼尔
                     */
                    if(s.equals("科比")){
                        list.add("奥尼尔");
                    }
                    //list.remove(s);默认删除集合中元素第一个位置的詹姆斯
                }
                System.out.println("-------------------------------------");
                //使用集合中的方法listIterator获取迭代器对象
                ListIterator<String> li = list.listIterator();
                //使用迭代中的方法hasNext和next依次取出集合中的元素
                while(li.hasNext()){
                    String s = li.next();
                    System.out.println(s);
                     /*
                        1.增加一个判断,如果存在科比
                        2.增加一个奥尼尔
                     */
                    if(s.equals("科比")){
                        li.add("奥尼尔");//使用的迭代器增加元素的方法
                    }
                }
                System.out.println(list);//[詹姆斯, 姚明, 科比, 奥尼尔, 库里, 艾弗森]
        
      • 并发修改异常原理:
        在这里插入图片描述

2.2 迭代器的实现原理

  1. 在调用Iterator的next方法之前,迭代器的索引位于第一个元素之前,不指向任何元素,当第一次调用迭代器的next方法后,迭代器的索引会向后移动一位,指向第一个元素并将该元素返回,当再次调用next方法时,迭代器的索引会指向第二个元素并将该元素返回,依此类推,直到hasNext方法返回false,表示到达了集合的末尾,终止对元素的遍历。
    在这里插入图片描述
    在这里插入图片描述

2.3 增强for

  1. 增强for循环(也称for each循环)是JDK1.5以后出来的一个高级for循环,专门用来遍历数组和集合的。它的内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作。格式:

    for(元素的数据类型  变量 : Collection集合or数组){
       //写操作代码  
    }
    
  2. 它用于遍历Collection和数组。通常只进行遍历元素,不要在遍历的过程中对集合元素进行增删操作。

  3. 新for循环必须有被遍历的目标,目标只能是Collection或者是数组;

  4. 新式for(迭代器)仅仅作为遍历操作出现,不能对集合进行增删元素操作,否则抛出
    ConcurrentModificationException并发修改异常

    /*
    	使用增强的for循环(for each)遍历数组
    */
    		int[] arr={1,2,3};
    		for(int a:arr){
        		System.out.println(a);
    		}
    /*
     	增强的for循环和普通for循环的区别
     		1:普通for在遍历的过程中可以对集合中的数组/集合中元素进行修改(索引)
     		2:增强的for没有索引,也不能修改数组/集合中的元素
    */
     		int[] arr1 = {1,2,3};
            //使用普通的for循环遍历数组
            for (int i = 0; i < arr1.length; i++) {
                //把数组中的元素扩大二倍
                arr1[i] *= 2;
                System.out.println(arr1[i]);
            }
            System.out.println("arr1[0]: "+arr1[0]);//2
            System.out.println("-------------------------");
            int[] arr2 = {1,2,3};
            //使用增强for循环遍历数组
            for (int i : arr2) {
                //把数组中的元素扩大二倍
                i *= 2;
                System.out.println(i);
            }
            System.out.println("arr2[0]: "+arr2[0]);//1
    /*
     	增强for循环遍历的同时,可以使用元素特有的方法
    */
     		String[] arr = {"aaa","2134214234","safdsaljflsakf"};
            for(String s: arr){
                System.out.println(s+"-->"+s.length());
            }
    /*
         使用增强for循环遍历集合
    */
     		ArrayList<Integer> list = new ArrayList<>();
            list.add(1);list.add(2);
    		list.add(null);//能接受空,但int类型不能接受,会报空指针异常
    		list.add(3);list.add(4);
            /*for(int i : list){//自动拆箱
                System.out.println(i);
            }*/
            for (Integer in : list) {
                System.out.println(in);
            }
    
  5. for循环和普通for循环的区别原理
    在这里插入图片描述

三.泛型

3.1 泛型概述

  1. 泛型是一种未知的数据类型.当我们不知道什么什么类型的时候,就可以使用泛型

  2. 泛型也可以看成一个变量,用来接受数据类型(E:element 元素,T: type类型)

  3. 集合在定义的时候,并不知道要存储什么类型的元素,就得使用泛型
    在这里插入图片描述

3.2 使用泛型的好处

  1. 创建集合对象,不使用泛型

    • 好处:集合的类型默认为Object类型,可以存储任意的对象
    • 弊端:不安全,不能使用元素特有的方法
    ArrayList list = new ArrayList();
            list.add("abc");
            list.add(1);
            //使用迭代器遍历集合
            Iterator it = list.iterator();
            while(it.hasNext()){
                //取出集合中元素,Object类型
                Object obj = it.next();
                System.out.println(obj);
    
                /*
                    想使用字符串特有的方法length
                    不能使用,需要向下转型
                    Object obj = "abc";多态
                 */
                String s = (String)obj;
                System.out.println(s.length());
            }
    
  2. 创建集合对象,使用泛型

    • 好处:
      • 想使用元素特有的方法,避免了类型转换的麻烦
      • 把运行时异常,提升到编译时期异常
    • 缺点:
      • 只能存储对应泛型的数据
     ArrayList<String> list = new ArrayList<>();
            list.add("abc");
            //list.add(1);
            //使用增强for遍历list集合
            for(String s : list){
                System.out.println(s+"-->"+s.length());
            }
    

3.3 泛型的定义与使用

  1. 我们在集合中会大量使用泛型,泛型,用来灵活地将数据类型应用到不同的类,方法,接口中。将数据类型作为参数进行传递。

3.3.1 定义和使用含有泛型的类

  1. 定义格式:

    修饰符 class 类名<代表泛型的变量>{ }
    
  2. 定义一个含有泛型的类,模拟ArrayList集合:

    • 什么时候使用泛型,当我们不知道使用什么类型时,使用泛型(泛型:代表未知的数据类型)

    • 什么时候确定泛型的数据类型:创建对象的时候

      //泛型的定义格式:<名称>--》泛型实例对象的类 
      public class GenericClass<C> {
          private C a;
          public C getA() {return a;}
          public void setA(C a) {this.a = a;}
      }
      //测试类
      //创建GenericClass对象,泛型使用Integer
              GenericClass<Integer> gc1 = new GenericClass<>();
              gc1.setA(1);
              Integer a = gc1.getA();
              System.out.println(a);
              //创建GenericClass对象,泛型使用Boolean
              GenericClass<Boolean> gc2 = new GenericClass<>();
              gc2.setA(true);
              Boolean b2 = gc2.getA();
              System.out.println(b2);
      

3.3.2 含有泛型的方法

  1. 定义含义泛型的方法:

    • 泛型定义在修饰符和返回值类型之间

    • 格式:

      修饰符<泛型> 返回值类型 方法名(参数列表(使用泛型)){方法体;}
      
    • 什么时候确定泛型的数据类型:调用方法的时候确定泛型的数据类型,传递什么类型的参数,泛型就是什么类型

      	//定义一个含有泛型的方法
          public <M> void show(M m){System.out.println(m);}
          //定义一个含有泛型的静态方法
          public static <S> void method(S s){System.out.println(s);}
      //创建GenericMethod对象
              GenericMethod gm = new GenericMethod();
              //调用含有泛型的show方法
              gm.show(1);//泛型就是Integer类型
              gm.show("abc");//泛型就是String类型
              gm.show(true);//泛型就是Boolean类型
              gm.show(8.8);//泛型就是Double类型
              gm.show('a');//泛型就是Character类型
              //通过类名直接调用静态方法
              GenericMethod.method(1);
              GenericMethod.method("abc");
      

3.3.4 含有泛型的接口

  1. 定义一个含义泛型的接口:

    • 格式:

      public interface 接口名<I>{ 
          //定义一个抽象方法
          public abstract void show(I i);
      }
      
  2. 定义一个含义泛型的接口实现类

    • 第一种使用方式:(类实现接口的同时,指定接口泛型的数据类型,这样数据类型就确定下来了)

    • 格式:

      public class GenericInterfaceImpl1 implements GenericInterface<String>{
          @Override
          public void show(String s) {
              System.out.println(s);
          }
      }
      
    • 第二种使用方式:(类实现含义泛型的接口,接口使用什么类型,类就使用什么类型,类跟着接口走,就相当于定义了一个含义泛型的类)

    • 格式:

      public class GenericInterfaceImpl2<I> implements GenericInterface<I>{
          @Override
          public void show(I i) {
              System.out.println(i);
          }
      }
      
    //实现类
    //创建含有泛型接口GenericInterface的实现类GenericInterfaceImpl1对象
            GenericInterfaceImpl1 gi1 = new GenericInterfaceImpl1();
            gi1.show("aaa");
            //创建含有泛型接口GenericInterface的实现类GenericInterfaceImpl2对象
            //在创建对象的同时,指定泛型的类型
            GenericInterfaceImpl2<Double> gi2 = new GenericInterfaceImpl2();
            gi2.show(8.8);
            GenericInterfaceImpl2<Integer> gi3 = new GenericInterfaceImpl2();
            gi3.show(1);
    

3.4 泛型通配符

  1. 当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符<?>表示。但是一旦使用泛型的通配符,只能使用Object类中的共性方法,集合中元素自身方法无法使用。

3.4.1 通配符基本使用

  1. 泛型的通配符:不知道用什么类型来接受的时候,此时可以使用(?)

    • ?:表示未知通配符,代表任意的数据类型。

    • 使用方式:

      • 不能创建对象的时候作为泛型使用

        //不能创建对象的时候作为泛型使用
        //ArrayList<?> list = new ArrayList<>();
         list.add(1);//报错,不能创建
         	
        
      • 作为方法的参数泛型使用

         ArrayList<Integer> list01 = new ArrayList<>();
                list01.add(1);
                list01.add(2);
                ArrayList<String> list02 = new ArrayList<>();
                list02.add("a");
                list02.add("b");
                printArrayList(list01);
                printArrayList(list02);
        	/*
                定义一个方法,对ArrayList集合进行遍历
                要求:可以遍历任意数据类型的ArrayList集合
             */
            public static void printArrayList(ArrayList<?> list){
                //使用迭代器遍历集合,迭代器的泛型和集合一样
                Iterator<?> it = list.iterator();
                while(it.hasNext()){
                    //取出元素是Object类型
                    Object obj = it.next();
                    System.out.println(obj);
                }
            }
        
  2. 此时只能接受数据,不能往集合中存储数据。

3.42 通配符高级应用–受限泛型

  1. 之前设置泛型的时候,实际上是可以任意设置的,只要是类就可以设置。但是在java的泛型中可以指定一个泛型的上限和下限。

    • 泛型的上限:

      • 格式:类型名称<? extends 类> 对象名称

        ? extends E 代表使用的泛型只要是E类型的子类/本身即可
        
      • 意思:只能接受该类型及其子类型

    • 泛型的下限:

      • 格式:类型名称<? super 类> 对象名称

        ? super E   代表使用的泛型只要是E类型的父类/本身即可
        
      • 意思:只能接收该类型及其父类型

    • 案例:

     Collection<Integer> list1 = new ArrayList<Integer>();
            Collection<String> list2 = new ArrayList<String>();
            Collection<Number> list3 = new ArrayList<Number>();
            Collection<Object> list4 = new ArrayList<Object>();
            /*
                类与类之间的继承关系:
                    Ingteger extends Number extends Object
                    String extends Object
             */
            getElement1(list1);
            //getElement1(list2);//报错  String和Number没有关系
            getElement1(list3);
            //getElement1(list4);//报错 传递的是Number的父类
            //getElement2(list1);//报错 传递的是Number的子类
            //getElement2(list2);//报错  String和Number没有关系
            getElement2(list3);
            getElement2(list4);
        }
        // 泛型的上限:此时的泛型?,必须是Number类型或者Number类型的子类
        public static void getElement1(Collection<? extends Number> coll){}
        // 泛型的下限:此时的泛型?,必须是Number类型或者Number类型的父类
        public static void getElement2(Collection<? super Number> coll){}
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值