【 Collection集合、Iterator迭代器、泛型、泛型通配符 】

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Sugar_i/article/details/81395358

Collection集合

集合:集合是java提供的容器,可以用来存储多个数据。

集合和数组的区别:

  • 数组的长度固定,集合的长度不可变。
  • 数组存储的都是同一类型的基本数据类型。而集合存储的都是对象,对象的类型可以不一致。

Collection:集合层次的顶层接口,没有直接的实现类,有不同的子接口继承Collection。而每一个子接口都有自己的特点。每一个子接口都有自己的实现类。
这里写图片描述
java.util.Collection接口中常用的集合方法:

public boolean add(E e):把指定的参数元素,添加到集合中;返回添加动作是否成功。
public boolean remove(Object e):根据参数指定的元素内容,从集合当中删除元素;返回删除是否成功。
public boolean contains(Object e):判断集合是否包含指定的元素。
public boolean isEmpty():判断集合是否为空白(一个元素都没有)。
public void clear():清空集合当中所有的元素内容。
public int size():获取集合的长度尺寸。
public Object[] toArray():将集合转换成为数组形式。

注意事项
对于remove方法contains方法来说,参数是Object类型。这里是如何得知两个对象是否“相同”的呢?
由equals和hashCode两个方法共同决定。

Collection<String> coll = new ArrayList<>();
coll.add("迪丽热巴");
coll.add("古力娜扎");
coll.add("玛尔扎哈");
System.out.println(coll); // [迪丽热巴, 古力娜扎, 玛尔扎哈]

boolean success = coll.remove("玛尔扎哈");
System.out.println("删除玛尔扎哈是否成功:" + success); // true
System.out.println(coll); // [迪丽热巴, 古力娜扎]

success = coll.remove("高圆圆");
System.out.println("删除高圆圆是否成功:" + success); // false
System.out.println(coll); // [迪丽热巴, 古力娜扎]

coll.add("鹿晗");
coll.add("马晗");
coll.add("牛晗");
coll.add("杨晗");
coll.add("于晗");
coll.add("朱晗");
coll.add("侯晗");
System.out.println(coll); // [迪丽热巴, 古力娜扎, 鹿晗, 马晗, 牛晗, 杨晗, 于晗, 朱晗, 侯晗]

System.out.println(coll.contains("马晗")); // true
System.out.println(coll.contains("狗晗")); // false
System.out.println("=============");

System.out.println("集合是否为空:" + coll.isEmpty()); // false

coll.clear();
System.out.println("集合是否为空:" + coll.isEmpty()); // true
System.out.println("集合的尺寸:" + coll.size()); // 0

coll.add("XXX");
coll.add("YYY");
coll.add("ZZZ");
System.out.println("集合的尺寸:" + coll.size()); // 3
System.out.println("=============");

Object[] array = coll.toArray();
for (int i = 0; i < array.length; i++) {
    System.out.println(array[i]);
}

Iterator迭代器

由于集合中并不是所有的集合都有索引值,所以遍历Collection集合,就要获取迭代器完成迭代操作。

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

概念
对于一个Collection集合来说,集合元素的获取方式,在取元素的时候要先判断集合当中有没有元素,如果有,把这个元素取出来,继续判断,如果有就取出。一直到把集合当中的所有元素都取出来了。这种取出的方法称为迭代。

java.util.Iterator<T>接口代表迭代器

public boolean hasNext:判断集合中有没有元素,有就返回true。
public E next():返回迭代的下一个元素。
这里写图片描述

注意
在使用迭代器遍历集合的过程当中,一定要避免直接通过集合改变其中元素的个数。
如果不听话,不乖,那么将会发生ConcurrentModificationException并发修改异常。

Collection<String> coll = new ArrayList<>();
coll.add("迪丽热巴");
coll.add("古力娜扎");
coll.add("泷泽萝拉");

Iterator<String> iter = coll.iterator();
while (iter.hasNext()) {
    System.out.println(iter.next());
// coll.add("阿里巴巴"); // ConcurrentModificationException
}

增强for循环:

增强for循环(昵称也叫做for-each循环)是JDK 1.5添加的特性之一。

for (数据类型 变量名称 : 数组) {
    // ...
}

注意:其中的数据类型并不一定是int,不代表索引值。
含义:用左边的变量,分别得到右侧数组当中的每一个数据值。

备注
这其实只是一个语法糖。对于数组来说,增强for循环底层其实就是一个普通的for循环。

double[] array = {1.5, 2.5, 3.5};
// 变量num将会分别得到数组array当中的每一个元素
for (double num : array) {
    System.out.println(num);
}

增强for循环照样也可以支持集合。

for (数据类型 变量名称 : 集合) {
    // ...
}

备注
这其实也是一种语法糖,对于集合来说,底层其实就是迭代器,只是表面上增强for循环的写法简单而已。

Collection<String> coll = new ArrayList<>();
coll.add("迪丽热巴");
coll.add("古力娜扎");
coll.add("阿里巴巴");

// name将会分别获取集合coll当中的每一个元素
for (String name : coll) { // iter.next()
    System.out.println(name);
}

使用增强for循环的时候,注意事项

  1. 支持数组,其实是一个语法糖,底层就是普通的fori循环。
  2. 支持java.lang.Iterable接口对象,其中就包含了集合。因为这个接口规定了一项能力:public Iterator<T> iterator():获取迭代器的方法。
  3. 支持集合,也是一个语法糖。底层就是在使用迭代器。
  4. 增强for循环当中没有索引值,所以就无法直接修改数组或集合中的内容。【重点】
// 外侧还定义了一个Person类。有name和age两个属性值。
// 增强for循环没有索引值,所以基本数据类型的数据值不能变,同时引用数据类型的地址值不能变。
Collection<Person> coll = new ArrayList<>();
coll.add(new Person("赵丽颖", 18));
coll.add(new Person("鹿晗", 73));
coll.add(new Person("王宝强", 84));

// [Person{name='赵丽颖', age=18}, Person{name='鹿晗', age=73}, Person{name='王宝强', age=84}]
System.out.println(coll);

// 换一个别的对象是做不到的。
// 但是通过对象名称地址值调用方法仍然是可以的,从而对象的成员变量内容发生了改变。
for (Person person : coll) { // Person person = iter.next();
    person.setAge(40);
}

// [Person{name='赵丽颖', age=40}, Person{name='鹿晗', age=40}, Person{name='王宝强', age=40}]
System.out.println(coll);

泛型

在集合中可以存取任意类型的对象,在把对象存储到集合中后,他们都会被提升成Object类型。每取出一个对象,进行相关操作,就必须进行类型转换。

Collection<String> coll = new ArrayList<>();
coll.add("abc");
coll.add("Hello");
// coll.add(100); // 错误写法!

// for (Object o : coll) {
//     String str = (String) o;
//     System.out.println(str.length());
//  }

for (String str : coll) {
   System.out.println(str.toUpperCase()); // 转成大写
}
  • 泛型:
    tips:一般在创建对象时,将未知的类型确定具体的类型。当没有指定泛型时,默认类型为Object类型。

使用泛型的好处

  1. 保证类型统一,确保类型安全。
  2. 将可能发生的类型安全问题,从运行期提前到编译期。(有问题尽量在javac的时候就暴露出来最好,别等到java运行时再暴露)
  3. 省去向下转型的麻烦。
  4. 让代码模板化。

自定义泛型的三种用法:

  1. 泛型类
  2. 泛型接口
  3. 泛型方法

如何自定义一个泛型类?

修饰符 class 类名称<泛型> {
    // ...
}

泛型的名称用什么都可以,一般推荐使用大写字母。
泛型代表一种尚不确定的类型,所有本类范围之内都能用这个泛型当做不确定的类型进行使用。

什么时候才能确定这个泛型?
当创建泛型类对象的时候,就可以确定。

// 定义一个泛型类:
public class Factory<A> {
  public A makePhone(A param) {
     // 进行组装的若干动作
     return param;
  }
}
// 测试类:
Factory<IPhone> factory1 = new Factory<>();
IPhone result1 = factory1.makePhone(new IPhone());

Factory<Nokia> factory2 = new Factory<>();
Nokia result2 = factory2.makePhone(new Nokia());

如何定义一个泛型接口呢?

修饰符 interface 接口名称<泛型> {
    // ...
}

含义和泛型类是完全相同的:本接口之内泛型通用。

什么时候才能确定接口的泛型?

  1. 实现类在实现接口的时候,直接指定具体泛型。
  2. 实现类仍然不指定具体泛型,那么实现类也必须是一个泛型类。
// 定义一个泛型接口
public interface MyInterface<T> {
    void method(T param); // 抽象方法的参数类型,跟着泛型走。
}

// 定义一个泛型的实现接口
public class MyInterfaceImpl<T> implements MyInterface<T> {
    @Override
    public void method(T param) { /* 这个泛型仍然不确定,
    本类的泛型是谁,它就是谁*/
    }
}

// 测试类
MyInterfaceImpl<String> impl = new MyInterfaceImpl<>();
impl.method("abc");

// 也可以用普通的实现类实现泛型接口,需要指定泛型的类型
public class MyInterfaceImplA implements MyInterface<String> {
    @Override
    public void method(String param) { // 这个参数就是具体泛型String类型
    }
}

对于泛型类/泛型接口来说,泛型是在本类/本接口当中全局通用。

泛型方法

修饰符 <泛型> 返回值类型 方法名称(参数类型 参数名称) {
    方法体
}

备注
这个泛型定义在方法上,所以只有当前这个方法自己专用。别人不能用。

// 定义一个泛型的类,在类中定义个泛型的方法
public class MyClass<A> {

    public void method1(A param) {

    }

    public void method2(A param) {

    }

    // 定义一个泛型的方法            
    public <B> B methodSpecial(B param) {
        // ...
        return param;
    }
}
// 测试类:
MyClass<Integer> obj = new MyClass<>();
obj.method1(100);
obj.method2(200);
// obj.method1("abc"); // 错误

String str = obj.methodSpecial("abc");
Person person = obj.methodSpecial(new Person());

泛型通配符
泛型的通配符其实就是一个问号:【?】
作用:用来被动匹配任何一种泛型。

注意事项

  1. 一旦使用了泛型?通配符进行匹配接收,那么遍历的时候就只能当做Object,因为不确定泛型到底传进来的是谁。
  2. 这个通配符问号,只能在匹配接收的时候使用,不能在定义泛型的时候使用。
public static void main(String[] args) {
    ArrayList<String> list1 = new ArrayList<>();
    ArrayList<Integer> list2 = new ArrayList<>();

    methodA(list1); // 正确
    methodB(list2); // 正确

//        methodA(list2); // 错误
//        methodB(list1); // 错误

//        methodObject(list1); // 错误
//        methodObject(list2); // 错误

    method(list1); // 正确
    method(list2); // 正确
}

private static void method(ArrayList<?> list) {
    for (Object obj : list) {
        System.out.println(obj);
    }
}

泛型的上下限

对于泛型的通配符问号,有两种特殊写法:上下限,上下界。

<?>:随便,谁都行。
<? extends 类>:可以是指定的类,或者其子类。(上限,最高不能超过这个类。)
<? super 类>:可以是指定的类,或者其父类。(下限,最低不能超过这个类。)
public static void main(String[] args) {
    ArrayList<Animal> animalList = new ArrayList<>();
    ArrayList<Dog> dogList = new ArrayList<>();
    ArrayList<Cat> catList = new ArrayList<>();

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

    methodExtends(animalList); // OK
    methodExtends(dogList); // OK
    methodExtends(catList); // OK

//  methodExtends(strList); // 错误!
    System.out.println("==============");

    methodSuper(dogList); // OK
    methodSuper(animalList); // OK
//  methodSuper(catList); // 错误!
//  methodSuper(strList); // 更错!
}

// 这个泛型只能匹配Animal或者是Animal的子类
private static void methodExtends(ArrayList<? extends Animal> list) {

}

// 这个泛型只能匹配Dog或者是Dog的父类
private static void methodSuper(ArrayList<? super Dog> list) {

}

猜你喜欢

转载自blog.csdn.net/Sugar_i/article/details/81395358