2021Java进阶篇(二)Collection和泛型

  • Post author:
  • Post category:java




2021Java进阶篇(二)Collection和泛型


进阶篇第二篇来了,希望对大家能够有帮助~~~





前言


话不多说直接上干货!




一、Collection集合



1.1 概述


集合

是Java中提供的一种容器,可以用来存储多个数据。


集合和数组的区别

  • 数组的长度是固定的,而集合的长度是可变的
  • 数组中存储的都是同一类型的元素,可以存储基本类型数据值。集合存储的都是对象
  • 而且对象的类型可以不一致。在开发中一般当对象比较多的时候,我们可以使用集合来进行存储数据
  • 而且对象的类型可以不一致。在开发中一般当对象多的时候,使用集合进行存储

集合中有

Vector

集合、

ArrayList

集合、

LinkedList

集合、

TreeSet

集合、

HashSet

集合、

LinkedHashSet

集合



1.2 Collection接口

Collection接口有两个子接口,分别是

List

接口和

Set

接口,Collection接口中定义的是所有单列集合中共性的方法,所有的单例集合都可以使用共性的方法,但是

并没有带索引的方法

,也是我们本篇文章中Collection接口要介绍的方法,剩余的方法会在接下来的博客中依次为大家进行介绍


List接口

List接口是有序的且允许数据重复,

有索引

,可以使用普通的for循环进行遍历

List接口中有三个子类分别是

Vector

集合、

ArrayList

集合、

LinkedList

集合


Set接口

Set接口不允许存储重复元素,是无序的集合(即存储和取出的元素的顺序有可能不一致),并且没有索引,因此不能使用普通的for循环进行遍历

Set集合有两个子类分别是

TreeSet

集合和

HashSet

集合,而HashSet集合又有一个子类

LinkedHashSet

集合(该集合是有序的集合)


附Collection接口的”族谱”

在这里插入图片描述


集合的学习方法

1、学习顶层:学习顶层接口/抽象类中共性的方法,所有的子类都可以使用

2、使用底层:底层不是接口就是抽象类,无法直接创建对象使用,需要使用底层的子类创建对象使用



1.3 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 boolean add(E e):  把给定的对象添加到当前集合中 。
// 创建集合对象,可以使用多态
Collection<String> coll = new ArrayList<>();
bollean boll = coll.add("张三");
System.out.println("boll="+boll); // true
System.out.println("coll ="+coll ); // 张三
coll.add("李四");
coll.add("王五");
coll.add("赵六");
coll.add("田七");
System.out.println(coll);//[张三, 李四, 王五, 赵六, 田七]

// public boolean remove(E e): 把给定的对象在当前集合中删除。
bollean boll2 = coll.remove("张三");
System.out.println("boll2="+boll2);// true
bollean boll3 = coll.remove("张武");
System.out.println("boll3="+boll3);// false  因为集合中并没有 张武 这个元素 所以会删除失败然后返回 false
System.out.println(coll);// [李四, 王五, 赵六, 田七]

// public boolean contains(E e): 判断当前集合中是否包含给定的对象
bollean boll4 = coll.contains("李四");
System.out.println("boll4 ="+boll4 );// true
bollean boll4 = coll.contains("张武");
System.out.println("boll4 ="+boll4 );// false 因为集合中并没有 张武 这个元素,所以会判断失败返回 false

// public int size(): 返回集合中元素的个数
int collSize = coll.size();
System.out.println("collSize="+collSize);// 4

// public Object[] toArray()`: 把集合中的元素,存储到数组中。
Object[] objects = coll.toArray();
for(int i = 0; i < objects.length; i++){
	System.out.println(objects[i]);
}
// 最终遍历结果:[李四, 王五, 赵六, 田七]



// public void clear():	清空集合中所有的元素。
coll.clear();
// public boolean isEmpty(): 判断当前集合是否为空
System.out,println(coll.isEmpty());// true


有关Collection中的方法不止上面这些,我们只是学习其中一些常用的方法,其他方法可以自行查看API学习



二、Iterator迭代器



2.1 Iterator 接口


概述

我们在程序开发中,经常需要对集合中的数据进行遍历。针对这种需求,Iterator接口也就应运而生了,

java.util.Iterator

接口也是Java集合中的一员,但是它与

Collection

,

Map

接口又有所不同,

Collection

接口与

Map

接口主要是用于存储元素,而

Iterator

接口则主要是用来进行迭代访问(遍历)

Collection

中的元素,因此

Iterator

对象也被称为迭代器


迭代

迭代:即Collection集合中元素的通用获取方式,在取出元素之前要判断集合中有没有元素,如果有元素,就把这个元素取出来,继续在判断,如果还有则继续取。直到把集合中所有元素全部取出



2.2 常用方法


  • public E next()

    : 返回迭代的下一个元素

  • public boolean hasNext()

    : 如果仍有元素可以迭代,返回true,判断集合还有没有下一个元素,有就返回true,没有就返回false
/*
Iterator迭代器,是一个接口,我们无法直接使用,需要使用Iterator接口的实现类对象,获取实现类的方式比较特殊
Collection接口中有一个方法叫做iterator(), 这个方法返回的就是迭代器的实现类对象
*/
Collection<String> list = new ArrayList<>();
list.add("张三");
list.add("张七");
list.add("张五");
list.add("张四");
/*
	 1.使用集合中的方法iterator()获取迭代器的实现类对象,使用Iterator接口接收(多态)
	 注意:Iterator<E>接口也是有泛型的,迭代器的泛型跟着集合走,集合是什么泛型,迭代器就是什么泛型
*/
// 多态    接口          实现类对象
Iterator<String> it = list.iterator();
// 使用Iterator接口中的方法hasNext判断有没有下一个元素
// 迭代器取出集合中元素的代码,是一个重复的过程
// 可以用循环优化,不知道集合多少元素,使用while循环,循环结束的条件,hasNext方法返回false
while(it.hasNext()){
    System.out.println(it.next());
}
// 使用for循环进行遍历
// Iterator<String> it2 = list.iterator() 充当了初始化功能
// it2.hasNext() 充当了布尔判断
// it.next() 充当了步进表达式和将取出的值打印 两个功能
for (Iterator<String> it2 = list.iterator(); it2.hasNext(); ) {
       System.out.println(it.next());
}

boolean b1 = it.hasNext();
System.out.println(b1);// 没有元素 返回false
str = it.next();//没有元素,还继续使用迭代器的next方法,会抛出NoSuchElementException异常(没有集合元素异常)
System.out.println(str);



2.3 迭代器的实现原理

当遍历集合的时候,首先通过调用集合的iterator()方法来获取迭代器对象,然后使用hasNext()方法判断集合中是否存在下一个元素,如果存在,则调用next()方法将元素取出,否则说明已经到达了集合末尾,然后返回false停止遍历元素

Iterator迭代器在遍历集合的时候,内部采用指针的方式来跟中集合中的元素,在调用

Iterator



next()

方法之前,迭代器的索引位于第一个元素之前不指向任何的元素,

当第一次

调用迭代器的

next()

方法后,迭代器的索引会向后移动一位,指向集合的第一个元素并将元素进行返回,当

再次调用

next()方法时,迭代器的索引会指向第二个元素并将该元素进行返回,以此类推,知道hasNext()方法返回

false

,代表到达了集合的末尾,然后停止对元素的遍历

附原理图
在这里插入图片描述



2.4 增强for

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


Collection<E> extends Iterable<E>

: 所有的单列集合都可以使用增强for


public interface Iterable<T>

实现这个接口允许对象成为”foreach“语句的目标


使用格式



for(集合/数组的数据类型 变量名 : 集合名/数组名){


System.out.println(变量名);

}

// 使用增强for对数组进行循环遍历
int[] arr = {1,2,3,4,5};
for(int i : arr){
	System.out.println(i);
}

// 使用增强for对集合进行循环遍历
Collection<String> coll = new ArrayList<>();
coll.add("张三");
coll.add("李四");
coll.add("王五");
coll.add("赵六");
for(String str : coll ){
	System.out.println(str);
}



三、泛型



3.1

概述

我们在介绍集合的时候,说到了集合中是可以存放任意对象的,只要把对象存储到集合之后,那么这时候它们就会被默认提升成Object类型,当我们在取出每一个对象并进行想要操作的时候就必须采用类型转换

// 在不使用泛型的情况下向集合中存储和取出元素
Collection coll = new ArrayList();
// 由于集合没有做任何限定,任何类型都可以存放其中
coll.add("张三");
coll.add(true);
coll.add(5);
Iterator it = coll.iterator();
while(it.hasNext()){
	String str = (String) it.next();
	// 想要使用String类特有的方法,length获取字符串的长度;不能使用 多态 Object obj = "abc"
	// 需要向下转型,这时候会抛出ClassCastException类型转换异常,不能把Integer类型转化为String类型
	System.out.println(str.length);
}

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

好处:

  • 集合不使用泛型,默认的类型就是Object类型,可以存储任意类型的数据

弊端:

  • 不安全,会引发异常
// 在使用泛型的情况下向集合中存储和出去元素
ArrayList<String> list = new ArrayList<>();
list.add("张三");
list.add("王五");
list.add("赵六");
list.add("天齐");
// 使用迭代器遍历list集合
Iterator<String> it = list.iterator();
while(it.hasNext()){
	String s = it.next();
	System.out,println(s + "的长度" +s.length);
}

创建集合对象,使用泛型

好处:

  • 避免了类型转换的麻烦,存储的是什么类型,取出的就是什么类型
  • 把运行期异常(代码运行后会抛出的异常),提升到了编译期(写代码的时候会报错)

弊端:

  • 泛型是什么类型,就只能存储什么类型的数据



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


定义格式

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


使用泛型


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


ArrayList<String> list = new ArrayList<>();

此时的泛型就是String类型



3.2.1 定义一个含有泛型的类

泛型是一个未知的数据类型,当我们不确定使用什么数据类型的时候,可以使用泛型,泛型是可以接收任意的数据类型,可以使用Integer、String、Student… 创建对象的时候确定泛型的数据类型


定义格式

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

	private E name;

    public E getName() {
        return name;
    }

    public void setName(E name) {
        this.name = name;
    }
}


public class Demo02GenericClass {
    public static void main(String[] args) {
//      不写泛型默认为Object类型
        GenericClass gc = new GenericClass();
        gc.setName("只能是字符串");

        Object obj = gc.getName();
//        创建GenericClass对象,泛型使用Integer类型
        GenericClass<Integer> gc2 = new GenericClass<>();
        gc2.setName(1);

        Integer name = gc2.getName();
        System.out.println(name);

//        创建GenericClass对象,泛型使用String类型
        GenericClass<String> gc3 = new GenericClass<>();
        gc3.setName("小明");
        String name1 = gc3.getName();
        System.out.println(name1);
    }
}



3.2.2 定义一个含有泛型的接口


定义格式

修饰符 interface接口名<代表泛型的变量> {  }
public interface GenericInterface<I> {
    public abstract void method(I i);
}

/*
* 含有泛型的接口:第一种使用方式:定义接口的实现类,实现接口,指定接口的泛型
* public interface Iterator<E> {
*   E next();
* }
* Scanner类实现了Iterator接口,并指定接口的泛型为String,所以重写的next方法泛型默认就是字符串
* public final class Scanner implements Iterator<String>{
*   public String next() {}
* }
* */
public class GenericInterfaceImpl 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<> {
 *   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) {
	     GenericInterfaceImpl gc = new GenericInterfaceImpl();
	     gc.method("字符串");
	
		 // 创建GenericInterfaceImpl2对象
	     GenericInterface<Integer> gc2 = new GenericInterfaceImpl2<>();
	     gc2.method(10);
	
	     GenericInterface<Double> gc3 = new GenericInterfaceImpl2<>();
	     gc3.method(2.1);
    }
}



3.3 泛型通配符

当使用泛型类或者接口的时候,传递的数据中,泛型类型不确定,可以通过通配符<?> 表示但是一旦使用泛型的通配符后,只能使用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){}
//?代表可以接收任意类型


泛型不存在继承关系,

Collection<Object> list = new ArrayList<String>

这是错误的定义方式


通配符高级使用 (受限泛型)


泛型的上限

  • 格式 :

    类型名称<? extends 类> 对象名称
  • 意义 :

    只能接收该类型及其子类


泛型的下限

  • 格式:

    类型名称 <? super 类 > 对象名称
  • 意义:

    只能接收该类型及其父类型

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>();

        getElement1(list1);//Integer是Number的子类
		// getElement1(list2);//报错 String既不是Number的子类也不是其本身 所以报错
        getElement1(list3);//Number是Number的本身
		// getElement1(list4);//报错 Object既不是Number的子类也不是其本身 所以报错

		// getElement2(list1);//报错 Integer既不是Number的父类也不是其本身 所以报错
		// getElement2(list2);//报错 String既不是Number的父类也不是其本身 所以报错
        getElement2(list3);//Number是Number的本身
        getElement2(list4);//Object是Number的父类


    }

	//  类与类之间的继承关系
	//  Integer extends Number extends Object
	//  String extends Object

    //  泛型的上限:此时的泛型?,必须是Number类型或者Number类型的子类
    public static void getElement1(Collection<? extends Number> coll) {
    }

    //    泛型的下限:此时的泛型?,必须是Number类型或者Number类型的父类
    public static void getElement2(Collection<? super Number> coll) {
    }



总结


今天的分享到这里就结束了,如果觉得写的还不错的话可以多多关注点赞转发哦,本系列会持续更新!!!



版权声明:本文为her_mia原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。