今日内容

  • Collection集合------------------>必须掌握
    • 单列集合继承体系
    • Collection常用方法
  • 迭代器------------------>必须掌握
    • 迭代器的使用
    • 迭代器的原理
    • 增强for循环
  • 泛型------------------>掌握使用泛型-----相对难点
    • 定义和使用含有泛型的类
    • 定义和使用含有泛型的方法
    • 定义和使用含有泛型的接口
    • 泛型通配符
  • 数据结构--------->了解
    • 常见数据结构的特点
  • List集合------------------>必须掌握
    • List常用方法
    • List实现类的使用

第一章 Collection集合

1.1 集合概述

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

1.2 单列集合常用类的继承体系[重点]

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

  • 单列集合继承体系:

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

      • List接口: 继承Collection接口,特
      • 特点: 元素有索引,元素可重复,元素存取有序
        • ArrayList类: 底层采用的是数组结构,查询快,增删慢
        • LinkedList类: 底层采用的是链表结构,查询慢,增删快
        • ...
      • Set接口: 继承Collection接口,特点: 元素无索引,元素唯一(不可重复)
        • HashSet类: 底层采用的是哈希表结构,由哈希表结构保证元素唯一,元素存取无序
        • LinkedHashSet类:底层采用的是哈希表+链表结构,由哈希表结构保证元素唯一,由链表保证元素存取有序
        • TreeSet类:底层采用的是红黑树结构,可以对元素进行排序
        • ....

      image-20210323091611804

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(): 把集合中的元素,存储到数组中
    /**
    * Created by PengZhiLin on 2021/8/3 9:34
    */
    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("陈羽凡");
           // col:[谢霆锋, 王宝强, 贾乃亮, 陈羽凡]
           System.out.println("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("res1:"+res1);// res1:true
           System.out.println("col:" + col);// col:[谢霆锋, 贾乃亮, 陈羽凡]
    
           // 删除潘粤明
           boolean res2 = col.remove("潘粤明");
           System.out.println("res2:"+res2);// res2:false
           System.out.println("col:" + col);// col:[谢霆锋, 贾乃亮, 陈羽凡]
    
           //- `public boolean contains(Object obj)`: 判断当前集合中是否包含给定的对象。
           // 判断是否包含王宝强这个元素
           boolean res3 = col.contains("王宝强");
           System.out.println("res3:"+res3);// res3: false
    
           // 判断是否包含贾乃亮这个元素
           boolean res4 = col.contains("贾乃亮");
           System.out.println("res4:"+res4);// res4: true
    
           //- `public boolean isEmpty()`: 判断当前集合是否为空。
           boolean res5 = col.isEmpty();
           System.out.println("col是否为空:"+res5);// false
    
           //- `public int size()`: 返回集合中元素的个数。
           int size = col.size();
           System.out.println("元素个数:"+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() 删除当前迭代出来的元素

  • 案例:

    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Iterator;
    
    /**
     * Created by PengZhiLin on 2021/8/3 9:51
     */
    public class Test {
        public static void main(String[] args) {
            // 创建Collection集合对象,限制集合元素的类型为String类型
            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:[]
    
        }
    }
    
    

2.2 迭代器的实现原理

  • 迭代器的实现原理

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

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

image-20210323101019946

2.3 迭代器的常见问题

常见问题一

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

    /**
     * Created by PengZhiLin on 2021/8/3 10:02
     */
    public class Test1 {
        public static void main(String[] args) {
    
            // 创建Collection集合对象,限制集合元素的类型为String类型
            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);
            }
            System.out.println("col:" + col);
    
            // 在进行集合元素获取时,如果集合中已经没有元素可以迭代了,
            // 还继续使用迭代器的next方法,将会抛出java.util.NoSuchElementException没有集合元素异常。
            //String e = it.next();// 报NoSuchElementException没有集合元素异常。
    
    
            // 解决办法: 重新获取迭代器
            // 获取迭代器
            Iterator<String> it1 = col.iterator();
    
            // 循环判断是否有元素可以取出来
            while (it1.hasNext()) {
                // 说明有元素可以取出来,取出来
                String e = it1.next();
                System.out.println("取出来的元素:" + e);
            }
            System.out.println("col:" + col);
    
        }
    }
    
    
  • 解决方式: 使用集合从新获取一个新的迭代器对象来使用

常见问题二

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

    /**
     * Created by PengZhiLin on 2021/8/3 10:02
     */
    public class Test2 {
        public static void main(String[] args) {
    
            // 创建Collection集合对象,限制集合元素的类型为String类型
            Collection<String> col = new ArrayList<>();
            //Collection<String> col = new CopyOnWriteArrayList<>();
    
            // 往集合中添加元素
            col.add("谢霆锋");
            col.add("王宝强");
            col.add("贾乃亮");
            col.add("陈羽凡");
    
            // 获取迭代器
            Iterator<String> it = col.iterator();
    
            // 循环判断是否有元素可以取出来
            while (it.hasNext()) {
                // 说明有元素可以取出来,取出来
                String e = it.next();
                System.out.println("取出来的元素:" + e);
    
                // 迭代的同时,往集合中,添加或者删除元素---->不可以的,会报ConcurrentModificationException并发修改异常
                col.add("潘粤明");// 解决方式:使用CopyOnWriteArrayList集合
                //col.remove(e);// --->解决方式:使用迭代器的删除方法
    
                // 迭代器自己删除----可以的
                //it.remove();
            }
            System.out.println("col:" + col);
    
    
        }
    }
    
    
  • 解决办法:

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

2.4

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

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

  • 格式:

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

    /**
     * Created by PengZhiLin on 2021/8/3 10:16
     */
    public class Test {
        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);
            }
        }
    }
    
    

第三章 泛型

3.1 泛型的概述

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

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

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

  • 泛型的好处:

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

    • 集合不使用泛型

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

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

        /**
         * Created by PengZhiLin on 2021/8/3 10:44
         */
        public class Test {
            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());
                    }
                }
            }
        }
        
        
    • 集合使用泛型

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

        /**
         * Created by PengZhiLin on 2021/8/3 10:44
         */
        public class Test {
            public static void main(String[] args) {
                // 创建Collection集合,使用泛型
                Collection<String> 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());
                    }
                }*/
        
                for (String str : col) {
                    System.out.println("字符串元素的长度:"+str.length());
                }
            }
        }
        
        

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

  • 定义含有泛型的类

    • 格式:

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

      /**
       * Created by PengZhiLin on 2021/8/3 10:54
       */
      public class MyGenericClass<E> {
      
          // 成员变量
          E e;// 未知类型的成员变量
      
          // 成员方法
          public E method(E e){
              return e;
          }
      
      }
      
      
  • 使用含有泛型的类

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

    • 案例

      /**
       * Created by PengZhiLin on 2021/8/3 11:02
       */
      public class Test {
          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);
      
      
          }
      }
      
      

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

  • 定义含有泛型的接口

    • 格式:

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

      /**
       * Created by PengZhiLin on 2021/8/3 11:08
       */
      public interface MyGenericInterface<E>{
          
          E method1(E e);
          
      }
      
      
  • 使用含有泛型的接口

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

      • 格式:

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

        /**
         * Created by PengZhiLin on 2021/8/3 11:11
         */
        public class MyImp1 implements MyGenericInterface<String> {
            @Override
            public String method1(String s) {
                return "String字符串";
            }
        }
        
        
    • 方式二:实现类实现接口的时候,不确定接口泛型的具体数据类型,而是创建实现类对象的时候确定泛型的具体数据类型

      • 格式:

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

      /**
       * Created by PengZhiLin on 2021/8/3 11:13
       */
      // 实现类实现接口的时候不指定接口泛型的具体数据类型
      public class MyImp2<E> implements MyGenericInterface<E>{
          @Override
          public E method1(E e) {
              return e;
          }
      }
      
      
      public class Test {
          public static void main(String[] args) {
              // 创建MyImp1对象
              MyImp1 imp1 = new MyImp1();
              String res1 = imp1.method1("itheima");
              System.out.println("res1:"+res1);
      
              // 创建MyImp2对象,指定泛型的具体数据类型为String
              MyImp2<String> imp2 = new MyImp2<>();
              String res2 = imp2.method1("itcast");
              System.out.println("res2:"+res2);
      
              // 创建MyImp2对象,指定泛型的具体数据类型为Integer
              MyImp2<Integer> imp3 = new MyImp2<>();
              Integer res3 = imp3.method1(100);
              System.out.println("res3:"+res3);
          }
      }
      
      

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

  • 定义含有泛型的方法

    • 格式:

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

      /**
       * Created by PengZhiLin on 2021/8/3 11:22
       */
      public class MyGenericMethod {
          
          public <T> T method(T t){
              return t;
          }
          
      }
      
      
  • 使用含有泛型的方法

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

    • 案例:

      /**
       * Created by PengZhiLin on 2021/8/3 11:23
       */
      public class Test {
          public static void main(String[] args) {
              // 调用含有泛型方法的时候,确定泛型的具体数据类型
              Integer res1 = MyGenericMethod.method(100);
              System.out.println("res1:"+res1);
      
              String res2 = MyGenericMethod.method("itheima");
              System.out.println("res2:"+res2);
          }
      }
      
      

3.5 泛型通配符

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

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

    • 泛型本身不存在继承关系,不可以给已指定泛型的变量接收有其他泛型类型的对象

      • 翻译: 左边变量泛型指定什么类型,右边对象的泛型就是什么类型,如果不一致就会报错(无论是否有父子关系,都报错)
      • eg: Collection col = new ArrayList();// 正确的
      • eg: Collection col = new ArrayList();// 错误的
      • ...
    • 如果想要使变量在未来接收有泛型定义的对象,又不确定泛型要定义的类型可以使用泛型通配符

      • Collection<?> col3 = new ArrayList();// 正确的
      • Collection<?> col4 = new ArrayList();//正确的
      • Collection<?> col5 = new ArrayList();// 正确的
  • 通配符基本使用

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

    • 案例:

      Collection<?> col3 = new ArrayList<String>();// 正确的
      Collection<?> col4 = new ArrayList<Integer>();//正确的
      Collection<?> col5 = new ArrayList<Object>();// 正确的
      ...
      
    • 注意:

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

      /**
       * Created by PengZhiLin on 2021/8/3 11:46
       */
      public class Test {
          public static void main(String[] args) {
              // 泛型本身不存在继承关系,不可以给已指定泛型的变量接收有其他泛型类型的对象
              //Collection<Object> col1 = new ArrayList<Object>();// 正确的
              //Collection<Object> col2 = new ArrayList<String>();// 错误的
      
              // 如果想要使变量在未来接收有泛型定义的对象,又不确定泛型要定义的类型可以使用泛型通配符
              //Collection<?> col3 = new ArrayList<String>();// 正确的
              //Collection<?> col4 = new ArrayList<Integer>();//正确的
              //Collection<?> col5 = new ArrayList<Object>();// 正确的
      
              //- 如果使用了泛型通配符,那么该集合变量元素类型默认是Object类型
              //- 如果使用了泛型通配符,那么该集合变量只能取元素,无法增加元素
              //Collection<?> col3 = new ArrayList<String>();
              // 添加元素
              //col3.add("abc");// 编译报错
      
              /*for (Object obj : col3) {
                  System.out.println(obj);
              }*/
      
              // 创建ArrayList集合,限制集合元素的类型为Object
              ArrayList<Object> list1 = new ArrayList<>();
      
              // 创建ArrayList集合,限制集合元素的类型为String
              ArrayList<String> list2 = new ArrayList<>();
      
              // 创建ArrayList集合,限制集合元素的类型为Number
              ArrayList<Number> list3 = new ArrayList<>();
      
              // 创建ArrayList集合,限制集合元素的类型为Integer
              ArrayList<Integer> list4 = new ArrayList<>();
              list4.add(100);
              list4.add(200);
              list4.add(300);
      
              //method1(list1);
              //method1(list2);
              //method1(list3);
              method1(list4);
              System.out.println(list4);
      
          }
      
          // 需求:定义一个方法,可以接收main方法中所有的ArrayList集合
          public static void method1(ArrayList<?> list){
              // 循环取元素---集合元素是Object类型
              // 如果使用了泛型通配符,那么该集合变量元素类型默认是Object类型
              /*for (Object obj : list) {
                  System.out.println(obj);
              }*/
              
              
              //- 如果使用了泛型通配符,那么该集合变量只能取元素,无法增加元素
              // 往list集合中添加元素
              //list.add(400);// 编译报错
      
          }
      
      }
      
      
  • 通配符高级使用----受限泛型

    • 上限:

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

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

      /**
       * Created by PengZhiLin on 2021/8/3 12:02
       */
      public class Test {
          public static void main(String[] args) {
              // 创建ArrayList集合,限制集合元素的类型为Object
              ArrayList<Object> list1 = new ArrayList<>();
      
              // 创建ArrayList集合,限制集合元素的类型为String
              ArrayList<String> list2 = new ArrayList<>();
      
              // 创建ArrayList集合,限制集合元素的类型为Number
              ArrayList<Number> list3 = new ArrayList<>();
      
              // 创建ArrayList集合,限制集合元素的类型为Integer
              ArrayList<Integer> list4 = new ArrayList<>();
      
              //method1(list1);// 编译报错
              //method1(list2);// 编译报错
              method1(list3);
              method1(list4);
      
              System.out.println("---------------");
      
              method2(list1);
              //method2(list2);// 编译报错
              method2(list3);
              //method2(list4);// 编译报错
          }
      
          // 定义一个方法,接收泛型为Number类型或者其子类类型的ArrayList集合
          public static void method1(ArrayList<? extends Number> list){
      
          }
      
          // 定义一个方法,接收泛型为Number类型或者其父类类型的ArrayList集合
          public static void method2(ArrayList<? super Number> list){
      
          }
      }
      
      

第四章 数据结构

4.1 数据结构介绍

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

4.2 常见数据结构

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

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

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

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

这里两个名词需要注意:

  • 压栈:就是存元素。即,把元素存储到栈的顶端位置,栈中已有元素依次向栈底方向移动一个位置。
  • 弹栈:就是取元素。即,把栈的顶端位置元素取出,栈中已有元素依次向栈顶方向移动一个位置。

队列

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

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

    • 先进先出(即,存进去的元素,要在后它前面的元素依次取出后,才能取出该元素)。例如,小火车过山洞,车头先进去,车尾后进去;车头先出来,车尾后出来。
    • 队列的入口和出口在两侧。例如,下图中的左侧为入口,右侧为出口。

数组

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

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

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

  • 增删元素慢

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

  • 指定索引位置删除元素:需要创建一个新数组,把原数组元素根据索引,复制到新数组对应索引的位置,原数组中指定索引位置元素不复制到新数组中。如下图

链表

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

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

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

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

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

4.3 树基本结构介绍

树具有的特点

  1. 每一个节点有零个或者多个子节点
  2. 没有父节点的节点称之为根节点,一个树最多有一个根节点。
  3. 每一个非根节点有且只有一个父节点

1562637870270

名词 含义
节点 指树中的一个元素
节点的度 节点拥有的子树的个数,二叉树的度不大于2
叶子节点 度为0的节点,也称之为终端结点
高度 叶子结点的高度为1,叶子结点的父节点高度为2,以此类推,根节点的高度最高
根节点在第一层,以此类推
父节点 若一个节点含有子节点,则这个节点称之为其子节点的父节点
子节点 子节点是父节点的下一层节点
兄弟节点 拥有共同父节点的节点互称为兄弟节点

二叉树

如果树中的每个节点的子节点的个数不超过2,那么该树就是一个二叉树。

1565609702432

二叉查找树

二叉查找树的特点:

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

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

1565611710800

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

注意:二叉查找树存在的问题:会出现"瘸子"的现象,影响查询效率

1565611927771

平衡二叉树

概述

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

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

如下图所示:

1565611927771

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

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

旋转

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

左旋:

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

右旋:

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

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

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

1562644020804

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

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

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

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

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

1562644117681

左左调整其实比较简单,只需要对节点进行右旋即可,如下图,对节点"10"进行右旋,

1562645661857

1562645709201

左右

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

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

插入了节点"10"或"8"导致失衡。

1562644916480

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

讲的右左,即左右跟右左互为镜像,左左跟右右也互为镜像。

1562645170454

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

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

1562645351977

右左

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

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

插入了节点"12"或"14"导致失衡。

1562645765291

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

1562645894833

右右

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

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

"14"或"19"导致失衡。

1562645951703

右右只需对节点进行一次左旋即可调整平衡,如下图,对"11"节点进行左旋。

1562646135227

红黑树

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

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

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

红黑树的特性:

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

如下图所示就是一个

1562653205543

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

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接口新增常用方法的使用

/**
 * Created by PengZhiLin on 2021/8/3 15:41
 */
public class Test {
    public static void main(String[] args) {
        // 创建List集合,限制集合元素的类型为String类型
        List<String> list = new ArrayList<>();

        // 往集合中添加元素
        list.add("林青霞");
        list.add("张曼玉");
        list.add("王祖贤");
        list.add("张敏");
        //list:[林青霞, 张曼玉, 王祖贤, 张敏]
        System.out.println("list:" + list);

        // - `public void add(int index, E element)`: 将指定的元素,添加到该集合中的指定位置上。
        // 往索引为1的位置添加一个元素:温碧霞
        list.add(1, "温碧霞");
        //list:[林青霞, 温碧霞, 张曼玉, 王祖贤, 张敏]
        System.out.println("list:" + list);

        //- `public E get(int index)`:返回集合中指定位置的元素。
        // 获取索引为3的元素
        System.out.println("索引为3的元素:"+list.get(3));// 王祖贤

        //- `public E remove(int index)`: 移除列表中指定位置的元素, 返回的是被移除的元素。
        // 删除索引为1的元素
        String removeE = list.remove(1);
        System.out.println("被删除的元素:"+removeE);// 温碧霞
        // list:[林青霞, 张曼玉, 王祖贤, 张敏]
        System.out.println("list:" + list);


        //- `public E set(int index, E element)`:用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
        // 把张曼玉换成邱淑贞
        String setE = list.set(1, "邱淑贞");
        System.out.println("被替换的元素:"+setE);// 张曼玉
        // list:[林青霞, 邱淑贞, 王祖贤, 张敏]
        System.out.println("list:" + list);

    }
}

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

    /**
     * Created by PengZhiLin on 2021/8/3 15:48
     */
    public class Test2 {
        public static void main(String[] args) {
            // 创建List集合,限制集合中元素的类型为Integer类型
            List<Integer> list = new ArrayList<>();
    
            // 添加元素
            list.add(1);
            list.add(2);
            list.add(3);
            list.add(4);
            //删除之前:[1, 2, 3, 4]
            System.out.println("删除之前:" + list);
    
            // 删除索引为2的元素----如果集合元素为Integer类型,那么删除的时候优先根据索引删除
            list.remove(2);
            //删除之后:[1, 2, 4]
            System.out.println("删除之后:" + list);
    
            // 删除2这个元素
            //list.remove(1);// 根据索引删除  remove(int index)
            Integer i = 2;
            list.remove(i);// 2  remove(Object obj)
            //删除之后:[1, 4]
            System.out.println("删除之后:" + list);
    
        }
    }
    
    

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):将元素推入此列表所表示的堆栈
      
      
    • 案例:

      /**
       * Created by PengZhiLin on 2021/8/3 15:56
       */
      public class Test {
          public static void main(String[] args) {
              // 创建LinkedList集合,限制集合元素的类型为String
              LinkedList<String> list = new LinkedList<>();
      
              // 往集合中添加元素
              list.add("林青霞");
              list.add("张曼玉");
              list.add("王祖贤");
              list.add("张敏");
              System.out.println("list:" + list);
      
              //public void addFirst(E e):将指定元素插入此列表的开头
              //public void addLast(E e):将指定元素添加到此列表的结尾
              list.addFirst("温碧霞");
              list.addLast("邱淑贞");
              System.out.println("list:" + list);
      
              //public E getFirst():返回此列表的第一个元素
              //public E getLast():返回此列表的最后一个元素
              String firstE = list.getFirst();
              String lastE = list.getLast();
              System.out.println("第一个元素:"+firstE);//  温碧霞
              System.out.println("最后一个元素:"+lastE);// 邱淑贞
      
              //public E removeFirst():移除并返回此列表的第一个元素
              //public E removeLast():移除并返回此列表的最后一个元素
              String removeFirst = list.removeFirst();
              String removeLast = list.removeLast();
              System.out.println("被删除的第一个元素:"+removeFirst);//  温碧霞
              System.out.println("被删除的最后一个元素:"+removeLast);// 邱淑贞
              System.out.println("list:" + list);// [林青霞, 张曼玉, 王祖贤, 张敏]
      
              //public E pop():从此列表所表示的堆栈处弹出一个元素====>removeFirst()
              //public void push(E e):将元素推入此列表所表示的堆栈==>addFirst()
              String popE = list.pop();
              System.out.println("弹出的第一个元素:" + popE);// 林青霞
      
              list.push("李嘉欣");
              System.out.println("list:" + list);// [李嘉欣, 张曼玉, 王祖贤, 张敏]
          }
      }
      
      

5.4 集合综合案例

需求:

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

    使用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 展示牌

实现:

/**
 * Created by PengZhiLin on 2021/8/3 16:14
 */
public class Test {
    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);

    }
}

总结

必须练习:
	1.总结单列集合继承体系和各个单列集合的特点---->默写
    2.Collection集合的常用方法
    3.List集合的常用方法
    4.LinkedList集合的特有方法
    5.迭代器的基本使用,增强for循环的使用--->使用迭代器,增强for循环遍历集合元素
    6.使用含有泛型的类,含有泛型的接口,含有泛型的方法---->ArrayList<E>类,List<E>接口....
    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()
    4.一边迭代一边删除,使用迭代器的 remove()
          
- 能够使用增强for循环遍历集合和数组
    格式:
		for(数据类型 变量名: 数组名\集合名){
            
        }
	快捷键: 数组名.for   或者  集合名.for
        
- 能够理解泛型上下限
  上限: <? extends 类名>
  下限: <? super 类名>
      
- 能够阐述泛型通配符的作用
    如果想要使变量在未来接收有泛型定义的对象,又不确定泛型要定义的类型可以使用泛型通配符?
      
- 能够说出常见的数据结构
  栈,队列,数组,链表,树
      
- 能够说出数组结构特点
- 能够说出栈结构特点
- 能够说出队列结构特点
- 能够说出单向链表结构特点
    - 栈结构: 先进后出
    - 队列结构:先进先出
    - 数组: 查询快,增删慢
    - 链表; 查询慢,增删快
    - 二叉查找树: 提高搜索效率
        
- 能够说出List集合特点
     元素有索引,元素存取有序,元素可以重复
        
- 能够完成斗地主的案例                   
     洗牌,发牌,造牌
posted on 2022-04-24 23:40  ofanimon  阅读(30)  评论(0编辑  收藏  举报
// 侧边栏目录 // https://blog-static.cnblogs.com/files/douzujun/marvin.nav.my1502.css