Java基础之Collection、泛型

前言: 你一身傲骨,怎能向这个莫名其妙的世界投降

大家好我是擦擦屁屁闻闻手,昨天我们讲了Java基础里边的数组,是不是感觉很爽,但是但是但是,它也是有弊端的(小声嘀咕:有弊端你写他干撒哈),哈哈有弊端归有弊端学还是要学的,存在即合理嘛,今天我们来学习新的容器:集合

集合概述

概念::集合是java中提供的一种容器,可以用来存储多个数据

  • 集合和数组既然都是容器,它们有啥区别呢?
    • 数组的长度是固定的。集合的长度是可变的 (一般数组是不能添加元素的,因为他们在初始化时就已定好长度了,不能改变长度,可以先转集合添加)
    • 数组中存储的是同一类型的元素,可以存储基本数据类型值。集合存储的都是对象。而且对象的类型可以不一致。在开发中一般当对象多的时候,使用集合进行存储

集合框架

JAVASE提供了满足各种需求的API,在使用这些API前,先了解其继承与接口操作架构,才能了解何时采用哪个类,以及类之间如何彼此合作,从而达到灵活应用。

  • 集合按照其存储结构可以分为两大类,分别是单列集合java.util.Collection双列集合java.util.Map,今天我们主要学习Collection 集合,后天讲解Map集合

  • Collection单列集合类的根接口,用于存储一系列符合某种规则的元素,它有两个重要的子接口,分别是java.util.List 和java.util.Set

    • List的特点是元素有序、元素可重复。List 接口的主要实现类有java.util.ArrayList和java.util.LinkedList
    • Set的特点是元素无序,而且不可重复,Set接口的主要实现类有java.util.HashSetjava.util.TreeSet
  • 图解:橙色框里填写的都是接口类型蓝色框里填写的都是具体的实现类

  • Collection 常用功能
    • Collection是所有单列集合的父接口,因此在Collection中定义了单列集合(List和Set)通用的一些方法,这些方法可用于操作 所有 的单列集合

      • public boolean add(E e) : 把给定的对象添加到当前集合中 。
      • public void clear() :清空集合中所有的元素。
      • public boolean remove(E e) : 把给定的对象在当前集合中删除。
      • public boolean contains(E e) : 判断当前集合中是否包含给定的对象。
      • public boolean isEmpty() : 判断当前集合是否为空。
      • public int size() : 返回集合中元素的个数。
      • public Object[] toArray() : 把集合中的元素,存储到数组中
      public class Test {
      
        public static void main(String[] args) {
            // 使用多态形式
            Collection<String> coll = new ArrayList<String>();
            // 使用方法
      
            // 添加功能 boolean add(String s)
            coll.add("小李广");
            coll.add("扫地僧");
            coll.add("石破天");
            System.out.println(coll);
      
            // boolean contains(E e) 判断o是否在集合中存在
            System.out.println("判断 扫地僧 是否在集合中"+coll.contains("扫地僧"));
      
            //boolean remove(E e) 删除在集合中的o元素
            System.out.println("删除石破天:"+coll.remove("石破天"));
            System.out.println("操作之后集合中元素:"+coll);
      
            // size() 集合中有几个元素
            System.out.println("集合中有"+coll.size()+"个元素");
      
            //Object[] toArray()转换成一个Object数组
            Object[] objects = coll.toArray();
            //遍历数组
            for (int i = 0; i < objects.length; i++) {
                System.out.println(objects[i]);
            }
      
            //void clear() 清空集合
            coll.clear();
            System.out.println("集合中内容为:"+coll);
            // boolean isEmpty() 判断是否为空
            System.out.println(coll.isEmpty());
          }
      }
      
      [小李广, 扫地僧, 石破天]
      判断 扫地僧 是否在集合中true
      删除石破天:true
      操作之后集合中元素:[小李广, 扫地僧]
      集合中有2个元素
      小李广
      扫地僧
      集合中内容为:[]
      true
      

有关Collection中的方法可不止上面这些,其他方法可以自行查看API学习

Iterator迭代器

  • Iterator接口

    • 在程序开发中,经常需要遍历集合中的所有元素。针对这种需求,JDK专门提供了一个接口java.util.Iterator。Iterator 接口也是Java集合中的一员,但它与Collection 、Map 接口有所不同,Collection 接口与Map 接口主要用于存储元素,而Iterator 主要用于迭代访问(即遍历)Collection中的元素,因此Iterator对象也被称为 迭代器
  • 想要遍历Collection集合,那么就要获取该集合迭代器完成迭代操作,下面介绍一下获取迭代器的方法

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

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

    • Iterator接口的常用方法如下

      • public E next() :返回迭代的下一个元素。
      • public boolean hasNext() :如果仍有元素可以迭代,则返回 true
      public class Test {
       
          public static void main(String[] args) {
              // 使用多态方式 创建对象
              Collection<String> coll = new ArrayList<String>();
              // 添加元素到集合
              coll.add("串串星人");
              coll.add("吐槽星人");
              coll.add("汪星人");
              //遍历
              //使用迭代器 遍历 每个集合对象都有自己的迭代器
              Iterator<String> it = coll.iterator();
              // 泛型指的是 迭代出 元素的数据类型
              while(it.hasNext()){ //判断是否有迭代元素
                  String s = it.next();//获取迭代出的元素
                  System.out.println(s);
              }
          }
      }
      
      串串星人
      吐槽星人
      汪星人
      

在进行集合元素取出时,如果集合中已经没有元素了,还继续使用迭代器的next方法,将会发生java.util.NoSuchElementException没有集合元素的错误

  • 迭代器的实现原理
    • 我们在之前案例已经完成了Iterator遍历集合的整个过程。当遍历集合时,首先通过调用t集合的iterator()方法获得迭代器对象,然后使用hashNext()方法判断集合中是否存在下一个元素,如果存在,则调用next()方法将元素取出,否则说明已到达了集合末尾,停止遍历元素。Iterator迭代器对象在遍历集合时,内部采用指针的方式来跟踪集合中的元素,为了让初学者能更好地理解迭代器的工作原理,接下来通过一个图例来演示Iterator对象迭代元素的过程

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

  • 增强for

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

重点:新for循环必须有被遍历的目标。目标只能是Collection(单列集合)或者是数组。新式for仅仅作为遍历操作出现

  • 遍历数组:
public class Test {
    
    public static void main(String[] args) {
        int[] array = {1, 2, 34, 45, 6};
        //使用增强for遍历数组
        for (int value : array) {//value代表数组中的每个元素
            System.out.println(value);
        }
    }
}
  • 遍历集合:
public class Test {
    
    public static void main(String[] args) {
        // 使用多态方式 创建对象
        Collection<String> coll = new ArrayList<String>();
        // 添加元素到集合
        coll.add("张三丰");
        coll.add("张翠山");
        coll.add("张无忌");
        for (String value : coll) {
            System.out.println(value);
        }
    }
}

泛型

  • 泛型概述

    • 在前面学习集合时,我们都知道集合中是可以存放任意对象的,只要把对象存储集合后,那么这时他们都会被提升成Object类型。当我们在取出每一个对象,并且进行相应的操作,这时必须采用类型转换

      public class Test {
          
          public static void main(String[] args) {
              Collection coll = new ArrayList();
              coll.add("abc");
              coll.add("itcast");
              coll.add(5);//由于集合没有做任何限定,任何类型都可以给其中存放
              Iterator it = coll.iterator();
              while(it.hasNext()){
              //需要打印每个字符串的长度,就要把迭代出来的对象转成String类型
                  String str = (String) it.next();
                  System.out.println(str.length());
              }
      
          }
      }
      

      程序在运行时发生了问题java.lang.ClassCastException。 为什么会发生类型转换异常呢? 我们来分析下:由于集合中什么类型的元素都可以存储。导致取出时强转引发运行时 ClassCastException。 怎么来解决这个问题呢?Collection虽然可以存储各种对象,但实际上通常Collection只存储同一类型对象。例如都是存储字符串对象。因此在JDK5之后,新增了泛型(Generic)语法,让你在设计API时可以指定类或方法支持泛型,这样我们使用API的时候也变得更为简洁,并得到了编译时期的语法检查

  • 泛型:可以在类或方法中预支地使用未知的类型。(一般在创建对象时,将未知的类型确定具体的类型。当没有指定泛型时,默认类型为Object类型)

  • 使用泛型的好处

    • 将运行时期的ClassCastException,转移到了编译时期变成了编译失败。
    • 避免了类型强转的麻烦
    public class Test {
       
        public static void main(String[] args) {
            //泛型设置为String
            Collection<String> list = new ArrayList<String>();
            list.add("abc");
            list.add("itcast");
            // list.add(5);//当集合明确类型后,存放类型不一致就会编译报错
            // 集合已经明确具体存放的元素类型,那么在使用迭代器的时候,迭代器也同样会知道具体遍历元素类型
            Iterator<String> it = list.iterator();
            while(it.hasNext()){
                String str = it.next();
                //当使用Iterator<String>控制元素类型后,就不需要强转了。获取到的元素直接就是String类型
                System.out.println(str.length());
            }
        }
    }
    

    泛型是数据类型的一部分,我们将类名与泛型合并一起看做数据类型。

  • 泛型的定义与使用

泛型,用来灵活地将数据类型应用到不同的类、方法、接口当中。将数据类型作为参数进行传递

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

    • 定义格式

    修饰符 class 类名<代表泛型的变量> { }

    例如,API中的ArrayList集合:

    class ArrayList<E>{
        public boolean add(E e){ }
        public E get(int index){ }
        ....
        }
    
  • 使用泛型:即什么时候确定泛型

    • 在创建对象的时候确定泛型

    ArrayList list = new ArrayList();

    此时,变量E的值就是String类型,那么我们的类型就可以理解为

    class ArrayList<String>{
    public boolean add(String e){ }
    public String get(int index){ }
    ...
    }
    

    再例如:

    ArrayList list = new ArrayList();

    此时,变量E的值就是Integer类型,那么我们的类型就可以理解为:

    class ArrayList<Integer> {
    public boolean add(Integer e) { }
    public Integer get(int index) { }
    ...
    }
    
  • 举例自定义泛型类

public class MyGenericClass<MVP> {
//没有MVP类型,在这里代表 未知的一种数据类型 未来传递什么就是什么类型
    private MVP mvp;
    public void setMVP(MVP mvp) {
        this.mvp = mvp;
    }
    public MVP getMVP() {
        return mvp;
    }
}
  • 使用:
public class Test {
   
    public static void main(String[] args) {
        // 创建一个泛型为String的类
        MyGenericClass<String> my = new MyGenericClass<String>();
        // 调用setMVP
        my.setMVP("大胡子登登");
        // 调用getMVP
        String mvp = my.getMVP();
        System.out.println(mvp);
        //创建一个泛型为Integer的类
        MyGenericClass<Integer> my2 = new MyGenericClass<Integer>();
        my2.setMVP(123);
        Integer mvp2 = my2.getMVP();
    }
 }
  • 含有泛型的方法

    • 定义格式:

    修饰符 <代表泛型的变量> 返回值类型 方法名(参数){ }

    • 例如
      public class MyGenericMethod {
        public <MVP> void show(MVP mvp) {
          System.out.println(mvp.getClass());
        }
        public <MVP> MVP show2(MVP mvp) {
            return mvp;
        }
    }
    
    • 使用格式:调用方法时,确定泛型的类型
    public class Test {
        public static void main(String[] args) {
            // 创建对象
            MyGenericMethod mm = new MyGenericMethod();
            // 演示看方法提示
            mm.show("aaa");
            mm.show(123);
            mm.show(12.45);
        }
    }
    
  • 含有泛型的接口

    • 定义格式:

    修饰符 interface接口名<代表泛型的变量> { }

    • 例如
    public interface MyGenericInterface<E>{
      public abstract void add(E e);
      public abstract E getE();
    }
    
    • 使用格式:1、定义类时确定泛型的类型
    public class MyImp1 implements MyGenericInterface<String> {
        @Override//覆盖重写
        public void add(String e) {
            // 省略...
        }
        @Override//覆盖重写
        public String getE() {
            return null;
        }
    }
    

    此时,泛型E的值就是String类型。

    • 使用格式:2、始终不确定泛型的类型,直到创建对象时,确定泛型的类型
    public class MyImp2<E> implements MyGenericInterface<E> {
        @Override//覆盖重写
        public void add(E e) {
            // 省略...
        }
        @Override//覆盖重写
        public E getE() {
            return null;
        }
      }
    
    /*
    * 使用
    */
    public class GenericInterface {
      public static void main(String[] args) {
          MyImp2<String> my = new MyImp2<String>();
          my.add("aa");
      }
    }
    
  • 泛型通配符

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

    • 通配符基本使用(不知道使用什么类型来接收的时候,此时可以使用?,?表示未知通配符)

      • 举个例子大家理解使用即可:
        public static void main(String[] args) {
            Collection<Intger> list1 = new ArrayList<Integer>();
            getElement(list1);
            Collection<String> list2 = new ArrayList<String>();
            getElement(list2);
            }
        public static void getElement(Collection<?> coll){}
        //?代表可以接收任意类型
      

      泛型不存在继承关系

  • 通配符高级使用----受限泛型

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

    • 泛型的上限:
      • 格式: 类型名称 <? extends 类 > 对象名称
      • 意义: 只能接收该类型及其子类
    • 泛型的下限:
      • 格式: 类型名称 <? super 类 > 对象名称
      • 意义: 只能接收该类型及其父类型

比如:现已知Object类,String 类,Number类,Integer类,其中Number是Integer的父类

public class Test {
   public static void main(String[] args) {
    Collection<Integer> list1 = new ArrayList<Integer>();
    Collection<String> list2 = new ArrayList<String>();
    Collection<Number> list3 = new ArrayList<Number>();
    Collection<Object> list4 = new ArrayList<Object>();
    getElement(list1);
    getElement(list2);//报错
    getElement(list3);
    getElement(list4);//报错
    getElement2(list1);//报错
    getElement2(list2);//报错
    getElement2(list3);
    getElement2(list4);
}
  // 泛型的上限:此时的泛型?,必须是Number类型或者Number类型的子类
  public static void getElement1(Collection<? extends Number> coll){}
  // 泛型的下限:此时的泛型?,必须是Number类型或者Number类型的父类
  public static void getElement2(Collection<? super Number> coll){} 
}

泛型一般在写一些框架时,会广泛使用,在这里只是给大家提一下,不至于以后看到了懵逼,可以在以后的学习或者工作中再深入的学习

愿景:愿程序永无 Bug

本文使用mdnice排版

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值