Java之集合框架和泛型

1. 集合介绍

集合是java中提供的一种容器,可以用来存储多个数据。由java基础语法中得知,如果数据多了,可以使用数组或者ArrayList集合来存放。

数组和集合的区别:

    数组的长度是固定的,集合的长度是可变的

    集合中存储的元素必须是引用类型


集合学习的目标:

    集合本身是一个存储的容器:

        必须能用集合存储对象

        能够增加元素、遍历集合

        能够掌握不同集合的特性


2. 回顾ArrayList集合

ArrayList比数组的好处是,存储的元素长度可变。

ArrayList原则上不存储基本类型,如果是基本类型,是自动装箱存储。


2.1 对基本类型的操作

public static void addShowArrList() {

    ArrayListarr = new ArrayList();

    arr.add(11); arr.add(22);

    arr.add(33); arr.add(44);

    arr.add(55); arr.add(66);

    for(int i = 0; i < arr.size(); i++) {

        System.out.println(arr.get(i));

    }

    arr.remove(2);

    for(int i = 0; i < arr.size(); i++) {

        System.out.println(arr.get(i));

    }

}


2.2 对引用类型的操作

定义一个Persion类:

public class Person {

    private String name;

    private int age;


    public Person() {

    }


    public Person(String name, int age) {

        Super();

        this.name = name;

        this.age = age;

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    public int getAge() {

        return age;

    }

    public void setAge(int age) {

        this.age = age;

    }

    public String toString() {

        return "Person [name=" + name + ", age=" + age + "]";

    }

}

测试代码:

public static void addShowArrList2() {

    ArrayListarrPerson = new ArrayList();

    arrPerson.add(new Person("aaa", 20));

    arrPerson.add(new Person("bbb", 25));

    arrPerson.add(new Person("ccc", 30));

    for (int i = 0; i < arrPerson.size(); i++) {

        System.out.println(arrPerson.get(i)); //这里因为Person中重写了toString方法,所以不是打印对象地址

    }

}


3. 集合的继承关系

查阅API手册,发现ArrayList类它继承了抽象类AbstractList,同时实现接口List,而List接口又继承了Collection接口。所以Collection接口为最顶层的集合接口了


interface List extends Collection{

}

public class ArrayList extends AbstractList implements List{

}

根据源码片段,说明使用ArrayList类时,该类已经把所有抽象方法进行了重写。那么,实现Collection接口的所有子类都会进行方法重写。

Java之集合框架和泛型_第1张图片

    Collection接口常用的子接口有:List接口、Set接口

    List接口常用的子类有:ArrayList类、LinkedList类

    Set接口常用的子类有:HashSet类、LinkedHashSet类

Collection接口是集合中的顶层接口,它中定义的所有功能子类都可以使用。

Collection表示一组对象,这些对象也称为Collection的元素。

    List派系的Collection允许有重复的元素,而Set派系的则不允许有重复元素。

    List派系的Collection是有序的,而Set派系的则是无序的集合


4. 集合Collection

4.1 Collection的add, clear方法

add, clear是Collection类的抽象方法,ArrayList最终将他们实现了,用ArrayList举例

Collection接口中的方法是集合中所有实现类必须拥有的方法

    List extends Collection

    ArrayList implements List

Java之集合框架和泛型_第2张图片
add方法向集合添加元素;clear方法清空集合,但是不会删掉集合


4.2 contains、size方法

private static void testFunc2() {

    Collectioncoll = new ArrayList();

    coll.add("abc");

    coll.add("def");

    coll.add("ghi");

    System.out.println(coll);

    System.out.println(coll.contains("def"));

    System.out.println(coll.contains("acb"));

    System.out.println(coll.size());

}


4.3 引申获取长度的方式

    Java中获取长度的三种方式,都返回int型:

        数组.length

        字符串.length()

        集合.size()

private static void testFunc3() {

    int[] arr = new int[3];

    System.out.println(arr.length);


    String str = "kasdlfjklsdfj";

    System.out.println(str.length());


    Collectioncoll = new ArrayList();

    coll.add("abc");

    coll.add("def");

    coll.add("ghi");

    System.out.println(coll);

    System.out.println(coll.size());

}


4.3 toArray方法

把集合转成数组, 返回是一个存储对象的数组

private static void testFunc3() {

    Collectioncoll = new ArrayList();

    coll.add("abc");

    coll.add("def");

    coll.add("ghi");

    System.out.println(coll);

    Object[] obj = coll.toArray();

    for(int i = 0; i < obj.length; i++) {

    System.out.println(obj[i]);

    }

}

将集合转为数组,这样数组中的集合信息就不能被修改


4.4 remove方法

删除集合中的指定参数

private static void testFunc4() {

    Collectioncoll = new ArrayList();

    coll.add("abc");

    coll.add("def");

    coll.add("ghi");

    System.out.println(coll);

    

    System.out.println(coll.remove("def"));

    System.out.println(coll);

}

特别注意:因为ArrayList类是List派系的,是允许有重复元素的,在remove的时候实际上会删除第一个集合中的元素

Java之集合框架和泛型_第3张图片


5. Iterator接口:迭代器,获取集合元素

由于Java中有多种集合,它们在存储元素时,采用的存储方式不同,我们要取出这些集合中的元素,可通过一种通用的获取方式来完成。

屏蔽了不同集合获取元素的不同

5.1 Iterator实现原理

迭代器的运用,使用了面向接口的编程思想:

Iterator是一个接口,使用它必须找到实现类

在Collection接口中有一个iterator方法, 返回Iterator:Iteratoriterator()

子类ArrayList实现了iterator方法


ArrayList的iterator()方法,返回的是是Iterator接口的实现类的对象

Iterator it = array.iterator(), it就是Iterator接口的实现类,是由ArrayList对象array的iterator方法返回的


5.2 Iterator代码实现

Java之集合框架和泛型_第4张图片

引申1:如果将ArrayList改为HashSet,意思为使用HashSet集合来添加元素,使用Iterator取出HashSet中的元素。取出时,由于HashSet是Set派系的,Set派系是无须集合,所以Iterator取出时是无序的,且在添加元素时是不能重复的:

Java之集合框架和泛型_第5张图片

引申2:使用for循环也能使用迭代器

    使用for的唯一好处是由于it是在for中定义的,因此退出for之后,it的存储空间立即被释放,比while节约空间

for(Iterator it = coll.iterator(); it.hasNext(); ) {

    System.out.println(it.next());

}


5.3 Iterator执行过程

Java之集合框架和泛型_第6张图片


5.4 集合迭代中的转型(不建议这样使用,尽量使用泛型)

集合可以存任意类型的对象

集合中,不指定存储的数据类型,因此它什么都可以存

特别注意:如果要使用默认类型的特有方法,必须进行向下转型

Java之集合框架和泛型_第7张图片

6. 增强型for循环

参考:https://www.jianshu.com/p/856ec374ff4e

也是三种遍历数组的方式之一

从JDK1.5开始Collection强行继承了Iterable接口,只有一个目的,就是要让集合使用foreach方法

6.1 foreach遍历数组

Java之集合框架和泛型_第8张图片

优势:简化代码,方便遍历容器

劣势:int a只是一个变量,arr中的元素赋值给a,arr内存中的数据无法被改变


6.2 foreach遍历集合

Person类:

Java之集合框架和泛型_第9张图片
Java之集合框架和泛型_第10张图片
需要导包:导包外的包中的Person类


7. 泛型

7.1 泛型概述

在5.4中,由于定义集合Collection时没有声明元素类型,因此同一个集合中可以add任何类型的数据,虽然遍历时打印元素不会出现问题,但要使用到强转类型时就会报错:Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

Java之集合框架和泛型_第11张图片

在JDK1.5开始就引入了泛型这种安全机制,保证程序的安全性

泛型:指明了集合中存储的数据类型,用一对<>表明:<数据类型>

这样,如果添加了非泛型的数据类型,编译器是会报错的

有了泛型,再使用泛型数据类型的特有方法时,也不需要进行类型强转了

Java之集合框架和泛型_第12张图片

引申:

JAVA的泛型是伪泛型,使用了泛型<数据类型>只是针对编译器生效。在编译生成的class文件中,其实是没有泛型的表现的(可以通过java反编译成源码查看,反编译出来的代码没有泛型),但这样也足够安全了。

据说C++的泛型是真泛型


7.2 带有泛型的类

使用过的ArrayList是我们最熟悉的带有泛型的类

    public class ArrayList

其中E是Element的简写,理解为一个变量类型,E是什么类型,ArrayList集合中就存什么类型,而且其成员方法都带有

    public boolean add(E e)

    Iterator it = arr.iterator();

例子见Iterator


7.3 带泛型的方法

看一段ArrayList类的add方法:

Java之集合框架和泛型_第13张图片
定义ArrayList方法使用的泛型类型是什么, 泛型方法的E就是什么类型


7.4 带泛型的接口

List接口就是一个带泛型的接口

api手册中倒找List接口

可见,我们在项目中定义类的时候,可以先实现接口,不理会泛型,这样,调用者在new对象的时候再来指定类型


7.5 使用泛型的好处总结

    解决了强转时的安全问题

    将错误从运行时提前到了编译时

    增加了开发和调用者的灵活性

    泛型的出现,带来了增强型for循环foreach的出现


7.6 泛型通配符 ?

例:定义一个函数,可以同时迭代不同泛型类型的集合

Java之集合框架和泛型_第14张图片

可见,由于ArrayList是List派系的,HashSet是Set派系的,因此参数必须使用List和Set派系的父类接口Collection。总之需要向上找相同的父类。

由于?通配符不知道什么类型,因此不能做强转


7.7 泛型的限定

你可能感兴趣的:(Java之集合框架和泛型)