01.10_学习Java的day20(详解)

一、泛型的概念

1、泛型长什么样?

<类型>

2、为什么要有泛型?

Java5决定对API做修整。
原来设计集合(不仅仅是集合)等类型时,并不确定集合中的元素是什么类型的。
集合是一种容器,在Java中是用来装对象的容器。
它只能设计成可以装任意类型的对象。

导致:(1)不安全(2)麻烦

生活中:瓶子,可以装各种液体。
假设所有的瓶子都没有标签。那么超市中所有瓶子都装了各种液体,但是没有说明是什么。
客户就不敢买。 或者说 买的时候,需要打开闻一闻、尝一尝才能确定是什么。

  中药铺。柜子/抽屉。生产柜子的厂家一开始不知道这个抽屉中用来装什么,什么都可以往里装。
  中药铺,如果所有抽屉都没有标签。很危险。

泛型就是为了解决类型安全的问题。同时还可以避免类型转换,使得使用更方便。
换句话说,泛型的好处:(1)安全(2)使用更简便

Java中编写方法,现在知道方法的功能是什么,例如:求两个整数的最大值。
public int max(int a, int b){
return a > b ? a : b;
}
未知的数据:两个整数,通过形参表示。
因为形参不需要在编写方法时确定值,由调用者在调用时确定它的值
受形参这种形式的启发,我们设计了泛型。
泛型分为类型形参和类型实参。代表类型。 为了区分,把方法()中的称为数据形参和数据实参。代表数据。

ArrayList, 这个E:类型形参
ArrayList:这个String:类型实参

public class TestGeneric {
   /* public int max(){
        int a;
        int b;
        //如果不是形参,就需要在这里手动初始化
        return a > b ? a : b;
    }*/
   //如果使用成员变量,那么就需要在创建对象时,为a,b初始化。如果此时这个a,b只是在这个方法中使用,a,b 的交给对象,就太麻烦了
/*   int a;
   int b;
   public int max(){
       return a > b ? a : b;
   }*/

    //形参的好处:在设计这个方法时,不需要确定它的值,在调用时,再由使用者确定它的值
    public int max(int a, int b){
        return a > b ? a : b;
    }

    @Test
    public void test01(){
        ArrayList list = new ArrayList();//容器,集合的对象
        list.add("hello");
        list.add("java");
        list.add(1);//Integer对象  1会自动装箱为Integer的对象
        list.add(new Date());
        
        //要从这个集合中取出一个元素
        Object value = list.get(3);//3是索引,下标
        System.out.println(value);

        //如果按照我们程序一开始的意图,我本来是想着往这个集合中装的都是String。
//        发现取出来的是Date对象,不安全

        //就算我知道元素是字符串,取出来也要进行类型的转换,很麻烦
        String obj = (String) list.get(0);

    }

    @Test
    public void test03() {
        //在使用ArrayList确定了元素的/泛型的类型是String
        ArrayList<String> list = new ArrayList<String>();//容器,集合的对象
        list.add("hello");
        list.add("java");
//        list.add(1);//报错,可以提前避免不符合的类型的对象,装到这个容器中,提高了安全性
//        list.add(new Date());//报错

        String s = list.get(0);//不需要类型转换,使用更简便了。
    }

    public void test02(){
        Student stu = new Student();
        int result = stu.compareTo(new Date());
    }
    
    
}
/*
如果没有泛型,Comparable是表示任意类型的对象的自然比较规则,设计为Object类型
 */
class Student implements Comparable{
    private String name;
    private int score;

    @Override
    public int compareTo(Object o) {
        //必须向下转型
        Student stu = (Student) o;//有风险
        return 0;
    }
}

二、泛型的语法

1、泛型的使用常见分为两大类

(1)泛型类、泛型接口
(2)泛型方法

2、泛型类、泛型接口

(1)声明一个泛型类或泛型接口的语法格式:
【修饰符】 class 类名<泛型类型形参列表> 【extends 父类】【implements 接口们】{
//…
}
【修饰符】 interface 接口名<泛型类型形参列表> 【extends 父接口们】{
//…
}

例如:源码中
public interface Comparable
public interface Comparator
public abstract class Enum<E extends Enum> implements Comparable, Serializable
public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable

自定义一个:
例如:声明一个学生类型,但是这个学生类型中的成绩的类型未知。
因为使用这个学生管理系统的老师对学生的成绩的类型不能统一。
语文老师:成绩应该标识为:优秀、良好、及格、不及格..  String
数学老师:成绩应该标识为:89.5,34.0....              double
英语老师:成绩应该标识为:A,B,C,D...                 char

(2)如何确定泛型形参的具体类型?
A:在创建这个类的对象时,可以指定泛型形参的具体类型
B:在继承泛型类或实现泛型接口时,可以指定泛型形参的具体类型

注意:泛型实参类型必须是引用数据类型,不能是基本数据类型。
泛型形参是代表对象的类型。

(3)泛型类上的泛型形参的使用要求?
A:强烈建议大家泛型形参使用单个的大写字母,不要使用单词
因为单词容易和现有的API中类型或自定义的其他类型无法区别。

B:泛型类/接口上的泛型形参这个类型绝对不能用于类或接口中的静态成员上

C:泛型类/接口上的泛型形参的类型可以设置上限
<泛型形参 extends 父类/父接口>
这个上限还可以是多个。
例如:<T extends Number & Comparable & Cloneable>
要求上限的类只有一个,而且必须在第一个,接口可以多个。

D:泛型类/接口上的泛型形参的类型可以多个
例如:ArrayList
Map<K,V>

(4)泛型的擦除
如果泛型形参没有上限,泛型擦除(没有指定时)按照Object处理。
如果泛型形参设定上限,泛型擦除(没有指定时)按照第一个上限处理。

public class TestGenericClass {
    @Test
    public void test06(){
        //泛型的擦除
        ArrayList list= new ArrayList();

        XueSheng x = new XueSheng();

        XueYuan x2 = new XueYuan();
//        x2.setScore();//形参类型识别为Number
    }

    @Test
    public void test01(){
        //现在是语文老师在用
        XueSheng<String> x = new XueSheng<String>("张三","优秀");

    }

    @Test
    public void test02(){
        //现在是数学老师在用
//        XueSheng<double> x = new XueSheng<double>("张三",89.5);
        //不能使用基本数据类型,改用包装类
        XueSheng<Double> x = new XueSheng<Double>("张三",89.5);
        Double score = x.getScore();
    }

    @Test
    public void test03(){
        EnglishXueSheng x = new EnglishXueSheng();
//        x.setScore(89.0);
        x.setScore('A');
    }

    @Test
    public void test04(){
        //使用有泛型上限的泛型类XueYuan<T extends Number>
//        XueYuan<String> x = new XueYuan<>();//不能指定String,因为String不是Number或Number的子类
        XueYuan<Integer> x1 = new XueYuan<Integer>();//不能指定String,因为String不是Number或Number的子类
        XueYuan<Double> x2 = new XueYuan<Double>();//不能指定String,因为String不是Number或Number的子类
    }

}

/*
在XueSheng这个类中,凡是表示学生的成绩的类型,统统都用T表示。
T:泛型形参
这个T的类型什么时候明确?
 */
class XueSheng<T>{
    private String name;
    //这个学生类型中的成绩的类型未知。
    private T score;//这个成绩在整个类中都要使用,所以在类名后面用一个泛型形参来代表它的未知的类型

    public XueSheng() {
    }

    public XueSheng(String name, T score) {
        this.name = name;
        this.score = score;
    }

    public String getName() {
        return name;
    }

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

    public T getScore() {
        return score;
    }

    public void setScore(T score) {
        this.score = score;
    }

    @Override
    public String toString() {
        return "XueSheng{" +
                "name='" + name + '\'' +
                ", score=" + score +
                '}';
    }
}

//英语老师,你的给我专门生成一个学员的类型
//英语的老师的学生成绩是确定的,是char(Character)
class EnglishXueSheng extends XueSheng<Character>{

}

//实现泛型接口:java.lang.Comparable<T>
//它的抽象方法:public int compareTo(T o);
class Employee implements Comparable<Employee>{
    private int id;
    private String name;

    @Override
    public int compareTo(Employee o) {
        return this.id - o.id;
    }
}

//以下声明泛型类的形式是不建议的
/*
class MyClass1<String>{
    private String info;
}
class MyClass2<Type>{
    private Type other;
}*/

//以下使用泛型类上的泛型形参是错误的
//我们说T在(1)new对象时(2)继承时确定,而调用method方法时,可能没有对象,也没有继承,所以T是未知的
class MyClass<T>{
/*    public static void method(T t){
        //....
    }*/
}

//以下声明泛型形参,指定了上限
//假设声明这个学生的类型时,成绩的类型也不确定,但是成绩限定为数值类型,不能是非数值类型
//数值类型:int,byte,short,long,double,float,不能是String等
//所有数值类型都是java.lang.Number的子类
//这个T必须是Number或Number的子类
class XueYuan<T extends Number>{
    private T score;

    public void setScore(T score) {
        this.score = score;
    }
}

//这个T必须是Number或Number的子类,并且这个类型实现了Comparable和Cloneable接口
class XueYuan2 <T extends Number  & Comparable<T> & Cloneable>{

}

//以下泛型类,声明了多个泛型形参
class Demo<T,U,K,V>{

}

3、泛型方法

(1)什么是泛型方法,语法格式

【修饰符】 <泛型类型形参列表> 返回值类型 方法名(【形参列表】)【throws 异常列表】{
}

说明:A: <泛型类型形参列表> ,泛型形参类型可以多个,例如:,<T,U>
B:泛型形参类型也可以指定上限,例如:<T extends Comparable>
C:泛型方法中的泛型形参类型,只在本方法有效

(2)为什么会需要泛型方法呢?

A:泛型类或泛型接口上面的泛型类型是不能用于静态成员上的,
那么当静态方法中想要使用泛型时,就需要单独声明泛型类型。

B:当某个类或接口不是泛型类或泛型接口,现在想要增加一个方法,这个方法是非静态方法,
它的形参类型或返回值类型未知,也可以单独为这个方法声明泛型形参

例如:我要声明一个数组工具类MyArrays,它包含一个排序方法
public static void sort(Object[] arr)

public class TestGenericMethod {
    @Test
    public void test01(){
        Student[] arr = new Student[2];
        arr[0] = new Student();
        arr[1] = new Student();
        MyArrays.sort1(arr);//运行会报错,因为Student没有实现Comparable接口
    }

    @Test
    public void test02(){
        Student[] arr = new Student[2];
        arr[0] = new Student();
        arr[1] = new Student();
//        MyArrays.sort2(arr);//编译会报错,因为Student没有实现Comparable接口,把隐患提前告知
    }
}
class Student{

}
class MyArrays{
    //按照元素的自然顺序比较大小
    //自然顺序就是Comparable<T>接口的int compareTo(T t)的比较规则,就是自然顺序的比较规则
    //不知道数组的元素的类型,使用Object处理,需要类型转换,不安全,也麻烦
    public static void sort1(Object[] arr){
        for (int i=1; i<arr.length; i++){
            for (int j=0; j<arr.length-i; j++){
                //比较两个对象的大小,为了调用compareTo方法,必须向下转型为Comparable<T>
               Comparable c = (Comparable) arr[j];//有风险,只有运行时,这个问题才能显示出来
                if(c.compareTo(arr[j+1]) > 0){
                    Object temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] =temp;
                }
            }
        }
    }

    public static  <T extends Comparable<T>> void sort2(T[] arr){
        for (int i=1; i<arr.length; i++){
            for (int j=0; i<arr.length-i; j++){
                //arr[j]是T类型,T一定是实现了Comparable接口的类型
                if(arr[j].compareTo(arr[j+1]) > 0){
                    T temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] =temp;
                }
            }
        }
    }
}

三、泛型的通配符

1、为什么会有泛型的通配符?

当我们使用某个泛型类/接口声明变量的时候,按理说,我们需要为它的泛型形参指定具体的类型。
例如:
class XueSheng,使用它是应该:XueSheng t;
class XueSheng,使用它是应该:XueSheng t;
但是,如果此时使用它的时候,这个具体的类型也不能确定,那怎么办?
A:泛型擦除
B:泛型通配符

例如:用这个泛型类/接口声明形参

2、形式

(1)<?>:?代表任意类型
只读
(2)<? extends 上限>:?代表上限或上限的子类
只读
(3)<? super 下限>:?代表下限或下限的父类
修改只能接收下限或下限的子类类型的对象。
例如: <? super Number>
实参: XueSheng或XueSheng
T类型的score只能设置为Number的子类Integer的对象

public class TestWild {
    @Test
    public void test01(){
        XueSheng<String> xueSheng1 = method1(new XueSheng<String>());
        XueSheng<Double> xueSheng2 =method1(new XueSheng<Double>());
        XueSheng<Integer> xueSheng3 =method1(new XueSheng<Integer>());
        String s1 = xueSheng1.getScore();
        Double s2 = xueSheng2.getScore();
        Integer s3 = xueSheng3.getScore();
        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s3);
    }

    @Test
    public void test02(){
        XueSheng<String> xueSheng1 = method2(new XueSheng<String>());
        XueSheng<Double> xueSheng2 =method2(new XueSheng<Double>());
        XueSheng<Integer> xueSheng3 =method2(new XueSheng<Integer>());
        String s1 = xueSheng1.getScore();
        Double s2 = xueSheng2.getScore();
        Integer s3 = xueSheng3.getScore();
        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s3);
    }

    //?不是代表Object,而是代表未知的任意引用数据类型
    public XueSheng method2(XueSheng<?> x){//擦除,按照Object,可以,就是麻烦点
//        x.setScore("优秀");//?虽然是代表任意类型,但是这个值变为只读,不能修改
        //因为?是表示任意类型,不确定类型,那么你赋值为什么值都是错误的
        return x;
    }

    //XueSheng后面没有写<>,表示擦除
    public XueSheng method1(XueSheng x){//擦除,按照Object,可以,就是麻烦点
        x.setScore("优秀");//形参T,按照Object处理,有风险
        x.setScore(12.8);
        x.setScore(1);
        return x;
    }

    @Test
    public void test03(){
//        method3(new XueSheng<String>());//错误,String不是Number或它子类
        method3(new XueSheng<Double>());
        method3(new XueSheng<Integer>());
    }
    public void method3(XueSheng<? extends Number> x){
        //因为?代表Number或Number的任一种子类,也是不确定的,可能是Integer,也可能是Double
        //如果是Double,设置为1就是错误的
//        x.setScore(1);//不能修改
    }

    @Test
    public void test04(){
//        method4(new XueSheng<String>());//错误,String不是Number或它的父类
//        method4(new XueSheng<Double>());//错误,String不是Number或它的父类
//        method4(new XueSheng<Integer>());//错误,String不是Number或它的父类
        method4(new XueSheng<Object>());
    }
    public void method4(XueSheng<? super Number> x){
        x.setScore(1);//可以,因为?代表Number或Number的父类,可以接收Integer对象
//        x.setScore("优秀");//错误,因为?代表Number或Number的父类,String类型不是Number家族
        x.setScore(1.0);//可以,因为?代表Number或Number的父类,可以接收Double对象
    }
}
class XueSheng<T>{
    private T score;

    public void setScore(T score) {
        this.score = score;
    }

    public T getScore() {
        return score;
    }

    @Override
    public String toString() {
        return "XueSheng{" +
                "score=" + score +
                '}';
    }
}

一、集合(非常重要)

开发中必用,面试必考。

1、集合是什么?用来干什么的?

集合是容器,是用来装对象的容器。

变量也是可以说是容器,装一个数据值。
数组也是容器,既可以用来装基本数据类型的值,也可以用来装对象。
集合是容器,是用来装对象的容器。不能装基本数据类型的值。如果把基本数据类型的值装到集合中,会自动装箱为包装类的对象。

2、容器分为很多种。

它把容器分为两大类,抽取它们的共同特征,主要抽取的是行为特征,即行为标准,形成了两大接口:
(1)Collection:这个系列的集合是装一组对象的
比喻:单身party
(2)Map:这个系列的集合是装一对一对的映射关系的,映射关系(key,value)
比喻:情侣party,家庭party

二、Collection系列

1、Collection系列的集合和之前学习的数组有什么不同?

(1)数组的长度是不可变、集合的大小是可变的
(2)实际开发中,一般使用数组存储基本数据类型的值,使用集合装对象比较多

2、java.util.Collection接口:这里的E是ElementType的首字母,表示元素的类型。即这个E是代表集合元素的类型。

先针对集合(容器)都有什么操作。
往容器中放对象,取对象,查询对象,修改对象,简称增删该查。
(1)添加
boolean add(E e ):添加一个元素对象
boolean addAll(Collection<? extends E> c) :添加多个元素对象,把c集合中的所有元素添加到当前集合中,
<? extends E>:上限,c集合中的元素的类型<=当前集合中元素类型E

    this = this ∪ c;

(2)查询
A:查询集合中元素的个数
int size()
B:查询集合是否包含某个/某些元素
boolean contains(Object o) :判断当前集合是否包含o对象
boolean containsAll(Collection<?> c) :判断当前集合是否包含c集合中的所有元素
即判断c集合是否是当前集合的子集
C:判断当前集合是否是空集合
boolean isEmpty()

(3)删除
void clear() :清空集合
boolean remove(Object o) :删除一个对象
boolean removeAll(Collection<?> c) :删除多个对象
this = this - this ∩ c;
boolean retainAll(Collection<?> c) :保留部分元素,删除this和c不同的元素
this = this ∩ c;

(4)修改(没有)

(5)集合的遍历
遍历:挨个访问集合中的元素
A:把集合转为数组(了解)
Object[] toArray()

B:使用迭代器遍历(掌握)
Iterator iterator()
迭代:等价于遍历,挨个访问集合中的元素
迭代器:专门遍历集合等容器的对象。
所有迭代器的抽象接口类型:java.util.Iterator
它的对象不是程序员new的,而是通过集合对象.iterator()获取的

3、如何使用Iterator迭代器?

(1)boolean hasNext() :判断当前迭代器游标后面是否还有元素可以迭代/访问。
(2)E next():取出当前游标指向的元素
(3)void remove() :删除刚刚迭代的元素
当我们使用Iterator迭代器遍历集合的时候,不能调用集合自己的remove删除方法,而要用迭代器自己的remove方法。
*/

public class TestContainer {
    @Test
    public void test12() {
        Collection<String> c = new ArrayList<>();
        c.add("hello");
        c.add("java");
        c.add("atguigu");

        //删除元素中长度>5的字符串
        //使用集合的remove()或removeAll()无法实现
        //只能一边遍历,一边根据条件删除
        Iterator<String> iterator = c.iterator();
        while(iterator.hasNext()){
            String element = iterator.next();
            if(element.length()>5){
//                iterator.remove();
                //以下写法错误,但是很容易犯
//                c.remove(element);//ConcurrentModificationException:并发修改集合的异常
                //因为c.remove(element)这个方法删除是独立与迭代器之外的,集合自己的删除方法,它删除不会通知迭代器
                //迭代器在遍历集合之初有一个游标,它一开始是要记录集合的元素个数的,
                //此时集合把元素删除了,迭代器不知道,游标就会指向不存在的位置。
            }
        }
        System.out.println(c);//[hello, java]
    }

    @Test
    public void test11() {
        Collection<String> c = new ArrayList<>();
        c.add("hello");
        c.add("java");
        c.add("atguigu");

        //访问集合中的字符串元素的长度
        //iterator是迭代器对象,用来遍历集合的
        Iterator<String> iterator = c.iterator();
        while(iterator.hasNext()){
            String element = iterator.next();
            System.out.println(element + "长度:" + element.length());
        }
    }

    @Test
    public void test10() {
        Collection<String> c = new ArrayList<>();
        c.add("hello");
        c.add("java");
        c.add("atguigu");

        //转为数组遍历
        //访问每一个字符串元素的长度
        Object[] objects = c.toArray();
        for (int i = 0; i < objects.length; i++) {
            Object object = objects[i];
            System.out.println(object + "长度为:" + ((String)object).length());
        }
    }
    
    @Test
    public void test09() {
        Collection<String> c = new ArrayList<>();
        c.add("hello");
        c.add("java");
        c.add("atguigu");

        Collection<String> c2 = new ArrayList<>();
        c2.add("hello");
        c2.add("java");
//        c2.add("ag");

        System.out.println(c.contains("hello"));//true
        System.out.println(c.containsAll(c2));//true
    }

    @Test
    public void test08() {
        Collection<String> c = new ArrayList<>();
        c.add("hello");
        c.add("java");
        c.add("atguigu");

        Collection<String> c2 = new ArrayList<>();
        c2.add("hello");
        c2.add("java");
        c2.add("chailinyan");

        c.retainAll(c2);
        System.out.println(c);//[hello, java]
    }

    @Test
    public void test07() {
        Collection<String> c = new ArrayList<>();
        c.add("hello");
        c.add("java");
        c.add("atguigu");

        Collection<String> c2 = new ArrayList<>();
        c2.add("hello");
        c2.add("java");
        c2.add("chailinyan");

        c.removeAll(c2);
        System.out.println(c);//[atguigu]
        System.out.println(c2);//[hello, java, chailinyan]
    }
    @Test
    public void test06() {
        Collection<String> c = new ArrayList<>();
        c.add("hello");
        c.add("java");
        c.add("atguigu");

        //remove方法删除元素是调用元素的equals方法
        c.remove("hello");
        c.remove(new String("java"));
        System.out.println(c);//[atguigu]
    }

    @Test
    public void test05() {
        Collection<String> c = new ArrayList<>();
        c.add("hello");
        c.add("java");
        c.add("atguigu");

        //集合的删除
        c.clear();
        System.out.println(c);
    }

    @Test
    public void test04() {
        Collection c1 = new ArrayList();
        c1.add(1);
        c1.add(2);

        Collection c2 = new ArrayList();
        c2.add(3);
        c2.add(4);

        c1.add(c2);//add()添加一个对象,此处把c2当成一个对象添加
        System.out.println(c1);//[1, 2, [3, 4]]
    }

    @Test
    public void test03() {
        Collection<Number> c1 = new ArrayList<>();
        c1.add(1);
        c1.add(2);

        //Integer<Number,是Number的子类
        Collection<Integer> c2 = new ArrayList<>();
        c2.add(3);
        c2.add(4);

//        c1.add(c2);//add()添加一个对象,此处把c2当成一个对象添加
        c1.addAll(c2);
        System.out.println(c1);//[1, 2, 3, 4]
    }

    @Test
    public void test02(){
        Collection<String> c = new ArrayList<>();
        c.add("hello");
        c.add("java");
        c.add("atguigu");

        System.out.println("元素的个数:" + c.size());
        System.out.println("集合的元素有:"+c);//打印对象,自动调用对象的toString方法
        //集合的元素有:[hello, java, atguigu] 说明ArrayList集合重写了toString方法
    }
    @Test
    public void test01(){
        /*
        Collection<E>是接口,是不能直接new对象的,必须创建它的实现类的对象。
        它的实现类有很多,最常用的有:ArrayList<E>
        这里写多态引用的目的是为了让大家关注Collection接口的方法,
        因为多态引用,这个变量c编译时类型是按照Collection处理,无法调用子类或实现类增加的方法。
         */
        Collection<String> c = new ArrayList<>();

        /*
        Collection<String> c = new ArrayList<String>(); //标准的指定泛型的格式
        Collection<String> c = new ArrayList<>();//右边<>没有省略,这种写法是JDK1.7之后才支持,和上面一样


        Collection c = new ArrayList<String>();//编译时按左边类型编译,相当于擦除了Collection<E>这个泛型,E按照Object处理
                                                //运行时按照右边处理,泛型是<String>类型。
        Collection c = new ArrayList();//编译时按左边类型编译,相当于擦除了Collection<E>这个泛型,E按照Object处理
         */


    }
}

java.lang.Comparable与java.util.Comparator:
自然比较接口 定制比较接口
java.lang.Iterable与java.util.Iterator
Iterable 表示可迭代的,实现它表示可以使用foreach遍历。
Iterator表示迭代器的公共接口
使用foreach遍历,本质上是使用 Iterator迭代器在迭代/遍历集合。

4、java.lang.Iterable接口

刚才迭代器:java.util.Iterator迭代器接口
Iterable接口的抽象方法:
Iterator iterator()
凡是实现Iterable接口的集合等类型,必须实现Iterator iterator(),提供迭代器对象,用于foreach遍历。

A:Java中的所有数组类型都实现了Iterable接口。表示所有数组都可以使用foreach遍历。
B:Java中的Collection接口继承了Iterable接口,意味着凡是实现Collection接口的,就会实现Iterable接口。

5、什么是foreach循环

(1)语法结构
for(元素的类型 元素名 : 数组/Collection集合名/其他实现了Iterable接口的容器对象名){
}

(2)使用foreach注意
因为它本质上也是使用Iterator进行迭代的,所以在使用foreach遍历集合的过程中,
也不要调用集合的add,remove等方法来修改集合的元素。
换句话说,使用foreach遍历集合只能查看,或通过集合对象修改对象的属性。但是修改集合元素的个数。

public class TestIterable {
    @Test
    public void test02(){
        Collection<String> c = new ArrayList<>();
        c.add("hello");
        c.add("java");
        c.add("atguigu");

        //String:元素的类型
        //str:自己命名,表示元素名称
        //c:集合对象名
        for (String str : c) {
            System.out.println(str);
        }
    }

    @Test
    public void test01(){
        int[] arr = {1,2,3,4,5};
        //快捷模板:iter
        //int:元素的类型
        //element:自己命名,表示元素名称
        //arr:表示数组名
        for (int element : arr) {
            System.out.println(element);
        }
    }
}

Collection 层次结构 中的根接口。Collection 表示一组对象,这些对象也称为 collection 的元素。
一些 collection 允许有重复的元素,而另一些则不允许。
一些 collection 是有序的,而另一些则是无序的。
JDK 不提供此接口的任何直接 实现:它提供更具体的子接口(如 Set 和 List)实现。

三、List系列的集合

1、List系列的集合的特点:

(1)允许元素重复
(2)有序的(也称为序列):可以根据元素的【index】来精确的访问某个元素

2、List接口比Collection接口更具体一点点,它增加了很多新的方法,这些方法都是和[index]有关

List是Collection的子接口,Collection接口有的它也有,它还有Collection没有,
下面主要演示Collection没有的方法:
(1)添加
void add(int index, E element)
boolean addAll(int index, Collection<? extends E> c)
(2)删除
E remove(int index)
(3)修改
E set(int index, E element) :修改/替换[index]位置的元素为element
(4)查询
E get(int index) :获取[index]位置的元素
int indexOf(Object o) :查询o在当前集合中下标,如果有多个,返回第一个的下标,如果没有,返回-1
int lastIndexOf(Object o) :查询o在当前集合中下标,如果有多个,返回最后一个的下标,如果没有,返回-1
List subList(int fromIndex, int toIndex) :截取[fromIndex,toIndex)部分的元素构成一个新的List集合

(5)遍历
原来Collection的遍历方式仍然支持
新增了:
ListIterator listIterator()
ListIterator listIterator(int index)

3、ListIterator迭代器,它是Iterator的子接口

Iterator迭代器有三个方法:
(1)boolean hasNext()
(2)E next()
(3)void remove()

ListIterator增加了方法:
列表迭代器,允许程序员按任一方向遍历列表、迭代期间修改列表,并获得迭代器在列表中的当前位置。
支持在遍历的过程中,增删改查元素:add(E e),remove(),set(E e),E next()
支持在遍历过程中,获取下标信息:nextIndex(),previousIndex()
支持任一方向遍历:next(), previous();

public class TestList {
    @Test
    public void test12() {
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("world");
        list.add("atguigu");
        ListIterator<String> listIterator = list.listIterator(list.size());
        //list.listIterator(list.size()):游标在最后
        System.out.println("从后往前遍历:");
        while(listIterator.hasPrevious()){
            System.out.println(listIterator.previous());
        }
    }


    @Test
    public void test11() {
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("world");
        list.add("atguigu");
        ListIterator<String> listIterator = list.listIterator();
        //list.listIterator();方法,游标在最前面,所以下面的循环不进去
        System.out.println("从后往前遍历:");
        while(listIterator.hasPrevious()){
            System.out.println(listIterator.previous());
        }
    }

    @Test
    public void test10() {
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("world");
        list.add("atguigu");

        ListIterator<String> listIterator = list.listIterator();
        System.out.println("从前往后遍历:");
        while(listIterator.hasNext()){
            System.out.println(listIterator.next());
        }
        System.out.println("---------------------------");
        System.out.println("从后往前遍历:");
        while(listIterator.hasPrevious()){
            System.out.println(listIterator.previous());
        }
    }

    @Test
    public void test09() {
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("world");
        list.add("atguigu");

        int count = 0;
        for (String s : list) {
            System.out.println("第" + ++count +"个元素:"+ s);
        }
        System.out.println("---------------------------");
        count = 0;
        for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
            String next =  iterator.next();
            System.out.println("第" + ++count +"个元素:"+ next);
        }
        System.out.println("-------------------------");
        ListIterator<String> listIterator = list.listIterator();
        while(listIterator.hasNext()){
            String next = listIterator.next();
            System.out.println("第" + listIterator.nextIndex() +"个元素:"+ next);
        }
    }

    @Test
    public void test08() {
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("world");
        list.add("atguigu");

        ListIterator<String> listIterator = list.listIterator();
        while(listIterator.hasNext()){
            String s = listIterator.next();
            if(s.equals("world")){
                listIterator.set("java");
            }
        }
        System.out.println(list);//[hello, java, atguigu]
    }


    @Test
    public void test07() {
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("world");
        list.add("atguigu");

        ListIterator<String> listIterator = list.listIterator();
        while(listIterator.hasNext()){
            String s = listIterator.next();
            if(s.equals("world")){
                listIterator.add("java");//迭代器的增加元素的方法
            }
        }
        System.out.println(list);//[hello, world, java, atguigu]
    }

    @Test
    public void test06() {
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("world");
        list.add("hello");
        list.add("java");
        list.add("atguigu");

        //[0,2)
        List<String> subList = list.subList(0, 2);
        System.out.println(subList);//[hello, world]
    }
    @Test
    public void test05() {
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("world");
        list.add("hello");
        list.add("java");
        list.add("atguigu");

        System.out.println(list.get(1));
        System.out.println(list.indexOf("hello"));
        System.out.println(list.lastIndexOf("hello"));
    }

    @Test
    public void test04() {
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("world");
        list.add("hello");
        list.add("java");
        list.add("atguigu");

        list.set(2,"chailinyan");
        System.out.println(list);

    }
    @Test
    public void test03() {
        List<Integer> list = new ArrayList<>();
        list.add(10);
        list.add(2);
        list.add(3);
        list.add(1);
        list.add(5);

        /*
        list.remove(int index)
        list.remove(Object obj)
        list.remove(1)和list.remove(int index)更匹配
         */
//        list.remove(1);
        list.remove(new Integer(1));//删除元素值是1的元素
        System.out.println(list);//[1, 3, 4, 5]
    }

    @Test
    public void test02(){
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("world");
        list.add("hello");
        list.add("java");
        list.add("atguigu");

        System.out.println(list);
        list.remove(2);
        System.out.println(list);
    }

    @Test
    public void test01(){
        //这里左边使用List,也是为了关注List接口中的方法
        //注意导包不要导错了,java.util.List(集合)和java.awt.List(做图形化界面的下拉列表用)
        List<String> list = new ArrayList<>();
        list.add("张三");
        list.add(0,"李四");

        System.out.println(list);//[李四,张三]
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值