Java进阶学习2:【集合和数组区别、Collection接口、迭代器、增强for、泛型】

沙海
沙海
沙海
165
文章
2
评论
2018年10月7日16:55:21Java进阶学习2:【集合和数组区别、Collection接口、迭代器、增强for、泛型】已关闭评论 154 11897字阅读39分39秒

01. 集合的介绍以及和数组的区别

  • 集合:集合是java中提供的一种容器,可以用来存储多个数据。
  • 集合和数组的区别 数组的长度是固定的。集合的长度是可变的。 数组可以存储任意类型数据。集合存储的都是引用数据类型。

02. 集合的继承体系

参考 Java基础学习10:【Collection、泛型】中的集合的继承体系图:

Java进阶学习2:【集合和数组区别、Collection接口、迭代器、增强for、泛型】

03. Collection中的常见方法

/*
    Collection是所有单列集合的根接口

    常见的方法:
        (常用)public boolean add(E e) : 把给定的对象添加到当前集合中 。
        public void clear() :清空集合中所有的元素。
        public boolean remove(E e) : 把给定的对象在当前集合中删除。
        public boolean contains(Object obj) : 判断当前集合中是否包含给定的对象。
        public boolean isEmpty() : 判断当前集合是否为空。
        (常用)public int size() : 返回集合中元素的个数。
        public Object[] toArray() : 把集合中的元素,存储到数组中
 */
public class Demo01CollectionMethod {
    public static void main(String[] args) {
        //创建集合
        Collection<String> c = new ArrayList<>();
        //public boolean add(E e) : 把给定的对象添加到当前集合中
        c.add("张三");
        c.add("李四");
        c.add("王叔叔");
        System.out.println("c:" + c); //[张三, 李四, 王叔叔]
        //public void clear() :清除集合中的元素
        //c.clear();
        //System.out.println("清除后的c:" + c);

        //public boolean remove(E e) :直接删除指定的元素。 如果删除成功返回true。 
        //boolean flag = c.remove("李五"); //直接删除李四这个元素
        //System.out.println("c:" + c);
        //System.out.println("flag:" + flag);

        //public boolean contains(Object obj) : 判断集合中是否包含指定的元素,如果包含返回true
        //System.out.println(c.contains("李四")); //判断集合c中是否包含李四

        //public boolean isEmpty():判断集合是否为空。 
        //c.clear();
        //System.out.println(c.isEmpty());

        //public int size() : 返回集合的大小,集合中有几个元素,大小就是几
        //System.out.println(c.size());//3

        //public Object[] toArray() :将集合转成一个数组
        Object[] objs = c.toArray();
        //将数组中的内容输出
        System.out.println(Arrays.toString(objs));
    }
}

04. 迭代器的遍历流程

我们之前都是使用for循环结合索引的方式遍历集合,但是这种方式并不适用于所有的集合,因为有些集合是没有索引的。
有一种通用的遍历方式,叫做迭代器遍历,可以适用于所有的集合。

什么是迭代器?
    迭代器其实就是遍历集合的一个工具,内部有一个光标, 这个光标最开始指向集合的最开头的位置

如何获取集合的迭代器?
    我们可以使用Collection中的方法iterator去获取一个迭代器对象。
    Iterator<E> iterator(): 获取一个迭代器并返回。

Iterator表示迭代器,如果要使用迭代器遍历集合,那么还需要使用迭代器里面的两个方法
    boolean hasNext(): 判断是否还有没有元素可以获取。如果还有元素可以获取,返回true。
    E next(): 获取当前位置的元素,然后把光标向后移动。

迭代器的遍历流程:
    1. 先调用集合的iterator方法,获取迭代器对象。
    2. 调用迭代器的hashNext方法,判断当前位置是否有元素可以获取。
    3. 如果有元素可以获取,那么就调用迭代器的next方法获取元素,并把光标向后移动。

05. 迭代器的代码实现

public class Demo01Iterator {
    public static void main(String[] args) {
        //定义集合
        Collection<String> c = new ArrayList<>();
        //添加元素
        c.add("诸葛亮");
        c.add("周瑜");
        c.add("王朗");
        //开始使用迭代器遍历
        //先调用集合的iterator方法,获取迭代器对象。
        Iterator<String> iterator = c.iterator();
        //使用循环改进
        while(iterator.hasNext()) {//条件判断集合中是否有元素可以获取,因为只有集合中有元素可以获取,那么我们才获取元素。
            //如果条件成立,表示有元素可以获取,那么我们就在循环体中获取元素
            System.out.println(iterator.next());
        }
        /*
        //调用迭代器的hashNext方法,判断当前位置是否有元素可以获取。
        System.out.println(iterator.hasNext()); //true
        //如果有元素可以获取,那么就调用迭代器的next方法获取元素,并把光标向后移动。
        System.out.println(iterator.next()); //诸葛亮

        System.out.println(iterator.hasNext());//true
        System.out.println(iterator.next());//周瑜

        System.out.println(iterator.hasNext()); //true
        System.out.println(iterator.next());//王朗

        System.out.println(iterator.hasNext());//false
        System.out.println(iterator.next());//NoSuchElementException
        */
    }
}

06. 并发修改异常

/*
    并发修改异常(ConcurrentModificationException)
    当我们使用迭代器遍历集合的时候, 同时使用集合的方法向集合中添加元素或者删除元素,将来就会引发并发修改异常。
 */
public class Demo02Iterator {
    public static void main(String[] args) {
        //创建集合,并添加元素
        Collection<String> c = new ArrayList<>();
        c.add("hello");
        c.add("java");
        c.add("world");
        //使用迭代器遍历集合
        Iterator<String> iterator = c.iterator();
        //使用while循环遍历
        while(iterator.hasNext()) {
            System.out.println(iterator.next());
            //c.add("你好");
        }
    }
}

07. 迭代器的源码分析

参考 Java基础学习10:【Collection、泛型】中的源码分析图:

Java进阶学习2:【集合和数组区别、Collection接口、迭代器、增强for、泛型】

08. 增强for遍历数组

/*
    在JDK5的时候,多了一个新特性,叫做增强for循环, 可以遍历数组和集合。

    格式:
        for(数据类型 变量名 : 容器) {
            //循环体
        }

    格式解析:
        数据类型: 要遍历的容器中保存的是什么类型的数据,这个数据类型就写什么。
        变量名: 该变量表示容器中的每一个元素。
        容器: 可以是数组,也可以是集合。

    增强for其实是一个语法糖, 语法糖指的是本质没有变,但是语法更加的简写。
    增强for遍历数组本质还是普通for循环。
 */
public class Demo01Foreach {
    public static void main(String[] args) {
        //定义一个int数组
        int[] intArr = {11, 22, 33, 44, 55};
        //使用增强for循环进行遍历。
        for(int num : intArr) {
            //num表示数组中的每一个元素
            System.out.println(num);
        }

        System.out.println("========================");
        //定义double数组
        double[] doubleArr = {10.1, 20.2, 30.3, 40.4, 50.5};
        //使用增强for循环进行遍历
        for(double num : doubleArr) {
            System.out.println(num);
        }
        System.out.println("========================");
        //定义一个数组
        int[] arr = {10, 20, 30, 40, 50};
        //使用增强for遍历数组,把里面的每一个元素都改为原来的2倍
        for(int num : arr) {
            num *= 2;
        }
        //输出数组中的每一个元素
        System.out.println(Arrays.toString(arr));
    }
}

09. 增强for遍历集合

/*
    增强for遍历集合

    格式:
        for(数据类型 变量名 : 集合) {
            //循环体
        }

    增强for遍历集合本质还是使用的迭代器进行的遍历。

    增强for的好处缺点:
        优点: 省略了索引的操作, 语法更加简洁。
        缺点: 不能操作索引, 如果在遍历的过程中需要操作索引,那么还需要使用普通for循环
 */
public class Demo02Foreach {
    public static void main(String[] args) {
        //定义集合,用来保存字符串
        Collection<String> c = new ArrayList<>();
        //添加元素
        c.add("hello");
        c.add("world");
        c.add("java");
        //使用增强for遍历集合
        for(String s : c) {
            //s表示集合中的每一个元素
            System.out.println(s);
        }
        System.out.println("=======================");
        //定义集合,用来保存学生,然后遍历。
        Collection<Student> c2 = new ArrayList<>();
        c2.add(new Student("王宝强", 20));
        c2.add(new Student("贾乃亮", 25));
        c2.add(new Student("陈羽凡", 30));
        //使用增强for遍历
        for(Student stu : c2) {
            System.out.println(stu);
        }
    }
}

10. 泛型的介绍以及泛型的好处

/*
    泛型就是一种未知的,不确定的数据类型。

    比如:ArrayList<E>,这个E就是泛型, 当我们使用这个类(创建对象)的时候,那么这个E表示的类型才能确定。

    泛型可以省略,泛型如果省略,相当于泛型是Object

    泛型的好处:
        1. 泛型可以省略向下转型的代码。
        2. 可以将问题从运行时期提前到编译时期。
 */
public class Demo01Generic {
    public static void main(String[] args) {
        //定义集合,不指定泛型
        ArrayList list = new ArrayList();
        //添加数据
        //list.add(100); 运行时期报错
        list.add("hello");
        list.add("world");
        list.add("java");
        //遍历集合,输出集合中每一个字符串的长度
        for (Object obj : list) {
            //obj表示的就是集合中的每一个元素。
            //向下转型
            String str = (String) obj;
            //输出每一个字符串的长度
            System.out.println(str.length());
        }
        System.out.println("=======================");
        //定义集合,指定泛型为String
        ArrayList<String> list2 = new ArrayList<>();
        //添加数据
        //list2.add(100); 编译时期报错
        list2.add("hello");
        list2.add("world");
        list2.add("java");
        //遍历集合,输出每一个字符串的长度
        for(String str : list2) {
            System.out.println(str.length());
        }
    }
}

11. 泛型擦除

/*
    泛型擦除。

    Java中的泛型都是伪泛型,泛型只在源代码阶段有效,一旦编译,泛型就会消失。
 */
public class Demo02Generic {
    public static void main(String[] args) {
        //定义集合
        ArrayList<String> list = new ArrayList<>();
        //添加元素
        list.add("你好");
        list.add("我好");
        list.add("大家好");
        //使用增强for遍历集合
        for(String str : list) {
            System.out.println(str);
        }
    }
}

12. 泛型类的定义

/*
    泛型是一种未知的,不确定的数据类型。

    如果在定义类的时候类名后面加上<T>, 此时就表示定义了一个泛型类, T可以使用任何字母代替.
    并且这个T表示了在类中定义了一个不确定的数据类型T, 这个不确定的,未知的数据类型,需要我们使用这个类的时候才能确定。
 */
public class Factory<T> {
    /*
        定义方法,修理任何东西
     */
    public T method(T t) {
        //修理...
        return t;
    }
​}

测试类:

/*
    演示泛型类
 */
public class Demo03Generic {
    public static void main(String[] args) {
        //创建Factory对象
        Factory<Phone> f = new Factory<>();
        //调用method方法
        Phone p = f.method(new Phone());
        //调用call方法
        p.call();
    }
}

13. 泛型方法的定义

/*
    如果想要缩小泛型的使用范围,延后泛型的确认时间,可以使用泛型方法。
    在泛型方法中定义的泛型, 需要等到调用方法的时候才能确定。

    泛型方法的定义格式:
        修饰符 <泛型> 返回值类型 方法名(参数列表) {
            方法体;
        }
 */
public class Factory<T> {
    /*
        定义方法,接收什么类型的参数,就得到什么类型的结果。
     */
    public <E> E getSame(E e) {
        return e;
    }
}

测试类:

/*
    演示泛型方法
 */
public class Demo04Generic {
    public static void main(String[] args) {
        //创建Factory对象
        Factory<Phone> f = new Factory<>();
        //调用getSame方法
        Phone phone = f.getSame(new Phone());
        Pad pad = f.getSame(new Pad());
    }
}

14. 泛型接口的定义

/*
    泛型接口。

    如果在接口名后面加上<T>, 那么这个接口就变成了泛型接口。
    在泛型接口中定义的泛型类型,可以在整个接口中使用。

    泛型接口的使用:
        1. 在实现类实现接口的时候,直接明确接口中的泛型类型是什么。
        2. 实现类在实现接口的时候,不指定泛型,等到使用实现类的时候再指定。
 */
public interface MyInterface<T> { //定义了一个未知的,不确定的数据类型T。
    //定义方法
    T method(T t);
}
/*
    实现类在实现接口的时候在接口名后面加上<>,明确接口中的泛型类型。
 */
public class MyClassA implements MyInterface<Phone>{
    @Override
    public Phone method(Phone phone) {
        return null;
    }
}
/*
    实现类在实现接口的时候不指定泛型。

    只要定义类或者接口的时候,在当前类或者接口的名字后面写的<T>,才表示在定义泛型T
 */
public class MyClassB<T> implements MyInterface<T>{
    @Override
    public T method(T t) {
        return null;
    }
}
/*
    泛型接口的测试类
 */
public class Demo05Generic {
    public static void main(String[] args) {
        //创建MyClassA对象
        MyClassA ma = new MyClassA();
        Phone p = ma.method(new Phone());

        //创建MyClassB对象
        MyClassB<Pad> mb = new MyClassB<>();
        Pad pad = mb.method(new Pad());
    }
}

15. 泛型通配符的使用

/*
    泛型之间没有继承关系
    ArrayList<Object> 不是ArrayList<String>的父类

    如果想要让泛型匹配任何数据类型,那么可以使用泛型通配符。
    ? 表示泛型通配符,可以接收任何类型的泛型。

    泛型通配符只能被动匹配, 不能主动使用。
 */
public class Demo01Generic {
    public static void main(String[] args) {
        //定义一个集合,里面保存字符串
        ArrayList<String> list = new ArrayList<>();
        //添加元素
        list.add("嫐"); //高富帅
        list.add("嬲"); //白费没
        list.add("挊"); //屌丝
        //调用printArrayList方法,传递保存字符串的集合
        printArrayList(list);

        //定义集合,保存Date对象
        ArrayList<Date> dateList = new ArrayList<>();
        printArrayList(dateList);

        //定义集合
        //ArrayList<?> list2 = new ArrayList<>();
        //list2.add();

    }

    /*
        要求,定义一个方法,可以遍历存放任何数据的集合。
     */
    public static void printArrayList(ArrayList<?> list) {
        //遍历list集合
        for(Object obj : list) {
            System.out.println(obj);
        }
    }
}

16. 泛型限定

/*
    泛型限定: 泛型限定可以对泛型通配符的取值范围进行限制。

    <? extends A>: 泛型类型可以是A类,也可以是A类的子类。 上限。
    <? super A>: 泛型类型可以是A类,也可以是A类的父类。   下限。
 */
public class Demo02Generic {

    public static void main(String[] args) {
        //定义集合,保存Student
        ArrayList<Student> stuList = new ArrayList<>();
        //添加元素
        stuList.add(new Student("小明", 18));
        stuList.add(new Student("小红", 28));
        //调用printArrayList进行遍历
        printArrayList(stuList); //参数集合的泛型要么是Person要么是Person的子类。

        //定义集合,保存Person
        ArrayList<Person> personList = new ArrayList<>();
        printArrayList(personList);

        //定义集合, 保存Object
        ArrayList<Object> objList = new ArrayList<>();
        //printArrayList(objList); 参数集合的泛型要么是Person,要么是Person的子类。不能是其父类

        //method(stuList);//参数集合的泛型要么是Person,要么是Person的父类。不能是其子类。
        method(personList);
        method(objList);
    }

    /*
        定义方法,演示泛型下限
     */
    //表示参数集合的泛型要么是Person,要么是Person的父类。
    public static void method(ArrayList<? super Person> list) { 

    }

    /*
        定义方法,遍历保存Person(也包括其子类比如Student,Teacher)的集合
     */
    //泛型要么是Person,要么是Person的子类
    public static void printArrayList(ArrayList<? extends Person> list) {
        //遍历参数list集合
        for(Person p : list) {
            System.out.println(p);
        }
    }
}

17. 斗地主案例

/*
    斗地主案例的分析
        1. 准备牌。
            a. 定义一个集合,用来保存每张扑克牌。
            b. 向集合中添加54张牌
        2. 洗牌
            洗牌就是打乱集合中元素的顺序。
            我们可以使用Collections工具类中的shuffle方法完成。
                static void shuffle(List<?> list): 打乱集合中元素的顺序。
        3. 发牌
            a. 定义三个集合,用来保存三个玩家手里的牌。
            b. 定义一个集合,用来保存底牌。
            c. 根据牌在集合中的索引进行发牌。
               把索引为0,3,6...的牌发给第一个玩家(索引对3取余结果是0)
               把索引为1,4,7...的牌发给第二个玩家(索引对3取余结果是1)
               把索引为2,5,8...的牌发给第三个玩家(索引对3取余结果是2)
        4. 看牌
            遍历每个玩家手中的牌。
 */
public class Demo01Game {
    public static void main(String[] args) {
        //1. 准备牌。
        //定义一个集合,用来保存每张扑克牌。
        ArrayList<String> poker = new ArrayList<>();
        //向集合中添加54张牌
        //定义数组,保存花色
        String[] colors = {"♠", "♥", "♣", "♦"};
        //定义数组,保存点数
        String[] nums = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"};
        //对花色和点数进行。 遍历保存花色和点数的数组(嵌套循环)
        for (String num : nums) {
            for (String color : colors) {
                //将花色和点数进行组合,并添加到集合中
                poker.add(color + num);
            }
        }
        //添加大小王
        poker.add("小王");
        poker.add("大王");

        //2. 洗牌
        //使用Collections的shuffle方法完成
        Collections.shuffle(poker);

        //3. 发牌
        //定义三个集合,用来保存三个玩家手里的牌。
        ArrayList<String> playerOne = new ArrayList<>();
        ArrayList<String> playerTwo = new ArrayList<>();
        ArrayList<String> playerThree = new ArrayList<>();
        //定义一个集合,用来保存底牌。
        ArrayList<String> diPai = new ArrayList<>();
        //遍历集合,拿到里面的每一张扑克牌,然后根据索引进行发牌。 因为操作索引,所以要使用普通for
        for(int i = 0; i < poker.size(); i++) {
            //拿到当前遍历到的扑克牌。变量i表示该扑克牌的索引。
            String card = poker.get(i);
            //在给玩家发牌之前进行判断,如果拿到的是最后三张牌,就放入到底牌集合
            if (i >= 51) { //如果牌的索引大于等于51,那么表示是最后三张牌
                //添加到底牌集合
                diPai.add(card);
                continue;//结束本次循环,直接开始下次循环。
            }

            //根据索引发牌
            if(i % 3 == 0) {//把索引为0,3,6...的牌发给第一个玩家(索引对3取余结果是0)
                playerOne.add(card);
            } else if(i % 3 == 1) { //把索引为1,4,7...的牌发给第二个玩家(索引对3取余结果是1)
                playerTwo.add(card);
            } else {//否则索引对3取余结果肯定是2,那么就发给第三个玩家
                playerThree.add(card);
            }
        }

        //4. 看牌
        lookCard("发哥", playerOne);
        lookCard("星爷", playerTwo);
        lookCard("华仔", playerThree);
        lookCard("底牌", diPai);
    }

    /*
        定义方法,用来看牌
        参数: 玩家姓名,玩家手中的牌
     */
    public static void lookCard(String name, ArrayList<String> list) {
        //打印玩家姓名
        System.out.print(name + ": ");
        //遍历保存玩家扑克牌的集合
        for(String card : list) {
            System.out.print(card + " ");
        }
        //输出空换行
        System.out.println();
    }
}
继续阅读
weinxin
资源分享QQ群
本站是一个IT技术分享社区, 会经常分享资源和教程; 分享的时代, 请别再沉默!
沙海
0004-基于Java的美食后台管理系统的设计与实现
Linux服务器网站环境安装
0007-基于微信平台的消息推送系统的研发源码
0009-基于Java的网络招聘系统的设计与实现源码