Collection、List、泛型、数据结构

Collection集合

 

  • 概述: 集合是java中提供的一种容器,可以用来存储多个引用数据类型的数据

  • 分类:

    • 单列集合: 以单个单个元素进行存储

    • 双列集合: 以键值对的形式进行存储

  • 集合与数组的区别:

    • 长度:

      • 数组长度是固定的

      • 集合长度是不固定的

  • 存储范围:

    • 数组可以存储基本类型+引用类型 eg; int[],String[]

    • 集合只能存储引用类型,如果要存储基本类型,需要存储基本类型对应的包装类类型 eg; ArrayList<String> ,ArrayList<Integer>

单列集合常用类的继承体系

  • 单列集合: 以单个单个元素进行存储

  • 单列集合继承体系:

  • Collection接口: 是所有单列集合的顶层父接口,所以Collection中定义了所有单列集合共有的方法

    • List接口: 继承Collection接口,特

    • 特点: 元素有索引,元素可重复,元素存取有序

      • ArrayList类: 底层采用的是数组结构,查询快,增删慢

      • LinkedList类: 底层采用的是链表结构,查询慢,增删快

      • ...

    • Set接口: 继承Collection接口,特点: 元素无索引,元素唯一(不可重复)

      • HashSet类: 底层采用的是哈希表结构,由哈希表结构保证元素唯一,元素存取无序

      • LinkedHashSet类:底层采用的是哈希表+链表结构,由哈希表结构保证元素唯一,由链表保证元素存取有序

      • TreeSet类:底层采用的是红黑树结构,可以对元素进行排序

Collection 常用功能

  • Collection是接口,只能通过其子类创建对象

  • 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(): 把集合中的元素,存储到数组中

Iterator迭代器

迭代的概念

  • 概述:迭代即Collection集合元素的通用获取方式。在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续再判断,如果还有就再取出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。

  • 迭代的步骤:

    • 获取迭代器对象

    • 使用迭代器对象判断集合中是否有元素可以取出

    • 如果有元素可以取出,就直接取出来该元素,如果没有元素可以取出,就结束迭代

获取迭代器对象

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

Iterator迭代器对象的常用方法

  • public boolean hasNext():如果仍有元素可以迭代,则返回 true。

  • public E next():返回迭代的下一个元素。

  • void remove()删除当前迭代出来的元素

public static void main(String[] args) {
    Collection<String> col = new ArrayList<>();
    col.add("张三");
    col.add("李四");
    col.add("王五");
    col.add("李里欧");
    // 获取迭代器
    Iterator<String> it = col.iterator();
    while (it.hasNext()) {
        String e = it.next();
        // 循环判断是否有元素可以取出来
        System.out.println("取出的元素"+e);
        // 删除当前迭代出来的元素
        it.remove();
    }
    System.out.println("col:"+col);// col:[]
}

Iterator迭代器对象在遍历集合时,内部采用指针的方式来跟踪集合中的元素。在调用Iterator的next方法之前,迭代器的索引位于第一个元素之前,不指向任何元素,当第一次调用迭代器的next方法后,迭代器的索引会向后移动一位,指向第一个元素并将该元素返回,当再次调用next方法时,迭代器的索引会指向第二个元素并将该元素返回,依此类推,直到hasNext方法返回false,表示到达了集合的末尾,终止对元素的遍历。

  • 概述: 增强for循环(foreach循环),是JDK1.5以后出来的一个高级for循环,专门用来遍历数组和Collection集合

  • 原理: 内部基于Iterator迭代器实现,所以在遍历的过程中,不能对集合中的元素进行增删操作,否则抛出ConcurrentModificationException并发修改异常

 public static void main(String[] args) {
        // 创建Collection集合对象,限制集合元素的类型为String类型
        Collection<String> col = new ArrayList<>();

        // 往集合中添加元素
        col.add("霆锋");
        col.add("宝强");
        col.add("乃亮");
        col.add("羽凡");

        // 增强for循环
        for (String name : col) {
            System.out.println("name:" + name);
            //col.add("粤明");// 报并发修改异常
            //col.remove(name);// 报并发修改异常
        }

        System.out.println("--------------------");

        // 创建String类型的数组,并存储元素
        String[] arr = {"冠希", "志祥", "吴签", "俊杰"};
        // 增强for循环
        for (String name : arr) {
            System.out.println("name:" + name);
        }
    }

  • 概述: JDK5之后,新增了泛型(Generic)语法,可以在类、接口或方法中预支地使用未知的类型

  • 简而言之: 泛型其实就是表示一种未知的数据类型,在使用的时候确定其具体数据类型

  • 表示方式: <泛型变量> 泛型变量任意字母

  • 泛型的好处:

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

    • 避免了类型转换的麻烦

  • 案例:

    • 集合不使用泛型

      • 可能会发生类型转换异常

      • 避免类型转换异常,就需要先做类型判断,再转型--->比较麻烦

 public static void main(String[] args) {
        // 创建Collection集合,不使用泛型
        Collection col = new ArrayList();

        // 往集合中添加元素
        col.add("张三");
        col.add(18);
        col.add("中国北京");
        col.add(3.14);
        System.out.println("col:" + col);

        // 循环遍历
        for (Object obj : col) {
            // 获取字符串元素的长度,打印输出
            //String str = (String)obj;
            //System.out.println("字符串元素的长度:"+str.length());

            // 避免类型转换异常
            if (obj instanceof String){
                String str = (String)obj;
                System.out.println("字符串元素的长度:"+str.length());
            }
        }
    }

  • 创建含有泛型的类的对象的时候,指定泛型的具体数据类型(只能是引用数据类型)

public static void main(String[] args) {
        // 创建含有泛型的类的对象的时候,指定泛型的具体数据类型(只能是引用数据类型)
        // 创建MyGenericClass对象,指定泛型的具体数据类型为String
        MyGenericClass<String> mc1 = new MyGenericClass<>();

        // 访问MyGenericClass类的成员变量
        mc1.e = "itheima";
        System.out.println("e:" + mc1.e);

        // 访问MyGenericClass类的成员方法
        String res1 = mc1.method("itcast");
        System.out.println("res1:"+res1);

        System.out.println("---------------");

        // 创建MyGenericClass对象,指定泛型的具体数据类型为Integer
        MyGenericClass<Integer> mc2 = new MyGenericClass<>();

        // 访问MyGenericClass类的成员变量
        mc2.e = 100;
        System.out.println("e:" + mc2.e);

        // 访问MyGenericClass类的成员方法
        Integer res2 = mc2.method(200);
        System.out.println("res2:"+res2);


    }

实现类实现接口的时候,确定接口泛型的具体数据类型

public class 类名 implements 接口名<具体的引用数据类型>{}

常见数据结构

  • stack,又称堆栈,它是运算受限的线性表,其限制是仅允许在表的一端进行插入和删除操作,不允许在其他任何位置进行添加、查找、删除等操作。

简单的说:采用该结构的集合,对元素的存取有如下的特点

  • 先进后出(即,存进去的元素,要在后它后面的元素依次取出后,才能取出该元素)。例如,子弹压进弹夹,先压进去的子弹在下面,后压进去的子弹在上面,当开枪时,先弹出上面的子弹,然后才能弹出下面的子弹。

栈的入口、出口的都是栈的顶端位置

队列queue,简称队,它同堆栈一样,也是一种运算受限的线性表,其限制是仅允许在表的一端进行插入,而在表的另一端进行取出并删除。

简单的说,采用该结构的集合,对元素的存取有如下的特点:

  • 先进先出(即,存进去的元素,要在后它前面的元素依次取出后,才能取出该元素)。例如,小火车过山洞,车头先进去,车尾后进去;车头先出来,车尾后出来。

  • 队列的入口和出口在两侧。例如,下图中的左侧为入口,右侧为出口。

数组:Array,是有序的元素序列,数组是在内存中开辟一段连续的空间,并在此空间存放元素。就像是一排出租屋,有100个房间,从001到100每个房间都有固定编号,通过编号就可以快速找到租房子的人

查找元素快:通过索引,可以快速访问指定位置的元素

增删元素慢

链表:linked list,由一系列结点node(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。我们常说的链表结构有单向链表与双向链表,那么这里给大家介绍的是单向链表\双向链表

  • 查找元素慢:想查找某个元素,需要通过连接的节点,依次向后查找指定元素。

  • 增删元素快:只需要修改链接下一个元素的地址值即可

树基本结构介绍

树具有的特点

  1. 每一个节点有零个或者多个子节点

  2. 没有父节点的节点称之为根节点,一个树最多有一个根节点。

  3. 每一个非根节点有且只有一个父节点

二叉查找树

二叉查找树的特点:

  1. 左子树上所有的节点的值均小于等于他的根节点的值

  2. 右子树上所有的节点值均大于或者等于他的根节点的值

  3. 每一个子节点最多有两个子树

平衡二叉树

它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树

左旋就是将节点的右支往左拉,右子节点变成父节点,并把晋升之后多余的左子节点出让给降级节点的右子节点;

右旋: 将节点的左支往右拉,左子节点变成了父节点,并把晋升之后多余的右子节点出让给降级节点的左子节点

红黑树

红黑树是一种自平衡的二叉查找树,是计算机科学中用到的一种数据结构,它是在1972年由Rudolf Bayer发明的,当时被称之为平衡二叉B树,后来,在1978年被

Leoj.Guibas和Robert Sedgewick修改为如今的"红黑树"。它是一种特殊的二叉查找树,红黑树的每一个节点上都有存储位表示节点的颜色,可以是红或者黑;

红黑树不是高度平衡的,它的平衡是通过"红黑树的特性"进行实现的

红黑树的特性:

  1. 每一个节点或是红色的,或者是黑色的。

  2. 根节点必须是黑色

  3. 每个叶节点(Nil)是黑色的;(如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点)

  4. 如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)

  5. 对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点;

  • List接口的概述

    • java.util.List接口继承自Collection接口,是单列集合的一个重要分支

  • List接口的特点

    • 它是一个元素存取有序的集合

    • 它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素

    • 集合中可以有重复的元素

List作为Collection集合的子接口,不但继承了Collection接口中的全部方法,而且还增加了一些根据元素索引来操作集合的特有方法,如下:

  • public void add(int index, E element): 将指定的元素,添加到该集合中的指定位置上。

  • public E get(int index):返回集合中指定位置的元素。

  • public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。

  • public E set(int index, E element):用指定元素替换集合中指定位置的元素,返回值的更新前的元素。

List的子类

  • ArrayList集合: 底层采用的是数组结构,查询快,增删慢

    • 方法: 来自Collection,List

  • LinkedList集合; 底层采用的是链表结构,查询慢,增删快

    • 方法: 来自Collection,List,LinkedList特有的方法

    • 特有的方法:

需求:

  • 按照斗地主的规则,完成造牌洗牌发牌的动作。 具体规则:

    使用54张牌打乱顺序,三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张留作底牌。

分析:

//1.造牌
//1.1 创建扑克盒单列集合,用来存储54张扑克牌
//1.2 创建花色单列集合,用来存储4个花色
//1.3 创建牌面值单列集合,用来存储13个牌面值
//1.4 往扑克盒单列集合中添加大小王
//1.5 花色和牌面值单列集合循环嵌套,拼接52张牌,添加到扑克盒集合中

//2.洗牌
//使用Collections的静态方法shuffle(List<?> list)

//3.发牌
//3.1 创建玩家1,玩家2,玩家3,底牌的集合,用来存储各自的牌
//3.2 循环遍历扑克盒中的牌
//3.3 在循环中,根据索引获取遍历出来的牌
//3.4 在循环中,判断遍历出来的牌的索引:
//3.5 如果索引>=51,牌就给底牌
//3.5 如果索引%3==0,牌就给玩家1
//3.5 如果索引%3==1,牌就给玩家2
//3.5 如果索引%3==2,牌就给玩家3
//3.6 展示牌

实现:

  public static void main(String[] args) {
        //1.造牌
        //1.1 创建扑克盒单列集合,用来存储54张扑克牌
        ArrayList<String> pokerBox = new ArrayList<>();

        //1.2 创建花色单列集合,用来存储4个花色
        ArrayList<String> colors = new ArrayList<>();
        colors.add("♠");
        colors.add("♥");
        colors.add("♣");
        colors.add("♦");

        //1.3 创建牌面值单列集合,用来存储13个牌面值
        ArrayList<String> numbers = new ArrayList<>();
        for (int i = 2; i <= 10; i++) {
            numbers.add(i + "");
        }
        numbers.add("J");
        numbers.add("Q");
        numbers.add("K");
        numbers.add("A");

        //1.4 往扑克盒单列集合中添加大小王
        pokerBox.add("大王");
        pokerBox.add("小王");

        //1.5 花色和牌面值单列集合循环嵌套,拼接52张牌,添加到扑克盒集合中
        for (String number : numbers) {
            for (String color : colors) {
                String pai = color + number;
                // 添加到集合中
                pokerBox.add(pai);
            }
        }
        System.out.println("牌:" + pokerBox);
        System.out.println("牌:" + pokerBox.size());

        //2.洗牌
        //使用Collections的静态方法shuffle(List<?> list)
        Collections.shuffle(pokerBox);
        System.out.println("打乱顺序后的牌:" + pokerBox);
        System.out.println("打乱顺序后的牌:" + pokerBox.size());

        //3.发牌
        //3.1 创建玩家1,玩家2,玩家3,底牌的集合,用来存储各自的牌
        ArrayList<String> player1 = new ArrayList<>();
        ArrayList<String> player2 = new ArrayList<>();
        ArrayList<String> player3 = new ArrayList<>();
        ArrayList<String> diPai = new ArrayList<>();

        //3.2 循环遍历扑克盒中的牌
        for (int i = 0; i < pokerBox.size(); i++) {
            //3.3 在循环中,根据索引获取遍历出来的牌
            String pai = pokerBox.get(i);
            //3.4 在循环中,判断遍历出来的牌的索引:
            if (i >= 51) {
                //3.5 如果索引>=51,牌就给底牌
                diPai.add(pai);
            } else if (i % 3 == 0) {
                //3.5 如果索引%3==0,牌就给玩家1
                player1.add(pai);
            } else if (i % 3 == 1) {
                //3.5 如果索引%3==1,牌就给玩家2
                player2.add(pai);
            } else {
                //3.5 如果索引%3==2,牌就给玩家3
                player3.add(pai);
            }
        }
        //3.6 展示牌
        System.out.println("玩家1的牌:"+player1+",牌数:"+player1.size());
        System.out.println("玩家2的牌:"+player2+",牌数:"+player2.size());
        System.out.println("玩家3的牌:"+player3+",牌数:"+player3.size());
        System.out.println("底牌:"+diPai);

    }

上一篇:Java 自学


下一篇:C++标准库与Java基础类对照