Java基础之集合

JAVA学习网 2020-09-04 10:22:04

集合 java.util.Collection \ java.util.Iterator \ java.util.Map

  1. 集合和数组的区别

    • 数组长度是固定的, 集合长度是可变的
    • 数组存储同一类型的基本数据类型值; 集合存储的是对象, 对象类型可以不一致
  2. 集合框架-Collection接口

    • 接口结构

      Collection接口
              |                                   1) 定义所有集合中共性的方法
              |                                   2) 底层是接口或抽象类, 无法直接创建对象, 需要使用创建子类对象使用
              |-  List接口
                      |                           1) 有序
                      |                           2) 允许重复元素
                      |                           3) 有索引, 可使用for循环遍历
                      |-  ArrayList集合
                      |                           底层数组实现, 查询快, 增删慢
                      |-  LinkedList集合
                      |                           底层链表实现, 查询慢, 增删快
                      |-  Vector集合
              |-  Set接口
                      |                           1) 不允许重复元素
                      |                           2) 无索引, 不能使用普通for循环遍历
                      |-  HashSet集合
                      |                           底层哈希表实现, 存取无序; hashCode()和equals()方法保证数据元素的唯一性
                              |-LinkedHashSet集合
                              |                   底层 哈希表+链表 实现, 存取有序
                      |-  TreeSet集合
                      |                           底层 红黑树 实现
      
    • 常用方法

      boolean add(E e):    向集合中添加元素
      boolean remove(E e)
      void clear()
      boolean contains(E e)
      boolean isEmpty()
      int size()
      Object[] toArray():   将集合转为数组
      
  3. 集合框架-Iterator接口

    • Iterator迭代器

      • 迭代遍历Collection集合中的元素, 遍历前要先判断集合中是否有元素
      • 适合无索引集合的遍历
      • Iterator是一个接口, 无法直接使用; 使用Collection接口中提供的 Iterator iterator()方法, 返回Iterator接口的实现类对象
    • 常用方法

      public E next():            返回迭代的下一个元素
      public boolean hasNext():   判断集合中是否有元素, 若有元素, 返回true
      

      小Demo

      private static Collection<String> demo1() {
          Collection<String> col = new ArrayList<>();
          col.add("lisa");
          col.add("sam");
          col.add("tom");
      
          Iterator<String> iterator = col.iterator(); // 多态 向下转型
          while (iterator.hasNext()) {
              System.out.println(iterator.next());
          }
          return col;
      }
      
    • 增强for循环

      • 底层仍然是Iterator迭代器, 遍历目标只能说集合或数组

      • for (数据类型 变量名 : 集合/数组) {...}

      • 小Demo

        private static void demo2(Collection<String> col) {
            for (String str : col) {
                System.out.println(str);
            }
            System.out.println("=====================");
        
            int[] num = {1,2,3,4,5};
            for (int i : num) {
                System.out.println(i);
            }
        }
        
  4. 集合框架-Map接口

    • 特点

      • Map集合是一个双列集合, 一个元素包含两个值(key, value)
      • Map集合中key和value的数据类型可相同, 可不同
      • Map集合中key不允许重复, value可重复, 并且key和value可为null值
      • Map集合中key和value是一一对应的
    • Map接口结构

      |-  HashMap
            |                   1) 底层 哈希表 实现, 存取无序, 多线程, 线程不安全
            |                   2) jdk1.8之前: 数组 + 单向链表
            |                   3) jdk1.8之后: 数组 + 单向链表 / 红黑树(长度超多8)
            |-  LinkedHashMap
            |                   1) 底层 哈希表+链表 实现, 存取有序
      |-  TreeMap
      |                         底层 红黑树 实现
      |-  HashTable
      |                         1) 底层是线程安全的 哈希表, 单线程, 速度慢, key和value值不可为null
      |                         2) 其子类 Properties集合是一个唯一和IO流有结合的集合
      
    • 常用方法

      public V put(K key, V value):           添加键值对(key, value)
          若向map集合放入的key不是重复的,返回null; 反之, 使用key对应的新value值替换原来的value值, 并返回旧的value值
      public V get(Object key):               根据key返回value
      public V remove(Object key)
          通过key删除键值对, 若key存在返回删除对应的value值, 反之则返回null
      boolean containsKey(Object key)
      public Set<K> keySet():                 将Map集合中所有的Key存储到Set集合中, 然后用Set集合的方法遍历所有的key
      public Set<Map.Entry<K, V>> entrySet()  将Map集合中所有的key-value键值对存储到Set集合中, 然后遍历所有的key-value对
      

      小Demo-Map常用方法

      private static void demo8() {
          Map<Integer, String> map = new HashMap<>();
          // public V put(K key, V value)
          String mp1 = map.put(1, "lisa");
          System.out.println(mp1);    // null
          String mp2 = map.put(1, "sam");
          System.out.println(mp2);    // lisa
      
          map.put(2, "jerry");
          map.put(3, "sam");  // key不能重复, 但是value可以重复
      
          // public Set<K> keySet()
          Set<Integer> set1 = map.keySet();    // [1,2,3]
          for (Integer i : set1) {             // 增强for循环遍历
              System.out.println(map.get(i));
          }
      
          // public Set<Map.Entry<K, V>> entrySet()
          Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator();    // Iterator迭代器遍历
          while (it.hasNext()) {
              Map.Entry<Integer, String> entry = it.next();
              Integer key = entry.getKey();
              String value = entry.getValue();
              System.out.println(key + "-" + value);
          }
      
      }
      

      小Demo-Map存储自定义类型作为key

      private static void demo9() {
          /*
              String类型作为Map集合的key
              String已经重写了hashCode()方法和equals()方法, 保证了存入Map集合时key的唯一性
              使用keySet()方法遍历, 运行结果:
                  DivMapClass{name='Hanmeimei', age=19}
                  DivMapClass{name='Linda', age=26}
                  DivMapClass{name='Tomas', age=22}
           */
          Map<String, DivMapClass> map1 = new HashMap<>();
          map1.put("China", new DivMapClass("Liming", 18));
          map1.put("America", new DivMapClass("Linda", 26));
          map1.put("Germany", new DivMapClass("Tomas", 22));
          map1.put("China", new DivMapClass("Hanmeimei", 19));
      
          Set<String> set1 = map1.keySet();
          for (String s : set1) {
              DivMapClass divMapClass = map1.get(s);
              System.out.println(divMapClass);
          }
      
          /*
              自定义DivMapClass类作为Map集合的key
              DivMapClass类需要自己重写hashCode()方法和equals()方法, 保证类作为key的唯一性
              重写前, 使用entrySet()的Iterator迭代器遍历, 运行结果:
                  DivMapClass{name='Tomas', age=22}-Germany
                  DivMapClass{name='Linda', age=26}-America
                  DivMapClass{name='Liming', age=18}-China
                  DivMapClass{name='Liming', age=18}-Japan
              重写后, 使用entrySet()的增强for循环遍历, 运行结果:
                  DivMapClass{name='Tomas', age=22}-Germany
                  DivMapClass{name='Liming', age=18}-Japan
                  DivMapClass{name='Linda', age=26}-America
           */
          Map<DivMapClass, String> map2 = new HashMap<>();
          map2.put(new DivMapClass("Liming", 18), "China");
          map2.put(new DivMapClass("Linda", 26), "America");
          map2.put(new DivMapClass("Tomas", 22), "Germany");
          map2.put(new DivMapClass("Liming", 18), "Japan");
      
      //        Iterator<Map.Entry<DivMapClass, String>> it = map2.entrySet().iterator();
      //        while (it.hasNext()) {
      //            Map.Entry<DivMapClass, String> next = it.next();
      //            DivMapClass key = next.getKey();
      //            String value = next.getValue();
      //            System.out.println(key + "-" + value);
      //        }
      
          for (Map.Entry<DivMapClass, String> entry : map2.entrySet()) {
              DivMapClass key = entry.getKey();
              String value = entry.getValue();
              System.out.println(key + "-" + value);
          }
      }
      
  5. 泛型

    • 是一种未知的数据类型, 不知道使用什么数据类型时使用

    • 两种方法

      • 创建集合对象时不使用泛型

        集合默认是Object类型, 可以存储任意类型的数据; 不安全, 会引发异常

        ex. ArrayList<String> list = new ArrayList<>()
        
      • 创建集合对象时使用泛型

        集合只能存储指定泛型类型的数据; 但是避免了类型转换的麻烦

        ex. ArrayList<String> list = new ArrayList<>()
        
    • 定义一个泛型类 GenericClass

      类似ArrayList, 可以直接使用指定的数据类型

      小Domo

      public class GenericClass<E> {
          private E variable;
          public GenericClass() {}
      
          public GenericClass(E variable) {
              this.variable = variable;
          }
      
          public E getVariable() {
              return variable;
          }
      
          public void setVariable(E variable) {
              this.variable = variable;
          }
      }
      
      private static void demo3() {
          GenericClass<Integer> egi = new GenericClass<>();    // 创建ExampleGeneric对象, 泛型使用Integer类型
          egi.setVariable(366);
          Integer i = egi.getVariable();
          System.out.println(i);                               // 366
      
          GenericClass<String> egs = new GenericClass<>();     // 创建ExampleGeneric对象, 泛型使用String类型
          egs.setVariable("hello");
          String str = egs.getVariable();
          System.out.println(str);                             // hello
      }
      
    • 定义一个泛型接口 GenericInterface

      • 定义接口的实现类, 并指定接口的泛型类型

        • 比如Scanner类实现了Iterator接口, 并指定接口泛型为String类型. 所以重写的next()方法的类型默认就为String

          ex. public final class Scanner implements Iterator<String>
          
      • 接口使用什么泛型, 实现类就使用什么泛型. 相当于定义一个含有泛型的类, 创建对象时再确定泛型的类型

        • 比如ArrayList类实现了List接口, 并直接使用List接口的泛型. 所以使用时再手动给定泛型的类型

          ex. public class ArrayList<E> implements List<E>
          
      • 小Demo

        public class GenericInterfaceImpl1 implements GenericInterface<String> {
            @Override
            public void method(String s) {
                System.out.println(s);
            }
        }
        
        private static void demo4() {
            // 实现方法1
            GenericInterfaceImpl1 gi1 = new GenericInterfaceImpl1();
            gi1.method("字符串类型");
            System.out.println("=============================");
        
            // 实现方法2
            GenericInterfaceImpl2<Integer> gi2 = new GenericInterfaceImpl2<>();
            gi2.method(5);
            GenericInterfaceImpl2<Double> gi3 = new GenericInterfaceImpl2<>();
            gi3.method(3.14);
        }
        
  • 通配符

    • 不知道使用什么类型接收数据时, 可以使用 ? 表示未知通配符

      • 不同创建对象使用, 只能作为方法的参数使用. 此时只能接收数据, 不能往集合中存储数据
    • 小Demo

      private static void demo5() {
          ArrayList<Integer> list1 = new ArrayList<>();
          list1.add(1);
          list1.add(2);
          ArrayList<String> list2 = new ArrayList<>();
          list2.add("alex");
          list2.add("fox");
      
          printList(list1);                         // 作为方法的参数使用
          printList(list2);
      
      //        ArrayList<?> list = new ArrayList<?>(); // 不能定义使用
      }
      
      private static void printList(ArrayList<?> list) {
          Iterator ie = list.iterator();
          while (ie.hasNext()) {
              System.out.println(ie.next());
          }
      }
      
  • 高级通配符 - 受限泛型

    • 泛型的上限

      只能接收该类型及其子类型: 类型名称 <? extends 类> 对象名称

    • 泛型的下限

      只能接收该类型及其父类型: 类型名称 <? super 类> 对象名称

    • 小Demo

      private static void demo7() {
          Collection<Integer> list1 = new ArrayList<>();
          Collection<String> list2 = new ArrayList<>();
          Collection<Number> list3 = new ArrayList<>();   // Number是Integer的父类
          Collection<Object> list4 = new ArrayList<>();   // 所有类的父类
      
          getElement1(list1);
      //        getElement1(list2); // Error
          getElement1(list3);
      //        getElement1(list4); //Error
      
      //        getElement2(list1); //Error
      //        getElement2(list2); //Error
          getElement2(list3);
          getElement2(list4);
      }
      // 泛型的上限: 此时的泛型 ? 必须是Number类型或者Number的子类
      public static void getElement1(Collection<? extends Number> col){}
      // 泛型的上限: 此时的泛型 ? 必须是Number类型或者Number的父类
      public static void getElement2(Collection<? super Number> col){}
      
阅读(1145) 评论(0)