Collection集合、List、泛型

自述

感觉自己java基础有些薄弱,打算从0开始学习,每天打卡并且记录自己学习的内容,有兴趣小伙伴评论q1一起进步!!!敬请关注小屋(cottage_123)谢谢支持,如要搬运请挂载链接,编写不易,感谢!

一、Collection集合

1.1 集合概述

集合:集合是java中提供的一种容器,可以用来存储多个数据。
集合和数组既然都是容器,它们有什么区别呢?

* 数组的长度是固定的!可以存放任意类型的数据,只不过,我们在开发的时候经常将同一类型的数据放入一个数组!【它的操作非常麻烦的,没有方法处理】
		Object[] array = new Object[5];
        array[0] = true;
        array[1] = 100;
        array[2] = "Niubility";	
* 集合的长度是可变的!也可以存放任意类型的数据!实际开发,集合里面一般也是放相同类型的数据!【它的操作非常方便的,提供了很多便捷的方法】
		ArrayList list = new ArrayList();
        list.add(true);
        list.add(100);
        list.add("Niubility");

1.2 集合体系结构

单列集合结构图

1.3 Collection 常用功能

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

public boolean add(E e)// 把给定的对象添加到当前集合中 。【E如果指定了具体类型,那么就只能向集合中添加指定类型的数据,没有指定具体类型,就相当于Object】
public void clear(): // 清空集合中所有的元素。
public boolean remove(E e): // 把给定的对象在当前集合中删除。
public boolean contains(Object obj): // 判断当前集合中是否包含给定的对象。
public boolean isEmpty(): // 判断当前集合是否为空。
public int size(): // 返回集合中元素的个数。
public Object[] toArray(): // 把集合中的元素,存储到数组中

代码演示:

public class Test02 {

    public static void main(String[] args) {

        // 创建Collection接口的子类对象
        Collection coll = new ArrayList(); // 父接口的引用指向子类对象(多态)

        // 这里只能调用coll里面的方法,不能调用子类独有的方法!

        // public boolean add(E e): // 把给定的对象添加到当前集合中 。
        coll.add("it");
        coll.add("java");
        coll.add("666");
        coll.add(true);
        System.out.println("coll:" + coll); // coll:[itheima, java, 666, true]

        // public boolean contains(Object obj): // 判断当前集合中是否包含给定的对象。
        System.out.println(coll.contains("itcast")); // false
        System.out.println(coll.contains("it"));// true

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

        // public Object[] toArray(): // 把集合中的元素,存储到数组中
        Object[] obj = coll.toArray();
        System.out.println(Arrays.toString(obj)); // [it, java, 666, true]

        // public boolean isEmpty(): // 判断当前集合是否为空。
        System.out.println(coll.isEmpty()); // false

        // public boolean remove(E e): // 把给定的对象在当前集合中删除。
        coll.remove(true);
        System.out.println("coll:" + coll); // coll:[it, java, 666]

        // public void clear(): // 清空集合中所有的元素。
        coll.clear();
        System.out.println("coll:" + coll); // coll:[]
        System.out.println(coll.isEmpty()); // true

    }

}

二、Iterator迭代器

2.1 Iterator接口

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

想要遍历Collection集合,那么就要获取该集合对应的迭代器来完成迭代操作,下面介绍一下获取迭代器的方法:

// Iterator接口里面的方法都需要使用对象来调用!所有的单列集合都有iterator()方法(就可以得到Iterator接口的实例对象)
public Iterator iterator(): // 获取集合对应的迭代器,用来遍历集合中的元素的

**迭代:**即Collection集合元素的通用获取方式。在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出来,一直把集合中的所有元素全部取出。
Iterator接口的常用方法

public boolean hasNext(): // 如果仍有元素可以迭代,则返回 true。
public E next(): // 返回迭代的下一个元素。

代码演示:

/*
    迭代器Iterator: 获取集合中所有的数据
        在获取数据之前,先判断集合里面是否有数据,有就取出来,然后继续判断继续取,一直到判断集合里面没有数据的时候就不取了。

    使用迭代器:
        1. 通过集合对象获得迭代器对象
        2. 调用迭代器的hasNext()方法判断集合中是否有数据
        3. 只要有数据,那么就调用next()方法获取集合中的数据!
 */
public class Test01 {

    public static void main(String[] args) {

        // 创建集合对象
        Collection coll = new ArrayList(); // 多态
        // 添加数据到集合中去
        coll.add("it");
        coll.add("itnb");
        coll.add("javaee");

        // 通过集合对象获得迭代器对象
        Iterator iterator = coll.iterator(); // 将集合的数据封装到迭代器中去了

        // 判断
        while(iterator.hasNext()){
            // 获取下一个数据
            Object obj = iterator.next();
            System.out.println("obj:"+ obj);
        }

    }

}

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

public class Test01 {

    public static void main(String[] args) {

        // 创建集合对象
        Collection coll = new ArrayList(); // 多态
        // 添加数据到集合中去
        coll.add("it");
        coll.add("itnb");
        coll.add("javaee");

        // 通过集合对象获得迭代器对象
        Iterator iterator = coll.iterator(); // 将集合的数据封装到迭代器中去了

        // 判断
        while(coll.hasNext()){
            // 获取下一个数据
            Object obj = iterator.next(); 
            System.out.println("obj:"+ obj);
        }
		System.out.println(iterator.next()); // 迭代器中的元素数据已经取完了,还在获取,就会出现这个没有此元素异常!
    }

}

注意2:在进行集合元素获取时,如果添加或移除集合中的元素 , 将无法继续迭代 , 将会抛出ConcurrentModificationException并发修改异常.

public class Test02 {

    public static void main(String[] args) {

        // 创建集合对象
        Collection coll = new ArrayList(); // 多态
        // 添加数据到集合中去
        coll.add("it");
        coll.add("itnb");
        coll.add("javaee");

        // 通过集合对象获得迭代器对象
        Iterator iterator = coll.iterator(); // 将集合的数据封装到迭代器中去了

        // 此时集合和迭代器是2个内容(里面的数据是一样的!)
        while(iterator.hasNext()){
            // 获取下一个数据
            Object obj = iterator.next();
            System.out.println("obj:"+ obj);

            // 修改集合的数据(增删改)
            coll.add("完犊子了。。。"); // ConcurrentModificationException:并发修改异常!

        }

    }

}

2.2 迭代器的实现原理

当遍历集合时,首先通过调用集合的iterator()方法获得迭代器对象,然后使用hashNext()方法判断集合中是否存在下一个元素,如果存在,则调用next()方法将元素取出,否则说明已到达了集合末尾,停止遍历元素。Iterator迭代器对象在遍历集合时,内部采用指针的方式来跟踪集合中的元素。在调用Iterator的next方法之前,迭代器的索引位于第一个元素之前,不指向任何元素,当第一次调用迭代器的next方法后,迭代器的索引会向后移动一位,指向第一个元素并将该元素返回,当再次调用next方法时,迭代器的索引会指向第二个元素并将该元素返回,依此类推,直到hasNext方法返回false,表示到达了集合的末尾,终止对元素的遍历。

迭代器不允许在迭代数据的过程中对集合中的数据进行修改操作,但是之前的普通循环遍历是可以的!

下列说法错误的是:( B )

A: 迭代器是用来迭代集合容器的;
B: 一个迭代器可以对一个集合迭代多次;
C: 迭代器只能从头到尾迭代,不能倒着迭代;
D: 迭代器迭代集合时,不能使用迭代器之外的方法修改集合的长度

2.3 增强for

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

* 普通for格式:
        // 在遍历集合的过程中,可以对集合中的元素数据进行修改(增删改)操作!
		for(int i=0;i<list.size();i++){
            // 获得单个对象的数据
            Object obj = list.get(i);
		}
* 增强for格式:
		// 在遍历集合的过程中,不能对集合中的元素数据进行修改(增删改)操作!【底层是迭代器】
		for(数据类型 变量名称:集合对象或者数组){
            
		}	
* 注意:
	① 增强for循环必须有被遍历的目标,目标只能是Collection或者是数组;
	② 增强for(迭代器)仅仅作为遍历操作出现,不能对集合进行增删改元素操作,否则抛出ConcurrentModificationException并发修改异常
public class Test03 {

    public static void main(String[] args) {

        // 创建集合对象
        Collection coll = new ArrayList(); // 多态
        // 添加数据到集合中去
        coll.add("it");
        coll.add("itnb");
        coll.add("javaee");

        // 普通遍历
        /*
            for (int i = 0; i < coll.size(); i++) {
                // 需要调用子类独有的方法,向下转型
                ArrayList list = (ArrayList) coll;
                // 得到单个对象
                Object o = list.get(i);
                System.out.println("o:" + o);
                list.remove(o); // 可以的!
            }
        */

        // 增强for循环  【快捷键:被遍历集合对象.iter+回车】
        for (Object obj : coll) { // 【集合在创建的时候没有泛型类型,默认是Object类型,那么遍历集合coll得到的单个对象的数据类型就是Object】
            System.out.println("obj:" + obj);
            // 删除数据
            // coll.remove(obj);  // 并发修改异常ConcurrentModificationException
        }

    }

}

三、泛型

3.1 泛型概述

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

/*
    未使用泛型的缺点!
        1. 需要单独使用存储数据自己的功能方法的时候,需要强转!
        2. 没有使用泛型,意味着可以存放任意类型的数据,在强转的过程中,可能出现ClassCastException
 */
public class Test01 {

    public static void main(String[] args) {
        // 创建集合对象[没有指定集合里面存放元素数据的类型,那么就是Object类型的]
        ArrayList coll = new ArrayList();
        // 添加数据到集合中去
        coll.add("it");
        coll.add("itnb");
        coll.add("javaee");
        // coll.add(true); // 没法转成字符串的!【希望一个集合只存放同一类型的数据】

        // 遍历
        for (int i = 0; i < coll.size(); i++) {
            // 获取集合中单个元素数据(Object类型)
            Object o = coll.get(i);
            // 用不了字符串的方法(charAt),强转
            String s = (String) o;
            char c = s.charAt(1);
            System.out.println(c);
        }

    }

}

泛型:可以在类或方法中预支地使用未知的类型。它是数据类型的一部分,我们将类名与泛型合并一起看做数据类型。

集合是一个类(是对象的数据类型),泛型指的是集合中元素数据的类型!

一般在创建对象时,将未知的类型确定具体的类型。当没有指定泛型时,默认类型为Object类型。

3.2 使用泛型的好处

① 将运行时期的ClassCastException,转移到了编译时期变成了编译失败。

② 避免了类型强转的麻烦。

/*
    未使用泛型的缺点!
        1. 需要单独使用存储数据自己的功能方法的时候,需要强转!
        2. 没有使用泛型,意味着可以存放任意类型的数据,在强转的过程中,可能出现ClassCastException

     泛型好处:
        1. 使用泛型后,对集合中的元素数据进行了类型的限定,从而将运行时期的异常提前到编译阶段!
        2. 不用强转!
 */
public class Test02 {

    public static void main(String[] args) {
        // 创建集合对象[指定集合里面存放元素数据的类型是String,向集合中只能添加String类型的数据]
        ArrayList<String> coll = new ArrayList<>();
        // 添加数据到集合中去
        coll.add("it");
        coll.add("itnb");
        coll.add("javaee");

        // 添加失败(将运行阶段的问题提升到编译阶段)
        // coll.add(true);

        // 遍历
        for (int i = 0; i < coll.size(); i++) {
            // 获取集合中单个元素数据(Object类型)
            String s = coll.get(i);
            System.out.println(s.charAt(1));
        }

    }

}

扩展:泛型仅仅只是针对编译阶段有效!在运行时期是会被擦除的(运行时期代码的类型是限制不了的)!

/*
    此块代码不用大家掌握,能够理解:泛型仅仅只是针对编译阶段有效!在运行时期是会被擦除的(运行时期代码的类型是限制不了的)
 */
public class Test03 {

    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        // 定义集合对象
        ArrayList<String> list = new ArrayList<>();
        list.add("it");
        list.add("itnb");
        list.add("javaee");
        //list.add(true); // 由于设定了泛型类型,在编译阶段会进行语法检测(添加的数据只要不是String类型的,就会报错!)

        // 本质:集合是可以存放任意类型的数据的!【以下代码:在运行阶段执行了list的add方法将boolean类型的true值添加到list集合中去】
        Class aClass = list.getClass();
        Method add = aClass.getMethod("add", Object.class);
        add.invoke(list,true); // 将boolean类型的true值添加到list集合中去

        System.out.println("list:"+ list); // list:[itheima, itcast, javaee, true]

    }

}

3.3 泛型的定义与使用

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

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

* 定义格式:
		修饰符 class 类名<代表泛型的变量> {  }
		例如:ArrayList的定义 ======>>>> public class ArrayList<E>{}
* 确定泛型类型:
		在创建对象的时候确定泛型
// 定义含有泛型的类!
public class MyArrayList<A>{  // A是一个预支的类型,在创建对象的时候来进行确定,一旦确定,那么操作对象的时候,指定的类型就只能是创建对象时的类型
    
    public static void main(String[] args){
        // 在创建对象的时候确定泛型
		MyArrayList<String> list1 = new MyArrayList<>(); // list1以后在编译阶段只能操作String类型的数据
        // 在创建对象的时候确定泛型
        MyArrayList<Boolean> list2 = new MyArrayList<>();// list2以后在编译阶段只能操作Boolean类型的数据
    }
    
    // 定义了很多其它功能的方法,比如向集合中添加数据的方法
    
}

// 泛型类的学习,可以参考ArrayList集合!!!

3.3.2 含有泛型的方法

* 定义格式:
		修饰符 <代表泛型的变量类型> 返回值类型 方法名(参数){  }
* 确定泛型类型:		
		调用方法时,确定泛型的类型
/*
    泛型方法:
        修饰符 <代表泛型的变量类型> 返回值类型 方法名(参数){  }

    泛型的类型什么时候确定:调用方法的时候!

 */
public class Test05 {

    public static void main(String[] args) {

        show1("xxx");
        String xxx = show2("xxx");
        Boolean flag = show2(true);

    }

    public static <VIP> void show1(VIP vip){
        System.out.println("xxxx");
    }

    public static <VIP> VIP show2(VIP vip){
        System.out.println("xxxx");
        return vip;
    }

}

3.3.3 含有泛型的接口

* 定义格式:
		修饰符 interface 接口名<代表泛型的变量> {  }
		例如:public interface List<E>
* 确定泛型类型:
	①定义类时确定泛型的类型
	②始终不确定泛型的类型,直到创建对象时,确定泛型的类型	
public interface MyCollection<A> {  // public interface Collection<E>{}
    public void add(A a);
}
public interface MyList<A> extends MyCollection<A> {  // public interface List<E> extends Collection<E>{}

}
/*
     * 定义格式:类似泛型类!
		修饰符 interface 接口名<代表泛型的变量> {  }
		例如:public interface List<E>
    * 确定泛型类型:
        ①定义类时确定泛型的类型
        ②始终不确定泛型的类型,直到创建对象时,确定泛型的类型
 */
public class MyArrayList<A> implements MyList<A> {

    @Override
    public void add(A a) {
        // 。。。。。。
    }

    public static void main(String[] args) {
        // 创建对象的时候确定泛型类型
        MyArrayList<String> ml1 = new MyArrayList<>(); // 此时代表接口中泛型变量A的类型是String类型
        MyArrayList<Integer> ml2 = new MyArrayList<>();// 此时代表接口中泛型变量A的类型是Integer类型
        ml2.add(10);
        ml1.add("xxx");
        //ml1.add(10); // 编译报错!ml1里面的泛型类型为String类型
    }
}
/*
    确定泛型类型:
        定义类时确定泛型的类型
 */
public class MyArray<E> implements MyList<Boolean> { // 虽然确定了接口的泛型类型,然后在定义类的时候也定义了泛型(变量)

    @Override
    public void add(Boolean flag) {

    }

    public static void main(String[] args) {
        // 创建对象
        MyArray<Integer> ml = new MyArray<>(); // 使用另外的一种确定泛型类型的方式(创建对象!) 【MyArray相当于是一个泛型类】
    }
}
/*
    确定泛型类型:
        定义类时确定泛型的类型
 */
public class MyArray implements MyList<Boolean>{

    public static void main(String[] args) {
        // 创建对象
        //MyArray<Integer> ml = new MyArray<>(); // 使用用另外的一种确定泛型类型的方式(创建对象!)

        MyArray m2 = new MyArray();
        m2.add(true); // 给定Boolean类型 【在MyList接口后面没有写泛型类型,那么调用这个方法可以给定任意类型!】

    }

    @Override
    public void add(Boolean b) {

    }
}

3.4 泛型通配符

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

泛型的通配符:不知道使用什么类型来接收的时候,此时可以使用?,?表示未知通配符。

此时只能接受数据,不能往该集合中存储数据。

public class Test06 {

    public static void main(String[] args) {

        // 创建2个集合对象
        Collection<String> coll1 =  new ArrayList<>();
        Collection<Integer> coll2 =  new ArrayList<>();

        // 调用方法
        add(coll1);  // coll1和coll2是Colleciont接口的子类对象!
        add(coll2);  // 若add方法的Collection泛型类型定义为String的时候,此时coll2就添加不进去了!

        //ArrayList<Object> list = new ArrayList<String>(); // 编译报错! 泛型类型不能使用继承!(必须要求2个泛型的类型一致!)
    }

    // 这个方法:后面调用的时候,传递进来的Colleciont子类对象的泛型类型是不确定的!也就是说明参数Colletion的泛型类型不能确定写死!
    public static void add(Collection<?> coll){ // 定义方法给定父接口类型(多态)
		// 此时只能接受数据,不能往该集合中存储数据
        //coll.add("xxx"); // 编译报错!
    }
}

泛型不存在继承关系(左右两侧得一致)

boolean containsAll(Collection<?> c) 如果此集合包含指定集合中的所有元素,则返回 true 。
public class Test07 {

    public static void main(String[] args) {

        Collection<String> coll1 = new ArrayList<>();
        coll1.add("it");
        coll1.add("itnb");

        Collection<String> coll2 = new ArrayList<>();
        coll2.add("itnb");

        // boolean containsAll(Collection<?> c) 如果此集合包含指定集合中的所有元素,则返回 true 。
        System.out.println(coll1.containsAll(coll2)); // true

        Collection<Integer> coll3 = new ArrayList<>();
        coll3.add(100);
        coll3.add(99);

        Collection<Integer> coll4 = new ArrayList<>();
        coll4.add(25);

        System.out.println(coll3.containsAll(coll4)); // false
    }
}

3.5 受限泛型

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

?表示泛型通配符,如果要对?泛型通配符的取值范围进行限制,可以使用泛型限定

3.5.1 泛型的上限

* 格式:  
		类型名称 <? extends 类 > 对象名称
* 意义:  
		只能接收该类型及其子类

3.5.2 泛型的下限

* 格式:  
		类型名称 <? super 类 > 对象名称
* 意义:  
		只能接收该类型及其父类型

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

public class Test08 {

    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);
        getElement1(list2); // 报错!
        getElement1(list3);
        getElement1(list4); // 报错!

        getElement2(list1); // 报错!
        getElement2(list2); // 报错!
        getElement2(list3);
        getElement2(list4);
    }

    // 当前这个方法内部集合的泛型类型只能是: Number类型和它的子类Integer类型
    public static void getElement1(Collection<? extends Number> coll) {
    }

    // 当前这个方法内部集合的泛型类型只能是: Number类型和它的父类Object类型
    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、付费专栏及课程。

余额充值