【Collection、List、泛型、数据结构】

day05【Collection、List、泛型、数据结构】

今日内容

  • Collection集合-------->重点掌握
    • 继承体系
    • 常用方法
  • 迭代器
    • 实现原理
    • 基本使用
    • 增强for循环-------->重点掌握
  • 泛型
    • 定义含有泛型的类,接口,方法
    • 使用含有泛型的类,接口,方法-------->重点掌握
    • 泛型通配符
  • 数据结构----->了解
    • 常见数据结构存储数据的特点
  • List集合-------->重点掌握
    • 特点
    • 常用方法
    • 子类使用

第一章 Collection集合

1.1 集合概述

  • 概述: 集合其实就是一种容器,可以用来存储多个引用类型的数据
  • 分类: 单列集合,双列集合
    • 单列集合: 以单个单个元素进行存储
    • 双列集合: 以键值对的方式进行存储
  • 集合与数组的区别:
    • 长度:
      • 数组长度是固定的
      • 集合长度是不固定的
    • 存储范围:
      • 数组可以存储基本类型+引用类型 eg; int[],String[]
      • 集合只能存储引用类型,如果要存储基本类型,需要存储基本类型对应的包装类类型 eg; ArrayList ,ArrayList

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

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

  • 单列集合继承体系:

    • Collection接口是所有单列集合的根接口,也就意味着所有的单列集合都实现了Collection接口

      • List接口继承Collection接口: List集合元素有索引,元素存取有序,元素可重复
        • ArrayList类: 数组存储结构, 查询快,增删慢
        • LinkedList类: 链表存储结构,查询慢,增删快
      • Set接口继承Collection接口: Set集合元素没有索引 , 元素不可重复(唯一)
        • HashSet类: 哈希表结构,由哈希表保证元素唯一,元素存取无序,不可以排序
          • LinkedHashSet类:: 链表+哈希表结构,由哈希表保证元素唯一,由链表保证元素存取有序,不可以排序
        • TreeSet类: 二叉树结构,可以对元素进行排序

        • 在这里插入图片描述

1.3 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(): 把集合中的元素,存储到数组中
    public class Test {
       public static void main(String[] args) {
           // 创建Collection集合对象,限制集合元素类型为String类型
           Collection<String> col = new ArrayList<>();
    
           // - public boolean add(E e):  把给定的对象添加到当前集合中 。
           col.add("王宝强");
           col.add("贾乃亮");
           col.add("谢霆锋");
           col.add("陈羽凡");
           System.out.println("col:" + col);// col:[王宝强, 贾乃亮, 谢霆锋, 陈羽凡]
    
           // - public void clear() :清空集合中所有的元素。
           // col.clear();
           // System.out.println("col:" + col);// col:[]
    
           // - public boolean remove(E e): 把给定的对象在当前集合中删除。
           boolean res1 = col.remove("谢霆锋");
           System.out.println("col:" + col);// col:[王宝强, 贾乃亮, 陈羽凡]
           System.out.println("res1:" + res1);// res1: true
    
           // - public boolean contains(Object obj): 判断当前集合中是否包含给定的对象。
           boolean res2 = col.contains("贾乃亮");
           boolean res3 = col.contains("谢霆锋");
           System.out.println("res2:" + res2);// res2: true
           System.out.println("res3:" + res3);// res3: false
    
           // - public boolean isEmpty(): 判断当前集合是否为空。如果集合中没有元素返回true,否则返回false
           boolean res4 = col.isEmpty();
           System.out.println("res4:" + res4);// res4: false
           // 清空集合中的所有元素
           // col.clear();
           // boolean res5 = col.isEmpty();
           // System.out.println("res5:" + res5);// res5: true
    
           // - public int size(): 返回集合中元素的个数。
           System.out.println("集合元素的个数:" + col.size());// 3
    
           // - public Object[] toArray(): 把集合中的元素,存储到数组中
           Object[] arr = col.toArray();
           System.out.println("arr:"+ Arrays.toString(arr));// arr:[王宝强, 贾乃亮, 陈羽凡]
    
       }
    }
    
    

第二章 Iterator迭代器

2.1 Iterator接口

迭代的概念
  • 概述:迭代即Collection集合元素的通用获取方式。**在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续再判断,如果还有就再取出来。一直把集合中的所有元素全部取出。**这种取出方式专业术语称为迭代。
  • 迭代的步骤:
    • 获取迭代器对象
    • 使用迭代器对象判断集合中是否有元素可以取出
    • 如果有元素可以取出,就直接取出来该元素,如果没有元素可以取出,就结束迭代
获取迭代器对象

Collection集合提供了一个获取迭代器的方法:

  • public Iterator iterator(): 获取集合对应的迭代器,用来遍历集合中的元素的。
Iterator迭代器对象的常用方法
  • public boolean hasNext():如果仍有元素可以迭代,则返回 true。

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

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

  • 案例:

    public class Test {
        public static void main(String[] args) {
            /*
                获取迭代器对象
                    Collection集合提供了一个获取迭代器的方法:
                    - public Iterator iterator(): 获取集合对应的迭代器,用来遍历集合中的元素的。
    
                Iterator迭代器对象的常用方法
                    - public boolean hasNext():如果仍有元素可以迭代,则返回 true。
                    - public E next():返回迭代的下一个元素。
                    - void remove()删除当前迭代出来的元素
             */
            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);
    
                // 需求:如果迭代出来的元素是谢霆锋,就删除该元素
                if ("谢霆锋".equals(e)){
                    it.remove();
                }
            }
    
            System.out.println("col:"+col);// col:[王宝强, 贾乃亮, 陈羽凡]
        }
    }
    

2.2 迭代器的实现原理

  • 迭代器的实现原理

​ 我们在之前案例已经完成了Iterator遍历集合的整个过程。当遍历集合时,首先通过调用集合的iterator()方法获得迭代器对象,然后使用hashNext()方法判断集合中是否存在下一个元素,如果存在,则调用next()方法将元素取出,否则说明已到达了集合末尾,停止遍历元素。

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

2.3 迭代器的常见问题

常见问题一
  • 在进行集合元素获取时,如果集合中已经没有元素可以迭代了,还继续使用迭代器的next方法,将会抛出java.util.NoSuchElementException没有集合元素异常。

    public class Test2_迭代常见问题1 {
        public static void main(String[] args) {
            /*
                问题一:在进行集合元素获取时,如果集合中已经没有元素可以迭代了,
                      还继续使用迭代器的next方法,将会抛出java.util.NoSuchElementException没有集合元素异常。
             */
            Collection<String> col = new ArrayList<>();
    
            // 往集合中添加元素
            col.add("王宝强");
            col.add("贾乃亮");
            col.add("谢霆锋");
            col.add("陈羽凡");
    
            // 获取迭代器对象
            Iterator<String> it = col.iterator();
    
            // 迭代--->快捷键:itit
            while (it.hasNext()) {
                String e = it.next();
                System.out.println("元素:" + e);
            }
    
            System.out.println("--------");
            // 调用next方法
            // String next = it.next();// 报NoSuchElementException没有找到元素异常
            // System.out.println("next:" + next);
    
            // 需求:再迭代col集合中所有的元素
            // 获取迭代器对象
            Iterator<String> it1 = col.iterator();
    
            // 迭代--->快捷键:itit
            while (it1.hasNext()) {
                String e = it1.next();
                System.out.println("元素:" + e);
            }
    
        }
    }
    
    
  • 解决方式: 使用集合从新获取一个新的迭代器对象来使用

常见问题二
  • 在进行集合元素迭代时,如果添加或移除集合中的元素 , 将无法继续迭代 , 将会抛出ConcurrentModificationException并发修改异常.

    public class Test3_迭代常见问题二 {
    
        public static void main(String[] args) {
            /*
                问题: 在进行集合元素迭代时,如果添加或移除集合中的元素,将无法继续迭代,将会抛出ConcurrentModificationException并发修改异常.
             */
            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();
                // 问题一: 迭代一个元素,就使用集合对象往集合中添加一个元素
                // col.add("itheima");// 报并发修改异常
    
                // 问题二: 迭代一个元素,就使用集合对象删除一个元素
                // col.remove(e);// 报并发修改异常
    
                // 迭代器可以一边迭代一边删除
                it.remove();
            }
            System.out.println("col:" + col);// col:[]
    
        }
    }
    
    
  • 解决办法:

    • 使用CopyOnWriteArrayList集合,就可以迭代的时候,往集合中添加或删除元素
    • 使用迭代器的remove方法实现一边迭代,一边删除

2.4 增强for

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

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

  • 格式:

    for(数据类型 变量名 :  数组名\集合名){
        
    }
    
  • 案例:

    public class Test4_增强for循环 {
        public static void main(String[] args) {
            /*
                for(数据类型 变量名 :  数组名\集合名){
    
                }
             */
            // 需求1: 增强for循环遍历集合元素
            Collection<String> col = new ArrayList<>();
    
            // 往集合中添加元素
            col.add("王宝强");
            col.add("贾乃亮");
            col.add("谢霆锋");
            col.add("陈羽凡");
    
            // 快捷键: 集合名.for
            for (String e : col) {
                System.out.println("e:" + e);
            }
    
            System.out.println("--------");
    
            // 需求1: 增强for循环遍历数组元素
            String[] arr = {"王宝强",
                    "贾乃亮",
                    "谢霆锋",
                    "陈羽凡"};
    
            // 快捷键: 数组名.for
            for (String e : arr) {
                System.out.println("e:" + e);
            }
        }
    }
    

第三章 泛型

3.1 泛型的概述

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

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

  • 表示方式: <泛型变量>

  • 泛型的好处:

    • 将运行时期的ClassCastException,转移到了编译时期变成了编译失败
    • 避免了类型转换的麻烦
  • 案例:

    • 集合不使用泛型

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

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

        public class Test1集合不使用泛型 {
            public static void main(String[] args) {
                // 注意: 不指定集合元素的类型,元素的类型默认是Object类型
        
                // 创建ArrayList集合对象,不指定泛型的具体数据类型
                ArrayList list = new ArrayList();
        
                // 往集合中添加元素
                list.add("itheima");
                list.add(666);
                list.add(3.14);
                list.add(true);
                list.add("itcast");
                System.out.println("list:"+list);// list:[itheima, 666, 3.14, true, itcast]
        
                // 获取集合中所有字符串元素的长度
                for (Object obj : list) {
                    // String str = (String) obj;// 发生类型转换异常
                    // System.out.println(str.length());
                    
                    // 转换类型之前先判断
                    if (obj instanceof String){
                        String str = (String) obj;
                        System.out.println(str.length());
                    }
                }
            }
        }
        
    • 集合使用泛型

      • 概述:指定泛型的具体数据类型----->(只能是引用数据类型)

        public class Test2集合使用泛型 {
            public static void main(String[] args) {
                // 注意: 使用集合的时候,建议通过泛型来限制集合元素类型一致
                
                // 创建ArrayList集合对象,指定泛型的具体数据类型为String
                ArrayList<String> list = new ArrayList<>();
        
                // 往集合中添加元素
                list.add("itheima");
                // list.add(666);// 编译报错,把运行时的类型转换异常,转移到了编译时期变成了编译失败
                // list.add(3.14);// 编译报错,把运行时的类型转换异常,转移到了编译时期变成了编译失败
                // list.add(true);// 编译报错,把运行时的类型转换异常,转移到了编译时期变成了编译失败
                 list.add("itcast");
                System.out.println("list:"+list);// list:[itheima, itcast]
        
                // 获取集合中所有字符串元素的长度
                for (String s : list) {
                    System.out.println(s.length());
                }
            }
        }
        

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

  • 定义含有泛型的类

    • 格式:

      public class 类名<泛型变量>{
          
      }
      泛型变量: 可以是任意字母,eg: A,B,E,...a,b,....;一般写E
      
    • 案例

      // 含有泛型的类
      public class MyGenericClass1<E> {
       
          E e;
          
          public void method1(E e) {
              System.out.println("e的类型:" + e.getClass());
          }
          
          public E method2(E e){
              return e;
          }
      }
      
  • 使用含有泛型的类

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

    • 案例

      public class Test {
          public static void main(String[] args) {
              // 使用含有泛型的类
              // 创建含有泛型的类的对象
              MyGenericClass1<String> mc1 = new MyGenericClass1<>();
              mc1.method1("itheima");
      
              // 创建含有泛型的类的对象
              MyGenericClass1<Integer> mc2 = new MyGenericClass1<>();
              mc2.method1(100);
      
      
          }
      }
      

3.3 定义和使用含有泛型的方法

  • 定义含有泛型的方法

    • 格式:

      修饰符 <泛型变量> 返回值类型 方法名(形参列名){
          方法体
      }
      // 泛型变量:可以写任意字母,一般写T
      
    • 案例:

      public class MyGenericMethod {
      
          // T:表示未知的数据类型,调用方法的时候确定具体的数据类型
          public <T> T method1(T t){
              return t;
          }
          
           public <T> T method2(T t){
              return t;
          }
      }
      
      
  • 使用含有泛型的方法

    • 调用含有泛型方法的时候,确定泛型的具体数据类型

    • 案例:

      public class Test {
          public static void main(String[] args) {
              // 使用含有泛型的方法:调用含有泛型的方法,指定泛型的具体数据类型
              MyGenericMethod mg = new MyGenericMethod();
              // 调用method1,指定泛型的具体类型为Integer
              Integer i = mg.method1(100);
              System.out.println("i:"+i);// i:100
      
              // 调用method1,指定泛型的具体类型为String
              String str = mg.method1("itheima");
              System.out.println("str:"+str);// str:itheima
      
          }
      }
      
      

3.4 定义和使用含有泛型的接口

  • 定义含有泛型的接口

    • 格式:

      public interface 接口名<泛型变量>{
          
      }
      // 泛型变量:可以是任意字母,一般写E
      
    • 案例:

      // 含有泛型的接口
      public interface MyGenericInterface<E> {
          
          void method1(E e);
          
          public default E method2(E e){
              return e;
          }
          
          
      }
      
  • 使用含有泛型的接口

    • 方式一: 实现类实现接口的时候,确定接口泛型的具体数据类型

      • 格式:

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

        // 实现接口的时候,确定接口泛型的具体数据类型
        class Imp implements MyGenericInterface<String>{
        
            @Override
            public void method1(String s) {
                System.out.println("method1.."+s);
            }
        
            @Override
            public String method2(String s) {
                System.out.println("method2..."+s);
                return s;
            }
        }
        
    • 方式二:实现类实现接口的时候,不确定接口泛型的具体数据类型,而是创建实现类对象的时候确定泛型的具体数据类型

      • 格式:

        public class 类名<泛型变量> implements 接口名<泛型变量>{}
        
      • 案例:

        // 实现类实现接口的时候,不确定接口泛型的具体数据类型,而是创建实现类对象的时候确定泛型的具体数据类型
        class Imp2<E> implements MyGenericInterface<E>{
        
            @Override
            public void method1(E e) {
                System.out.println("e的类型:"+e.getClass());
            }
        
            @Override
            public E method2(E e) {
                System.out.println("e的类型:"+e.getClass());
                return e;
            }
        }
        
        public class Test {
            public static void main(String[] args) {
                // 接口: 被实现
                // 创建实现类对象的时候确定泛型的具体数据类型
                Imp2<String> i1 = new Imp2<>();
                i1.method1("itheima");
                String res1 = i1.method2("itcast");
        
                System.out.println("-----");
                
                Imp2<Integer> i2 = new Imp2<>();
                i2.method1(100);
                Integer res2 = i2.method2(200);
        
            }
        }
        
        

3.5 泛型通配符

  • 概述: 泛型通配符用问号表示(?)

  • 为什么需要泛型通配符:

    • 泛型本身不存在继承关系,不可以给已指定泛型的变量接收有其他泛型类型的对象
      • Collection<Object> list = new ArrayList<String>(); //错误格式,泛型不存在继承关系
    • 如果想要使变量在未来接收有泛型定义的对象,又不确定泛型要定义的类型可以使用泛型通配符
      • Collection<?> list 变量接收
  • 通配符基本使用

    • 格式: 数据类型<?> 变量

    • 注意:

      • 如果使用了泛型通配符,那么该集合变量元素类型默认是Object类型
      • 如果使用了泛型通配符,那么该集合变量只能取元素,无法增删元素
    • 案例:

      public class Test {
          public static void main(String[] args) {
              // Integer继承Number,Number继承Object,String继承Object
              ArrayList<Object> list1 = new ArrayList<>();
              ArrayList<String> list2 = new ArrayList<>();
              ArrayList<Integer> list3 = new ArrayList<>();
              ArrayList<Number> list4 = new ArrayList<>();
      
              // 泛型本身不存在继承关系,不可以给已指定泛型的变量接收有其他泛型类型的对象
              // method1(list1);// 编译报错
              // method1(list2); // 正确
              // method1(list3);// 编译报错
              // method1(list4);// 编译报错
      
              method2(list1);
              method2(list2);
              method2(list3);
              method2(list4);
          }
      
          // 定义一个方法,可以接收任意ArrayList集合对象
          // 如果想要使变量在未来接收有泛型定义的对象,又不确定泛型要定义的类型可以使用泛型通配符
          public static void method2(ArrayList<?> list) {// 已指定泛型的变量
              System.out.println("执行了");
              // 注意: 如果使用了泛型通配符,那么该集合元素类型默认是Object类型
              for (Object obj : list) {
      
              }
              // 注意: 如果使用了泛型通配符,那么该集合变量只能取元素,无法增删元素
               // list.add(1000);// 编译报错
          }
      
          public static void method1(ArrayList<String> list) {// 已指定泛型的变量
      
          }
      }
      
      
  • 通配符高级使用----受限泛型

    • 上限:

      • 格式: <? extends 类名>
      • 表示: 只接受该类类型或者其子类类型
    • 下限:

      • 格式: <? super 类名>
      • 表示: 只接受该类类型或者其父类类型
      • 案例:
    • 案例:

      public class Test {
          public static void main(String[] args) {
              // Integer继承Number,Number继承Object,String继承Object
              ArrayList<Object> list1 = new ArrayList<>();
              ArrayList<String> list2 = new ArrayList<>();
              ArrayList<Integer> list3 = new ArrayList<>();
              ArrayList<Number> list4 = new ArrayList<>();
      
              method1(list1);
              //method1(list2);// 编译报错
              method1(list3);
              method1(list4);
              
              //method2(list1);// 编译报错
              //method2(list2);// 编译报错
              method2(list3);
              method2(list4);
      
          }
      
          // 定义一个方法,只可以接收泛型是Integer或者其父类类型的ArrayList集合对象
          public static void method1(ArrayList<? super Integer> list){
              list.add(100);
          }
      
          // 定义一个方法,只可以接收泛型是Number或者其子类类型的ArrayList集合对象
          public static void method2(ArrayList<? extends Number> list){
        
          }
      
      }
      

第四章 数据结构

4.1 数据结构介绍

  • 数据结构 :其实就是存储数据和表示数据的方式
  • 常见的数据结构:栈、队列、数组、链表和红黑树

4.2 常见数据结构

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

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

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

  • 栈的入口、出口的都是栈的顶端位置
    在这里插入图片描述

这里两个名词需要注意:

  • 压栈:就是存元素。即,把元素存储到栈的顶端位置,栈中已有元素依次向栈底方向移动一个位置。
  • 弹栈:就是取元素。即,把栈的顶端位置元素取出,栈中已有元素依次向栈顶方向移动一个位置。
队列
  • 队列queue,简称队,它同堆栈一样,也是一种运算受限的线性表,其限制是仅允许在表的一端进行插入,而在表的另一端进行取出并删除。

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

    • 先进先出(即,存进去的元素,要在后它前面的元素依次取出后,才能取出该元素)。例如,小火车过山洞,车头先进去,车尾后进去;车头先出来,车尾后出来。
    • 队列的入口和出口在两侧。例如,下图中的左侧为入口,右侧为出口。
      在这里插入图片描述
数组
  • 数组:Array,是有序的元素序列,数组是在内存中开辟一段连续的空间,并在此空间存放元素。就像是一排出租屋,有100个房间,从001到100每个房间都有固定编号,通过编号就可以快速找到租房子的人。

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

  • 查找元素快:通过索引,可以快速访问指定位置的元素
    在这里插入图片描述

  • 增删元素慢

  • 指定索引位置增加元素:需要创建一个新数组,将指定新元素存储在指定索引位置,再把原数组元素根据索引,复制到新数组对应索引的位置。

  • 在这里插入图片描述

  • **指定索引位置删除元素:**需要创建一个新数组,把原数组元素根据索引,复制到新数组对应索引的位置,原数组中指定索引位置元素不复制到新数组中。
    在这里插入图片描述

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

在这里插入图片描述

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

  • 多个结点之间,通过地址进行连接。例如,多个人手拉手,每个人使用自己的右手拉住下个人的左手,依次类推,这样多个人就连在一起了。

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

  • 增删元素快:只需要修改链接下一个元素的地址值即可
    在这里插入图片描述

4.3 树基本结构介绍

树具有的特点
  1. 每一个节点有零个或者多个子节点
  2. 没有父节点的节点称之为根节点,一个树最多有一个根节点。
  3. 每一个非根节点有且只有一个父节点
    在这里插入图片描述
名词含义
节点指树中的一个元素
节点的度节点拥有的子树的个数,二叉树的度不大于2
叶子节点度为0的节点,也称之为终端结点
高度叶子结点的高度为1,叶子结点的父节点高度为2,以此类推,根节点的高度最高
根节点在第一层,以此类推
父节点若一个节点含有子节点,则这个节点称之为其子节点的父节点
子节点子节点是父节点的下一层节点
兄弟节点拥有共同父节点的节点互称为兄弟节点
二叉树

如果树中的每个节点的子节点的个数不超过2,那么该树就是一个二叉树。
在这里插入图片描述

二叉查找树

二叉查找树的特点:

  1. 左子树上所有的节点的值均小于等于他的根节点的值
  2. 右子树上所有的节点值均大于或者等于他的根节点的值
  3. 每一个子节点最多有两个子树

案例演示(20,18,23,22,17,24,19)数据的存储过程;

1565611710800.png

遍历获取元素的时候可以按照"左中右"的顺序进行遍历: 17 , 18, 19,20,22,23,24

注意:二叉查找树存在的问题:会出现"瘸子"的现象,影响查询效率
在这里插入图片描述

平衡二叉树
概述

为了避免出现"瘸子"的现象,减少树的高度,提高我们的搜素效率,又存在一种树的结构:“平衡二叉树”

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

如下图所示:
在这里插入图片描述

如下图所示,左图是一棵平衡二叉树,根节点10,左右两子树的高度差是1,而右图,虽然根节点左右两子树高度差是0,但是右子树15的左右子树高度差为2,不符合定义,

所以右图不是一棵平衡二叉树。

旋转

在构建一棵平衡二叉树的过程中,当有新的节点要插入时,检查是否因插入后而破坏了树的平衡,如果是,则需要做旋转去改变树的结构。

左旋:

左旋就是将节点的右支往左拉,右子节点变成父节点,并把晋升之后多余的左子节点出让给降级节点的右子节点;
在这里插入图片描述

右旋:

将节点的左支往右拉,左子节点变成了父节点,并把晋升之后多余的右子节点出让给降级节点的左子节点
在这里插入图片描述

举个例子,像上图是否平衡二叉树的图里面,左图在没插入前"19"节点前,该树还是平衡二叉树,但是在插入"19"后,导致了"15"的左右子树失去了"平衡",

所以此时可以将"15"节点进行左旋,让"15"自身把节点出让给"17"作为"17"的左树,使得"17"节点左右子树平衡,而"15"节点没有子树,左右也平衡了。如下图,
在这里插入图片描述

由于在构建平衡二叉树的时候,当有新节点插入时,都会判断插入后时候平衡,这说明了插入新节点前,都是平衡的,也即高度差绝对值不会超过1。当新节点插入后,

有可能会有导致树不平衡,这时候就需要进行调整,而可能出现的情况就有4种,分别称作左左,左右,右左,右右

左左:只需要做一次右旋就变成了平衡二叉树。
右右:只需要做一次左旋就变成了平衡二叉树。
左右:先做一次分支的左旋,再做一次树的右旋,才能变成平衡二叉树。
右左:先做一次分支的右旋,再做一次数的左旋,才能变成平衡二叉树。
课上只讲解“左左”的情况
左左

左左:只需要做一次右旋就变成了平衡二叉树。

左左即为在原来平衡的二叉树上,在节点的左子树的左子树下,有新节点插入,导致节点的左右子树的高度差为2,如下即为"10"节点的左子树"7",的左子树"4",插入了节点"5"或"3"导致失衡。
在这里插入图片描述

左左调整其实比较简单,只需要对节点进行右旋即可,如下图,对节点"10"进行右旋,
在这里插入图片描述

在这里插入图片描述

左右

左右:先做一次分支的左旋,再做一次树的右旋,才能变成平衡二叉树。

左右即为在原来平衡的二叉树上,在节点的左子树的右子树下,有新节点插入,导致节点的左右子树的高度差为2,如上即为"11"节点的左子树"7",的右子树"9",

插入了节点"10"或"8"导致失衡。
在这里插入图片描述

左右的调整就不能像左左一样,进行一次旋转就完成调整。我们不妨先试着让左右像左左一样对"11"节点进行右旋,结果图如下,右图的二叉树依然不平衡,而右图就是接下来要

讲的右左,即左右跟右左互为镜像,左左跟右右也互为镜像。
在这里插入图片描述

左右这种情况,进行一次旋转是不能满足我们的条件的,正确的调整方式是,将左右进行第一次旋转,将左右先调整成左左,然后再对左左进行调整,从而使得二叉树平衡。

即先对上图的节点"7"进行左旋,使得二叉树变成了左左,之后再对"11"节点进行右旋,此时二叉树就调整完成,如下图:
在这里插入图片描述

右左

右左:先做一次分支的右旋,再做一次数的左旋,才能变成平衡二叉树。

右左即为在原来平衡的二叉树上,在节点的右子树的左子树下,有新节点插入,导致节点的左右子树的高度差为2,如上即为"11"节点的右子树"15",的左子树"13",

插入了节点"12"或"14"导致失衡。
在这里插入图片描述

前面也说了,右左跟左右其实互为镜像,所以调整过程就反过来,先对节点"15"进行右旋,使得二叉树变成右右,之后再对"11"节点进行左旋,此时二叉树就调整完成,如下图:
在这里插入图片描述

右右

右右:只需要做一次左旋就变成了平衡二叉树。

右右即为在原来平衡的二叉树上,在节点的右子树的右子树下,有新节点插入,导致节点的左右子树的高度差为2,如下即为"11"节点的右子树"13",的左子树"15",插入了节点

"14"或"19"导致失衡。
在这里插入图片描述

右右只需对节点进行一次左旋即可调整平衡,如下图,对"11"节点进行左旋。
在这里插入图片描述

红黑树

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

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

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

红黑树的特性:

  1. 每一个节点或是红色的,或者是黑色的。
  2. 根节点必须是黑色
  3. 每个叶节点(Nil)是黑色的;(如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点)
  4. 如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)
  5. 对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点;

如下图所示就是一个
在这里插入图片描述

在进行元素插入的时候,和之前一样; 每一次插入完毕以后,使用黑色规则进行校验,如果不满足红黑规则,就需要通过变色,左旋和右旋来调整树,使其满足红黑规则;

4.4 小结

  • 栈结构: 先进后出
  • 队列结构:先进先出
  • 数组: 查询快,增删慢
  • 链表; 查询慢,增删快
  • 二叉查找树: 提供搜索效率

第五章 List接口

5.1 List接口介绍

  • List接口的概述
    • java.util.List接口继承自Collection接口,是单列集合的一个重要分支
  • List接口的特点
    • 它是一个元素存取有序的集合
    • 它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素
    • 集合中可以有重复的元素

5.2 List接口中常用方法

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集合特有的方法都是跟索引相关,我们在基础班都学习过。

List接口新增常用方法的使用
public class Test {
    public static void main(String[] args) {
        // 创建List集合对象,限制集合元素的类型为String
        List<String> list = new ArrayList<>();

        // 往集合中添加元素的方法
        list.add("马蓉");
        list.add("李小璐");
        list.add("张柏芝");
        list.add("白百何");
        System.out.println("list:" + list);// list:[马蓉, 李小璐, 张柏芝, 白百何]

        // - public void add(int index, E element): 将指定的元素,添加到该集合中的指定位置上。
        list.add(1, "潘金莲");
        System.out.println("list:" + list);// list:[马蓉, 潘金莲, 李小璐, 张柏芝, 白百何]

        // - public E get(int index):返回集合中指定位置的元素。
        String e = list.get(1);
        System.out.println("e:" + e);// e:潘金莲

        // - public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。
        String removeE = list.remove(1);
        System.out.println("被删除的元素:"+removeE);// 被删除的元素:潘金莲
        System.out.println("list:" + list);// list:[马蓉, 李小璐, 张柏芝, 白百何]

        // - public E set(int index, E element):用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
        String setE = list.set(2, "董洁");
        System.out.println("被替换的元素:"+setE);// 被替换的元素:张柏芝
        System.out.println("list:" + list);// list:[马蓉, 李小璐, 董洁, 白百何]
    }
}

  • 注意: 如果集合元素为Integer类型,那么删除的时候优先根据索引删除

    public class Test2 {
        public static void main(String[] args) {
            /*
                public boolean remove(Object obj): 删除指定的元素。
                public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。
             */
            // 创建List集合,限制集合元素的类型为Integer
            List<Integer> list = new ArrayList<>();
    
            // 往集合中添加元素
            list.add(1);
            list.add(2);
            list.add(3);
            list.add(4);
    
            // 删除元素
            list.remove(2);// 优先根据索引删除
            System.out.println("list:" + list);// list:[1, 2, 4]
        }
    }
    
    

5.3 List的子类

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

    • 方法: 来自Collection,List
  • LinkedList集合; 底层采用的是链表结构,查询慢,增删快

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

    • 特有的方法:

      public void addFirst(E e):将指定元素插入此列表的开头
      public void addLast(E e):将指定元素添加到此列表的结尾
      public E getFirst():返回此列表的第一个元素
      public E getLast():返回此列表的最后一个元素
      public E removeFirst():移除并返回此列表的第一个元素
      public E removeLast():移除并返回此列表的最后一个元素
      public E pop():从此列表所表示的堆栈处弹出一个元素
      public void push(E e):将元素推入此列表所表示的堆栈
      
      
    • 案例:

      public class Test {
          public static void main(String[] args) {
              // 创建LinkedList集合,限制集合元素类型为String
              LinkedList<String> list = new LinkedList<>();
      
              // 往集合中添加一些元素
              list.add("苍老师");
              list.add("小泽老师");
              list.add("波老师");
              System.out.println("list:"+list);// list:[苍老师, 小泽老师, 波老师]
      
              // public void addFirst(E e):将指定元素插入此列表的开头
              list.addFirst("吉泽老师");// list:[吉泽老师, 苍老师, 小泽老师, 波老师]
              System.out.println("list:"+list);// list:[吉泽老师, 苍老师, 小泽老师, 波老师]
      
              // public void addLast(E e):将指定元素添加到此列表的结尾
              list.addLast("三村老师");
              System.out.println("list:"+list);// list:[吉泽老师, 苍老师, 小泽老师, 波老师, 三村老师]
      
              // public E getFirst():返回此列表的第一个元素
              System.out.println("第一个元素: "+list.getFirst());// 吉泽老师
      
              // public E getLast():返回此列表的最后一个元素
              System.out.println("最后一个元素: "+list.getLast());// 三村老师
      
              // public E removeFirst():移除并返回此列表的第一个元素
              String removeFirstE = list.removeFirst();
              System.out.println("removeFirstE:"+removeFirstE);// 吉泽老师
      
              // public E removeLast():移除并返回此列表的最后一个元素
              String removeLastE = list.removeLast();
              System.out.println("removeLastE:"+removeLastE);// 三村老师
              System.out.println("list:"+list);// list:[苍老师, 小泽老师, 波老师]
      
      
              // public E pop():从此列表所表示的堆栈处弹出一个元素(removeFirst方法)
              String popE = list.pop();
              System.out.println("popE:"+popE);// 苍老师
      
              // public void push(E e):将元素推入此列表所表示的堆栈(addFirst)
              list.push("明日花老师");
              System.out.println("list:"+list);// list:[明日花老师, 小泽老师, 波老师]
      
          }
      }
      
      

5.4 集合综合案例

需求:

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

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

分析:

  • 造牌
    1.创建一个集合,用来存储54张扑克牌
    2.创建一个花色集合,用来存储4个花色
    3.创建一个牌面值集合,用来存储13个牌面值
    4.往存储扑克牌的集合中存储大小王
    5.花色集合和牌面值集合循环嵌套,生成52张扑克牌
    6.在循环中,把生成的扑克牌添加到存储扑克牌的集合中

  • 洗牌: 打乱集合元素顺序

  • 发牌:

    1.创建4个集合,用来存储玩家1,玩家2,玩家3,底牌的手牌,
    2.循环遍历打乱顺序之后的存储54张扑克牌的集合
    3.在循环中,判断索引
    3.0 如果索引大于等于51,就取出的牌给底牌
    3.1 如果索引%30,就取出的牌给玩家1
    3.2 如果索引%3
    1,就取出的牌给玩家2
    3.3 如果索引%3==2,就取出的牌给玩家3
    4.展示牌

  • 展示牌: 打印输出即可

实现:

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

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

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

        // 4.往存储扑克牌的集合中存储大小王
        pokerBox.add("大王");
        pokerBox.add("小王");

        // 5.花色集合和牌面值集合循环嵌套,生成52张扑克牌
        for (String number : numbers) {
            for (String color : colors) {
                // 拼接牌
                String pai = color + number;
                // 6.在循环中,把生成的扑克牌添加到存储扑克牌的集合中
                pokerBox.add(pai);
            }
        }

        System.out.println("牌:" + pokerBox);
        System.out.println("牌:" + pokerBox.size());


        // 洗牌:  打乱集合元素的顺序
        // public static void shuffle(List<?> list) 随机打乱List集合中元素的顺序
        Collections.shuffle(pokerBox);
        System.out.println("牌:" + pokerBox);
        System.out.println("牌:" + pokerBox.size());


        // 发牌:
        // 1.创建4个集合,用来存储玩家1,玩家2,玩家3,底牌的手牌,
        ArrayList<String> play1 = new ArrayList<>();
        ArrayList<String> play2 = new ArrayList<>();
        ArrayList<String> play3 = new ArrayList<>();
        ArrayList<String> diPai = new ArrayList<>();

        // 2.循环遍历打乱顺序之后的存储54张扑克牌的集合
        for (int i = 0; i < pokerBox.size(); i++) {
            // 取出牌
            String pai = pokerBox.get(i);

            // 3.在循环中,判断索引
            // 3.0 如果索引大于等于51,就取出的牌给底牌
            if (i >= 51) {
                diPai.add(pai);
            } else if (i % 3 == 0) {
                // 3.1 如果索引%3==0,就取出的牌给玩家1
                play1.add(pai);
            } else if (i % 3 == 1) {
                // 3.2 如果索引%3==1,就取出的牌给玩家2
                play2.add(pai);
            } else if (i % 3 == 2) {
                // 3.3 如果索引%3==2,就取出的牌给玩家3
                play3.add(pai);
            }
        }
        // 4.展示牌
        System.out.println("玩家1:"+play1+",牌数;"+play1.size());
        System.out.println("玩家2:"+play2+",牌数;"+play2.size());
        System.out.println("玩家3:"+play3+",牌数;"+play3.size());
        System.out.println("底牌:"+diPai+",牌数;"+diPai.size());

    }
}

总结

必须练习:
	1.总结单列集合继承体系和各个单列集合的特点
    2.Collection集合的常用方法
    3.迭代器的基本使用,增强for循环的使用--->使用迭代器,增强for循环遍历集合元素
    4.使用含有泛型的类,含有泛型的接口,含有泛型的方法---->ArrayList<E>,List<E>接口
    5.List集合的常用方法
    6.LinkedList集合的特有方法
    7.斗地主案例---->选做
        
- 能够说出集合与数组的区别
     - 长度:
      - 数组长度是固定的
      - 集合长度是不固定的
     - 存储范围:
      - 数组可以存储基本类型+引用类型 eg; int[],String[]
      - 集合只能存储引用类型,如果要存储基本类型,需要存储基本类型对应的包装类类型

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

- 能够使用迭代器对集合进行取元素
    1.获取迭代器,使用Collection的iterator()方法
    2.使用迭代器判断是否有元素可以迭代 hasNext()
    3.使用迭代器取出可以迭代的元素 next()
          
- 能够使用增强for循环遍历集合和数组
    格式:
		for(数据类型 变量名: 数组名\集合名){
            
        }

- 能够理解泛型上下限
  上限: <? extends 类名>
  下限: <? super 类名>
      
- 能够阐述泛型通配符的作用
    如果想要使变量在未来接收有泛型定义的对象,又不确定泛型要定义的类型可以使用泛型通配符?
      
- 能够说出常见的数据结构
  栈,队列,数组,链表,- 能够说出数组结构特点
- 能够说出栈结构特点
- 能够说出队列结构特点
- 能够说出单向链表结构特点
    - 栈结构: 先进后出
    - 队列结构:先进先出
    - 数组: 查询快,增删慢
    - 链表; 查询慢,增删快
    - 二叉查找树: 提供搜索效率
        
- 能够说出List集合特点
     元素有索引,元素存取有序,元素可以重复
        
- 能够完成斗地主的案例                   
     洗牌,发牌,造牌
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答: Java集合框架中的Map是一种键值对的数据结构,可以存储一组具有唯一键和对应值的元素。使用可以在编译时进行类检查,确保集合中只能存储指定类的元素,提高代码的可读性和安全性。 在引用中的示例代码中,List<String>定义了一个只能存储字符串类元素的列表。在引用中的示例代码中,Collection<String>和Collection<Integer>分别定义了只能存储字符串类和整数类元素的集合。使用通配符可以增加集合的灵活性。比如在coll.removeAll(c)方法中,传入的Collection对象的可以是任意类。另外,还可以应用于Map集合,如引用中的示例代码中,Set<Map.Entry<String,String>>定义了只能存储键值对类为String的元素的集合。 综上所述,Java集合框架中的Map和可以一起使用,通过可以指定集合中存储的元素类,增加代码的可读性和类安全性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [java基础中的--------Map+](https://blog.csdn.net/liutaiwu/article/details/107915445)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值