《Java语言高级特性(阿里云大学)》笔记 第28~34章 关于类集(文档+思维导图)

课程链接:https://edu.aliyun.com/course/1012

第1~6章 关于线程:https://blog.csdn.net/weixin_43494837/article/details/113764127?spm=1001.2014.3001.5501

第7~14章 类库+正则+国际化+比较器:https://blog.csdn.net/weixin_43494837/article/details/113764156?spm=1001.2014.3001.5501

第15~20章 关于IO:https://blog.csdn.net/weixin_43494837/article/details/113764173?spm=1001.2014.3001.5501

第21~27章 关于反射:https://blog.csdn.net/weixin_43494837/article/details/112797304?spm=1001.2014.3001.5501

(思维导图在最后)

————————————————————————————————————

第28章:类集框架简介

课时123:类集框架简介

  • 类集:一套动态对象数组的实现方案。

  • 在实际开发中,没有任何一项开发可以离开数组,但是传统的数组实现起来非常繁琐,而且长度是其致命伤。正因为长度问题,所以传统的数组是不可能大范围使用的,但开发又离不开数组,所以最初就只能依靠一些数据结构来实现动态的数组处理,其中最为重要的两个结构:链表、树。然而这些数据结构的实现,也有如下的一些问题:

    • 数据结构的代码实现困难,对于一般的开发者是无法进行使用的;
    • 对于链表或二叉树,
      • 进行更新处理时,维护是非常麻烦的;
      • 还需要尽可能保证操作其的性能。

    因此,在JDK1.2,Java引入了类集开发框架。主要就是对常见的数据结构进行完整的实现包装,并提供一系列的接口与实现子类,来帮助用户减少使用数据结构而带来的开发困难。(最初的类集实现,由于Java本身的技术所限,所以对于数据的控制并不严格,全部采用了Object类型进行数据接收;在JDK1.5后,由于泛型技术的推广,所以类集本身也得到了良好的改进,可以直接使用泛型来保存相同类型的数据;并且随着数据量的不断增加,从JDK1.8开始,类集的实现算法也得到了良好的性能提升。)

  • 类集框架的核心接口:Collection、List、Set、Map、Iterator、Enumeration、Queue、ListIterator。

课时124:Collection接口简介

  • java.util.Collection是单值集合操作的最大的父接口,在该接口中定义有所有的单值数据的处理操作。(单值:每次操作只能保存一个对象,就类似每次子弹上膛只能上一颗)。

  • Collection包是从JDK1.2开始有的(JDK1.2最大的特征,除了引入图形界面Swing开发包,然后就是类集)。

  • Collection接口的核心方法:

    No 方法 类型 描述
    1 public boolean add(E e) 普通 保存数据
    2 public boolean addAll(Collection c) 普通 追加一组数据(如一个链表连接另一个链表)
    3 public void clear() 普通 清空集合(让根节点为空,并进行GC处理)
    4 public boolean contains(Object o) 普通 查询数据是否存在(需要equals()方法支持)
    5 public boolean remove(Object o) 普通 删除数据(需要equals()方法支持)
    6 public int size() 普通 获取数据长度(最大值为Integer.MAX_VALUE)
    7 public Object[] toArray() 普通 将集合转为对象数组
    8 public Iterator iterator() 普通 将集合转为Iterator接口

    以上方法中,【增加】add() 和 【输出】iterator() 这两个最常用。

  • 在JDK1.5版本之前,Collection只是一个独立的接口;在JDK1.5后,提供了Iterable父接口,并且在JDK1.8后针对于Iterable接口也得到了一些扩充。另外,在JDK1.2~JDK1.4的时代里,如果要进行集合的使用往往会直接操作Collection接口,而从JDK1.5时代开始,更多情况下选择的都是Collection的两个子接口:允许重复的List子接口、不允许重复的Set子接口。

    《Java语言高级特性(阿里云大学)》笔记 第28~34章 关于类集(文档+思维导图)_第1张图片

第29章:List集合

课时125:List接口简介

  • List是Collection的子接口,其最大的特点是允许保存有重复元素数据,该接口的定义如下:

    public interface List extends Collection
    
  • List子接口对于Collection接口进行了方法扩充:

    No 方法 类型 描述
    1 public E get(int index) 普通 获取指定索引的数据
    2 public E set(int index, E element) 普通 修改指定索引的数据
    3 public ListIterator listIterator() 普通 将List转为ListIterator
  • List接口的常用子类:

    • ArrayList
    • LinkedList
    • Vector

    《Java语言高级特性(阿里云大学)》笔记 第28~34章 关于类集(文档+思维导图)_第2张图片

  • 从JDK1.9开始,List接口中追加有一些static方法,以方便用户的处理(如of()方法):

    import java.util.List;
    public class JavaDemo{
        public static void main(String[] args) {
            List list  = List.of("Hello", "World", "Hello", "Pikaqiu");
    //        System.out.println(list); // 输出:[Hello, World, Hello, Pikaqiu]
            Object[] objArr = list.toArray(); // 调用Collection接口中的toArray()方法
            for (Object obj : objArr) {
                System.out.print(obj + " "); // 输出:Hello World Hello Pikaqiu
            }
        }
    }
    
  • 在JDK1.8之后,Iterable父接口之中定义有一个forEach()循环输出的方法:

    default void forEach(Consumer action)
    

    使用forEach()方法实现输出:

    import java.util.List;
    public class JavaDemo{
        public static void main(String[] args) {
            List list  = List.of("Hello", "World", "Hello", "Pikaqiu");
            list.forEach(System.out::println);
    //        输出:
    //        Hello
    //        World
    //        Hello
    //        Pikaqiu
        }
    }
    

课时126:ArrayList子类

  • ArrayList类的定义:

    public class ArrayList
    extends AbstractList
    implements List, RandomAccess, Cloneable, Serializable
    

    《Java语言高级特性(阿里云大学)》笔记 第28~34章 关于类集(文档+思维导图)_第3张图片

  • 示例:

    import java.util.ArrayList;
    import java.util.List;
    public class JavaDemo{
        public static void main(String[] args) {
            List list = new ArrayList<>();
            list.add("Hello");
            list.add("World");
            list.add("Hello");
            list.add("Pikaqiu");
            list.add(null);
            System.out.println(list); // 输出:[Hello, World, Hello, Pikaqiu, null]
            System.out.println(list.get(0)); // 输出:Hello
        }
    }
    

    可以发现,ArrayList中保存的数据是:

    • 重复的
    • 顺序存储的
  • forEach()输出实现(非标准输出):

    list.forEach((str)->{ // 消费型函数式接口
                System.out.print(str + " "); // 输出:Hello World Hello Pikaqiu
            });
    

    需要注意的是,此种输出并不是正常开发情况下要考虑的操作形式。

  • ArrayList源码分析:

    • 无参构造:

      public ArrayList() {
          this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
      }
      

      DEFAULTCAPACITY_EMPTY_ELEMENTDATA常量定义:

      private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
      
    • 单参构造(可开辟指定容量的ArrayList):

      public ArrayList(int initialCapacity) {
              if (initialCapacity > 0) {
                  this.elementData = new Object[initialCapacity];
              } else if (initialCapacity == 0) {
                  this.elementData = EMPTY_ELEMENTDATA;
              } else {
                  throw new IllegalArgumentException("Illegal Capacity: "+
                                                     initialCapacity);
              }
          }
      

      elementData变量定义:

      transient Object[] elementData; // transient:不可序列化
      

      EMPTY_ELEMENTDATA常量定义:

      private static final Object[] EMPTY_ELEMENTDATA = {}; // 默认开辟空数组
      

      可以发现,ArrayList实际上封装的仍然是一个数组。

    • add()方法:

      如追加数据时,长度不足够,则会开辟一个更大的新数组,并将旧数组的内容拷贝到新数组里,然后再进行追加。

      • JDK1.9之后:ArrayList默认的构造只会使用默认的空数组,使用时才会开辟数组,默认的开辟长度为10;
      • JDK1.9之前:ArrayList默认的构造实际上就会默认开辟大小为10的数组。

      当ArrayList中保存的容量不足时,会采用成倍的方式进行增长,如第一次增长10,第二次增长就是20。

      所以再使用ArrayList子类时,一定要估算出数据量有多少,如果超过了10个,那么应该采用有参构造的方法进行创建,以避免产生垃圾数组。

课时127:ArrayList保存自定义类对象

  • 实现:

    import java.util.ArrayList;
    import java.util.List;
    class Person {
        private String name;
        private int age;
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        public String toString() {
            return "姓名:" + this.name + ",年龄:" + this.age;
        }
    }
    public class JavaDemo{
        public static void main(String[] args) {
            List list = new ArrayList<>();
            Person pikaqiu = new Person("皮卡丘", 6);
            Person xiaozhi = new Person("小智", 12);
            Person xiaojin = new Person("小进", 12);
            list.add(pikaqiu);
            list.add(xiaozhi);
            list.add(xiaojin);
            list.forEach(System.out::println);
            System.out.println("————————————");
            list.remove(xiaojin); // remove()方法,不常用
            list.forEach(System.out::println);
            System.out.println("————————————");
            System.out.println(list.contains(pikaqiu)); // contains()方法,不常用
        }
    }
    

课时128:LinkedList子类

  • LinkedList类的定义:

    public class LinkedList
    extends AbstractSequentialList
    implements List, Deque, Cloneable, Serializable
    

    《Java语言高级特性(阿里云大学)》笔记 第28~34章 关于类集(文档+思维导图)_第4张图片

  • 示例:

    import java.util.LinkedList;
    import java.util.List;
    public class JavaDemo{
        public static void main(String[] args) {
            List list = new LinkedList<>();
            list.add("Hello");
            list.add("world");
            list.add("Hello");
            list.add("pikaqiu");
            list.add(null);
            System.out.println(list); // 输出:[Hello, world, Hello, pikaqiu, null]
            // get()方法:根据索引获取数据内容
            System.out.println(list.get(0)); // 输出:Hello
        }
    }
    

    可以发现,LinkedList和ArrayList使用是完全一样的,但它们的内部实现机制是完全不同的。

    • LinkedList只提供了无参构造,并未提供有参构造。
    • 查看源码可以发现,LinkedList封装的其实就是链表的实现。
  • 面试题:请问ArrayList与LinkedList有什么区别?

    • ArrayList是数组实现的集合操作,而LinkedList是链表实现的集合操作;
    • 在使用get()方法时:ArrayList的时间复杂度为“O(1)”,而LinkedList的时间复杂度为“O(n)”(n为集合的长度);
    • ArrayList在使用时默认的初始化对象数组的大小长度为10,如果空间不足则会采用2倍形式进行容量的扩充,所以在保存大数据量时,可能会造成垃圾的产生以及性能的下降,而这时候可以使用LinkedList类保存。

课时129:Vector子类

  • Vector是一个原始古老的程序类,这个类是在JDK1.0时提供的。到了JDK1.2时,由于许多开发者已经习惯使用Vector,并且许多系统类也是基于Vector实现的,所以考虑到其使用的广泛性,类集框架将其保留了下来,并让其多实现了一个List接口。

  • Vector类的定义:

    public class Vector
    extends AbstractList
    implements List, RandomAccess, Cloneable, Serializable
    

    《Java语言高级特性(阿里云大学)》笔记 第28~34章 关于类集(文档+思维导图)_第5张图片

  • 示例:

    import java.util.List;
    import java.util.Vector;
    public class JavaDemo{
        public static void main(String[] args) {
            List list = new Vector<>();
            list.add("Hello");
            list.add("world");
            list.add("Hello");
            list.add("pikaqiu");
            list.add(null);
            System.out.println(list); // 输出:[Hello, world, Hello, pikaqiu, null]
            // get()方法:根据索引获取数据内容
            System.out.println(list.get(0)); // 输出:Hello
        }
    }
    
  • Vector类源码分析:

    public Vector() {
        this(10);
    }
    
    public Vector(int initialCapacity) {
        this(initialCapacity, 0);
    }
    
    public Vector(int initialCapacity, int capacityIncrement) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
        this.elementData = new Object[initialCapacity];
        this.capacityIncrement = capacityIncrement;
    }
    

    可以发现,Vector类的无参构造,是直接开辟一个10长度的数组,而后其余的实现操作与ArrayList是相同的。

    并且,Vector类中的操作方法采用的都是synchronized同步处理,而ArrayList并没有进行同步处理,所以Vector类中的方法是线程安全的,但性能不如ArrayList。

第30章:Set集合

课时130:Set接口简介

  • Set集合最大的特点:不允许保存重复元素。

  • 在JDK1.9以前,Set集合与Collection集合的定义并无差别;但在JDK1.9后,Set集合也像List集合一样扩充了一些static方法。

  • Set集合的定义:

    public interface Set extends Collection
    

    需要注意的是,Set集合并不像List集合那样扩充了许多的新方法,并没有List集合中的get()方法。

    《Java语言高级特性(阿里云大学)》笔记 第28~34章 关于类集(文档+思维导图)_第6张图片

  • 示例:

    import java.util.Set;
    public class JavaDemo{
        public static void main(String[] args) {
            Set set  = Set.of("Hello", "World", "Hello", "Pikaqiu"); // 抛出异常
            set.forEach(System.out::println);
    //        抛出异常:
    //        Exception in thread "main" java.lang.IllegalArgumentException: duplicate element: Hello
        }
    }
    

    可以发现,当Set使用of()新方法时,如有重复元素,则会抛出异常,这与传统的Set集合不保存重复元素的特点相一致。

  • Set接口的常用子类:

    • HashSet
    • TreeSet

课时131:HashSet子类

  • HashSet是Set接口中使用最多的一个子类,其最大的特点就是保存的数据是无序的。

  • HashSet类的定义:

    public class HashSet
    extends AbstractSet
    implements Set, Cloneable, Serializable
    

    《Java语言高级特性(阿里云大学)》笔记 第28~34章 关于类集(文档+思维导图)_第7张图片

  • 示例:

    import java.util.HashSet;
    import java.util.Set;
    public class JavaDemo{
        public static void main(String[] args) {
            Set set = new HashSet<>();
            set.add("Hello");
            set.add("Pikaqiu");
            set.add("Hello");
            set.add("World");
            set.forEach(System.out::println);
    //        输出:
    //        Hello
    //        World
    //        Pikaqiu
        }
    }
    

    可以发现,HashSet中保存的数据是:

    • 不重复的
    • 无序存储的

课时132:TreeSet子类

  • Set接口的另外一个子接口就是TreeSet,其与HashSet最大区别在于,TreeSet集合里保存的数据是有序的。

  • TreeSet类定义:

    public class TreeSet
    extends AbstractSet
    implements NavigableSet, Cloneable, Serializable
    

    在这个子类中依然继承了AbstractSet父抽象类,同时又实现了一个NavigableSet父接口:

    《Java语言高级特性(阿里云大学)》笔记 第28~34章 关于类集(文档+思维导图)_第8张图片

  • 示例:

    import java.util.Set;
    import java.util.TreeSet;
    public class JavaDemo{
        public static void main(String[] args) {
            Set set = new TreeSet<>();
            set.add("Hello");
            set.add("Pikaqiu");
            set.add("Hello");
            set.add("World");
            set.forEach(System.out::println);
    //        输出:
    //        Hello
    //        Pikaqiu
    //        World
        }
    }
    

    可以发现,HashSet中保存的数据是:

    • 不重复的
    • 顺序存储的

课时133:分析TreeSet子类排序操作

  • 使用TreeSet保存自定义类:

    import java.util.Set;
    import java.util.TreeSet;
    class Person {
        private String name;
        private int age;
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        public String toString() {
            return "姓名:" + this.name + ",年龄:" + this.age;
        }
    }
    public class JavaDemo{
        public static void main(String[] args) {
            Set set = new TreeSet<>();
            Person pikaqiu = new Person("皮卡丘", 6);
            Person xiaozhi = new Person("小智", 12);
            Person xiaojin = new Person("小进", 12); // 年龄相同,姓名不同
            Person xiaojin2 = new Person("小进", 12); // 数据重复
            Person xiaozhi2 = new Person("小智", 3);
            set.add(pikaqiu); // 抛出异常
            set.add(xiaozhi);
            set.add(xiaojin);
            set.add(xiaojin2);
            set.add(xiaozhi2);
            set.forEach(System.out::println);
    //        抛出异常:
    //        Exception in thread "main" java.lang.ClassCastException: Person cannot be cast to java.base/java.lang.Comparable
        }
    }
    

    从抛出的异常可以发现,TreeSet类允许保存自定义类数据,但自定义类必须要实现Comparable接口。

    • TreeSet本质上是利用TreeMap子类实现集合数据的存储,而TreeMap(实质上是树)就需要根据Comparable来确定大小。

    给Person类加上实现Comparable接口:

    import java.util.Set;
    import java.util.TreeSet;
    class Person implements Comparable{
        private String name;
        private int age;
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        public String toString() {
            return "姓名:" + this.name + ",年龄:" + this.age;
        }
        @Override
        public int compareTo(Person person) {
            if (this.age > person.age) return 1;
            else if (this.age < person.age) return -1;
            else return this.name.compareTo(person.name);
        }
    }
    public class JavaDemo{
        public static void main(String[] args) {
            Set set = new TreeSet<>();
            Person pikaqiu = new Person("皮卡丘", 6);
            Person xiaozhi = new Person("小智", 12);
            Person xiaojin = new Person("小进", 12); // 年龄相同,姓名不同
            Person xiaojin2 = new Person("小进", 12); // 数据重复
            Person xiaozhi2 = new Person("小智", 3);
            set.add(pikaqiu);
            set.add(xiaozhi);
            set.add(xiaojin);
            set.add(xiaojin2);
            set.add(xiaozhi2);
            set.forEach(System.out::println);
    //        输出:
    //        姓名:小智,年龄:3
    //        姓名:皮卡丘,年龄:6
    //        姓名:小智,年龄:12
    //        姓名:小进,年龄:12
        }
    }
    

    在使用自定义类对象进行比较处理的时候,一定要将该类中所有属性都依次进行大小关系的匹配,否则某一个或者几个属性相同的时候也会被认为是重复数据,所以TreeSet是利用了Comparable接口来确认重复数据的。

    由于TreeSet在操作过程之中需要将类中的所有属性进行比对,这样的实现难度太高了(如类有很多个属性),所以在实际的开发中应该首选HashSet子类进行存储。

课时134:分析重复元素消除

  • 把上课时的代码中,主类里的TtreeSet换成HashSet:

    public class JavaDemo{
        public static void main(String[] args) {
            Set set = new HashSet<>();
            Person pikaqiu = new Person("皮卡丘", 6);
            Person xiaozhi = new Person("小智", 12);
            Person xiaojin = new Person("小进", 12); // 年龄相同,姓名不同
            Person xiaojin2 = new Person("小进", 12); // 数据重复
            Person xiaozhi2 = new Person("小智", 3);
            set.add(pikaqiu);
            set.add(xiaozhi);
            set.add(xiaojin);
            set.add(xiaojin2);
            set.add(xiaozhi2);
            set.forEach(System.out::println);
    //        输出:
    //        姓名:小智,年龄:3
    //        姓名:小进,年龄:12
    //        姓名:小进,年龄:12
    //        姓名:小智,年龄:12
    //        姓名:皮卡丘,年龄:6
        }
    }
    

    可以发现,输出数据中仍然有重复的。

  • TreeSet类是利用了Comparable接口来实现了重复元素的判断,但HashSet判断重复元素的方式并不是利用Comparable接口完成的,它利用的是Object类中提供的方法实现的:

    • 对象编码:public int hashCode()
    • 对象比较:public boolean equals(Object obj)

    在进行重复元素判断时,首先利用hashCode()进行编码的匹配:

    • 如果该编码不存在,则表示数据不存在,证明没有重复;
    • 如果该编码存在,则进一步进行对象比较处理,如果发现重复了,则此数据是不允许保存的。

    (hashCode()生成的编码就类似身份证号码一样,是根据一定的算法进行生成的。)

  • 去除重复元素(重写equals()和hashCode()方法):

    ( idea编辑器自动equals()和hashCode()方法:Code → Generate → equals() and hashCode() )

    class Person {
        private String name;
        private int age;
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        public String toString() {
            return "姓名:" + this.name + ",年龄:" + this.age;
        }
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Person person = (Person) o;
            return age == person.age &&
                    Objects.equals(name, person.name);
        }
        @Override
        public int hashCode() {
            return Objects.hash(name, age);
        }
    }
    
  • 在Java程序中,真正的重复元素的判断处理,利用的就是equals()和hashCode()这两个方法共同作用完成的,而只有在有排序要求的情况下(如TreeSet),才会需要实现Comparable接口。

第31章:集合输出

课时135:Iterator迭代输出

  • 集合输出实际上从JDK1.8开始,就在Iterable接口中提供了一个forEach()方法可以进行输出,但它并不是传统意义上的集合输出形式,并且也很难在实际的开发中出现。对于集合操作而言,一共有四种输出形式:Iterator迭代输出(95%)、ListIterator双向迭代输出(0.1%)、Enumeration枚举输出(4.9%)、foreach输出(与Iterator相当)。

  • 通过Collection接口的继承关系可以发现,从JDK1.5开始,其多继承了一个Iterable父接口,并且在这个接口里面定义有一个iterator()操作方法,通过此方法可以获取Iterator接口对象(在JDK1.5之前,这一方法直接定义在Collection接口之中):

    • 获取Iterator接口对象:public Iterator iterator()
  • Iterator接口的操作方法:

No 方法 类型 描述
1 public boolean hasNext() 普通 判断是否还有数据
2 public E next() 普通 获取下一个数据
3 default void remove() 普通 删除数据

hasNext()和next()这两个方法,我们在之前使用Scanner类的时候就使用过,而Scanner类就是Iterator接口的子类。

《Java语言高级特性(阿里云大学)》笔记 第28~34章 关于类集(文档+思维导图)_第9张图片

  • 示例:

    import java.util.Iterator;
    import java.util.Set;
    public class JavaDemo{
        public static void main(String[] args) {
    //        Set set = Set.of("Hello", "World", "Hello", "Pikaqiu"); // 有重复元素,抛出异常
            // 抛出异常:Exception in thread "main" java.lang.IllegalArgumentException: duplicate element: Hello
            Set set = Set.of("Hello", "World", "Pikaqiu");
            Iterator iterator = set.iterator();
            while (iterator.hasNext()) {
                System.out.println(iterator.next());
            }
    //        输出:
    //        World
    //        Hello
    //        Pikaqiu
        }
    }
    
  • Iterator接口的remove()方法:

    • 如非必须,请勿使用

    • Collection接口也有remove()方法,但它会导致迭代失败:

      import java.util.Iterator;
      import java.util.Set;
      public class JavaDemo{
          public static void main(String[] args) {
              Set set = Set.of("Hello", "World", "Pikaqiu");
              Iterator iterator = set.iterator();
              while (iterator.hasNext()) {
                  String str = iterator.next();
                  if (str.equals("World")) set.remove(str); // 抛出异常
                  // 抛出异常:Exception in thread "main" java.lang.UnsupportedOperationException
                  else System.out.println(str);
              }
          }
      }
      
    • 使用Iterator接口的remove()方法:

      import java.util.HashSet;
      import java.util.Iterator;
      import java.util.Set;
      public class JavaDemo{
          public static void main(String[] args) {
              // 使用Iterator的remove()方法时,set要使用子类进行实例化
              Set set = new HashSet<>();
              set.add("Hello");
              set.add("World");
              set.add("Pikaqiu");
              Iterator iterator = set.iterator();
              while (iterator.hasNext()) {
                  String str = iterator.next();
                  if (str.equals("World")) iterator.remove();
                  else System.out.println(str);
              }
              System.out.println("————————————");
              System.out.println(set);
      //        输出:
      //        Hello
      //        Pikaqiu
      //        ————————————
      //        [Hello, Pikaqiu]
          }
      }
      
  • 面试题:请解释Collection.remove() 与 Iterator.remove() 的区别:

    • 在进行迭代输出时,如果使用Colletion.remove(),会造成并发更新的异常,导致程序删除出错;而利用Iterator接口中的remove()方法,则可以在迭代输出中进行正常的删除处理。

课时136:ListIterator双向迭代输出

  • 使用Iterator进行的迭代输出操作有一个特点:只允许由前向后输出,而如果现在需要进行双向迭代处理,那么就必须依靠Iterator的子接口:ListIterator接口来实现了。

  • ListIterator类的定义:

    public interface ListIterator
    extends Iterator
    

    《Java语言高级特性(阿里云大学)》笔记 第28~34章 关于类集(文档+思维导图)_第10张图片

    需要注意的是,如果想要获取ListIterator接口对象,Collection中并没有定义相关的处理方法,但是List子接口有,也就是说这个输出的接口是专门为List集合准备的。

  • ListIterator接口的操作方法:

    No 方法 类型 描述
    1 public boolean hasNext() 普通 判断是否还有数据
    2 public E next() 普通 获取下一个数据
    3 public boolean hasPrevious() 普通 判断是否还有前一个数据
    4 public E previous() 普通 获取前一个数据
  • 示例:

    import java.util.ArrayList;
    import java.util.List;
    import java.util.ListIterator;
    public class JavaDemo{
        public static void main(String[] args) {
            List list = new ArrayList<>();
            list.add("Hello");
            list.add("World");
            list.add("Pikaqiu");
            ListIterator listIterator = list.listIterator();
            while (listIterator.hasNext()) {
                System.out.println(listIterator.next());
            }
            System.out.println("————————————");
            while (listIterator.hasPrevious()) {
                System.out.println(listIterator.previous());
            }
    //        输出:
    //        Hello
    //        World
    //        Pikaqiu
    //        ————————————
    //        Pikaqiu
    //        World
    //        Hello
        }
    }
    

    如果想实现由后向前的遍历,那么首先要实现的是由前向后实现遍历处理。

    (也就是在调用previous()方法之前,需要有调用next()方法,否则previous()方法输出是没有内容的。)

课时137:Enumeration枚举输出

  • Enumeration是JDK1.0时就提供的输出接口,它只为Vector一个类服务。

  • Enumeration类的定义:

    public interface Enumeration
    

    《Java语言高级特性(阿里云大学)》笔记 第28~34章 关于类集(文档+思维导图)_第11张图片

  • 获取Enumeration对象:

    • 通过Vector类的方法:public Enumeration elements()
  • Enumeration接口的操作方法:

    No 方法 类型 描述
    1 public boolean hasMoreElements() 普通 判断是否还有数据
    2 public E nextElement() 普通 获取下一个数据
  • 示例:

    import java.util.Enumeration;
    import java.util.Vector;
    public class JavaDemo{
        public static void main(String[] args) {
            Vector vector = new Vector<>();
            vector.add("Hello");
            vector.add("World");
            vector.add("Pikaqiu");
            Enumeration enumeration = vector.elements();
            while (enumeration.hasMoreElements()) {
                System.out.println(enumeration.nextElement());
            }
    //        输出:
    //        Hello
    //        World
    //        Pikaqiu
        }
    }
    

课时138:foreach输出

  • 除了使用迭代接口实现输出之外,从JDK1.5开始,加强型for循环也可以实现集合的输出了。这种输出的形式与数组的输出操作形式类似:

    import java.util.ArrayList;
    import java.util.List;
    public class JavaDemo{
        public static void main(String[] args) {
            List list = new ArrayList<>();
            list.add("Hello");
            list.add("World");
            list.add("Pikaqiu");
            for (String str: list) {
                System.out.println(str);
            }
    //        输出:
    //        Hello
    //        World
    //        Pikaqiu
        }
    }
    

    这种输出最初出现时,很多人并不建议使用,因为标准的集合操作还是应该以Iterator为主,但毕竟JDK1.5都已经推出十多年了,很多语法也开始被大部分人所习惯。

第32章:Map集合

课时139:Map接口简介

  • Collection接口保存的数据:单个对象;Map接口保存的数据:二元偶对象(key=value),而存储二元偶对象的核心意义在于,需要通过key来获取对应的value。

  • Collection集合保存数据的目的:为了输出;Map集合保存数据的目的:为了进行key的查找。

  • Map接口时进行二元偶对象保存的最大父接口,接口定义如下:

    public interface Map
    
  • Map接口的核心操作方法:

    No 方法 类型 描述
    1 public V put(K key,V value) 普通 保存数据
    2 public V get(Object key) 普通 获取数据
    3 public Set> entrySet() 普通 将Map转为Set
    4 public boolean containsKey(Object key) 普通 判断key是否存在
    5 public Set keySet() 普通 将所有的key转为Set
    6 public V remove(Object key) 普通 根据指定key删除数据
  • 示例:

    import java.util.Map;
    public class JavaDemo{
        public static void main(String[] args) {
            Map map = Map.of("one", 1, "two", 2); // {one=1, two=2}
    //        Map map = Map.of("one", 1, "two", 2, "one", 11); // 抛出异常
            // 抛出异常:Exception in thread "main" java.lang.IllegalArgumentException: duplicate key: one
    //        Map map = Map.of("one", 1, "two", 2, null, 11); // 抛出异常
            // 抛出异常:Exception in thread "main" java.lang.NullPointerException
            System.out.println(map);
        }
    }
    

    在Map的of()方法中:

    • key有重复:会抛出java.lang.IllegalArgumentException的异常
    • key为null:会抛出java.lang.NullPointerException的异常

    of()方法并不是标准用法,正常的开发中需要通过子类进行接口对象的实例化。

  • Map接口的常用子类:

    • HashMap
    • HashTable
    • TreeMap
    • LinkedHashMap

课时140:HashMap子类

  • HashMap是Map接口中最为常见的一个子类,其主要特点是无序存储。

  • HashMap类的定义:

    public class HashMap
    extends AbstractMap
    implements Map, Cloneable, Serializable
    

    《Java语言高级特性(阿里云大学)》笔记 第28~34章 关于类集(文档+思维导图)_第12张图片

  • 示例:

    import java.util.HashMap;
    import java.util.Map;
    public class JavaDemo{
        public static void main(String[] args) {
            Map map = new HashMap<>();
            map.put("one", 1);
            map.put("two", 2);
            map.put("one", 11); // key重复
            map.put(null, 0); // key为null
            map.put("zero", null); // value为null
            System.out.println(map);
    //        输出:
    //        {null=0, zero=null, one=11, two=2}
        }
    }
    

    put()方法会返回该key原来的value:

    import java.util.HashMap;
    import java.util.Map;
    public class JavaDemo{
        public static void main(String[] args) {
            Map map = new HashMap<>();
            System.out.println(map.put("one", 1)); // 输出:null
            System.out.println(map.put("one", 11)); // 输出:1
        }
    }
    
    • put()方法的源码分析:

      在使用put()方法进行数据保存时,会调用一个putVal()方法,同时会将key进行hash处理(生成一个hash码);而在putVal()方法中,会提供一个Node节点类进行数据的保存,并会调用一个resize()方法进行容量的扩充。

  • 面试题:在进行HashMap的put()操作时,如何实现容量扩充?

    • 初始化容量DEFAULT_INITIAL_CAPACITY为16:
    • 在保存数据时,如果容量超过了临界值(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY = 0.75 * 16 = 12),就会进行容量的扩充:
    • 在进行扩充时,采用的也是成倍增长的形式,即:每一次都扩充2倍的容量。
  • 面试题:请解释HashMap的工作原理?

    • 在HashMap中,依然是利用Node类来进行数据存储,所以其可以使用的数据结构只有:
      • 链表(时间复杂度O(n))
      • 二叉树(时间复杂度O(logn));
    • 从JDK1.8开始,HashMap的实现出现了改变,因为要适应大数据(数据量小时,链表和红黑树的性能差别不大),所以其存储发生了变化;
    • 在使用HashMap进行数据保存时:如果保存数据的容量没有超过TREEIFY_THRESHOLD(8),那么会按链表的形式进行存储;如果超过了,则会将链表转为红黑树以实现树的平衡,并利用左旋与右旋保证数据的查询性能。

课时141:LinkedHashMap子类

  • HashMap虽然是Map集合中最为常用的子类,但其保存的数据都是无序的(有序与否对Map没有影响),如果现在希望Map集合中保存的数据为有序的,则可以使用LinkedHashMap(基于链表实现的)。

  • LinkedHashMap类的定义:

    public class LinkedHashMap
    extends HashMap
    implements Map
    

    因为是用链表保存,所以在使用LinkedHashMap类时,数据量不要很大,否则会造成时间复杂度攀升。

    《Java语言高级特性(阿里云大学)》笔记 第28~34章 关于类集(文档+思维导图)_第13张图片

  • 示例:

    import java.util.LinkedHashMap;
    import java.util.Map;
    public class JavaDemo{
        public static void main(String[] args) {
            Map map = new LinkedHashMap<>();
            map.put("one", 1);
            map.put("two", 2);
            map.put("one", 11); // key重复
            map.put(null, 0); // key为null
            map.put("zero", null); // value为null
            System.out.println(map);
    //        输出:
    //        {one=11, two=2, null=0, zero=null}
        }
    }
    

课时142:Hashtable子类

  • Hashtable类是从JDK1.0就提供了的,与Vector、Enumeration为最早的一批动态数组实现类,并且是最早的key-value接口,后来为了将其继续保留下来,让其多实现了一个Map接口。

  • Hashtable类的定义:

    public class Hashtable
    extends Dictionary
    implements Map, Cloneable, Serializable
    

    《Java语言高级特性(阿里云大学)》笔记 第28~34章 关于类集(文档+思维导图)_第14张图片

  • 示例:

    import java.util.Hashtable;
    import java.util.Map;
    public class JavaDemo{
        public static void main(String[] args) {
            Map map = new Hashtable<>();
            map.put("one", 1);
            map.put("two", 2);
            map.put("one", 11); // key重复
    //        map.put(null, 0); // key为null,抛出异常
            // 抛出异常:java.lang.NullPointerException
    //        map.put("zero", null); // value为null,抛出异常
            // 抛出异常:java.lang.NullPointerException
            System.out.println(map);
    //        输出:
    //        {two=2, one=11}
        }
    }
    

    可以发现,在Hashtable中保存数据时,设置的key或value都不允许为null,否则会出现NullPointerException异常。

  • 面试题:请解释HashMap与Hashtable的区别?

    • HashMap中的方法都是异步方法(非线程安全),而Hashtable中的方法都是同步方法(线程安全);
    • HashMap允许保存有null的数据,而Hashtable不允许保存null,否则会出现NullPointerException异常。

课时143:Map.Entry内部接口

  • 在LinkedList中,数据存储依靠的是链表,会把数据存储到Node节点中;而HashMap中的Node内部类实现了Map.Entry的内部接口:

    static class Node implements Map.Entry
    
  • Map.Entry接口的定义:

    public static interface Map.Entry
    

    《Java语言高级特性(阿里云大学)》笔记 第28~34章 关于类集(文档+思维导图)_第15张图片

    在JDK1.9之前,基本都不会考虑创建Map.Entry对象;但在JDK1.9之后,Map接口新增了entry()方法。

    • 创建Map.Entry对象:

      static  Map.Entry entry (K k, V v)
      
  • Map.Entry接口的方法:

    • 获取key: K getKey()
    • 获取value: V getValue()
  • 示例:

    import java.util.Map;
    public class JavaDemo{
        public static void main(String[] args) {
            Map.Entry entry = Map.entry("one", 1);
            System.out.println("key = " + entry.getKey());
            System.out.println("value = " + entry.getValue());
            System.out.println(entry.getClass().getName());
    //        输出:
    //        key = one
    //        value = 1
    //        java.util.KeyValueHolder
        }
    }
    
  • 在Map集合里,Map.Entry就是作为Key+Value的包装类。在数据存储时,都会把key和value包装为一个Map.Entry对象来使用。

课时144:迭代输出Map集合

  • 迭代输出Map集合有两种方式:

    • 利用Iterator
    • 利用foreach

    虽然Map集合可以进行迭代输出,但它主要的用法在于进行key的查找。

  • 利用Iterator输出Map集合(集合的标准输出):

    • 集合的标准输出:利用Iterator接口进行循环输出。但Map接口中并没有方法可以直接返回Iterator对象。

      《Java语言高级特性(阿里云大学)》笔记 第28~34章 关于类集(文档+思维导图)_第16张图片

    • Map集合中提供的方法:

      • 将Map集合转成Set集合: Set> entrySet()

        《Java语言高级特性(阿里云大学)》笔记 第28~34章 关于类集(文档+思维导图)_第17张图片

    • 利用Iterator输出Map集合的步骤:

      1. 利用Map接口中的entrySet()方法,将Map集合转为Set集合;
      2. 利用Set接口中的iterator()方法,将Set集合转为Iterator接口实例;
      3. 利用Iterator进行迭代输出,获取每一组的Map.Entry对象。
    • 示例:

      import java.util.HashMap;
      import java.util.Iterator;
      import java.util.Map;
      import java.util.Set;
      public class JavaDemo{
          public static void main(String[] args) {
              Map map = new HashMap<>();
              map.put("one", 1);
              map.put("two", 2);
              map.put("three", 3);
              // 1. 将Map集合转为Set集合
              Set> set = map.entrySet();
              // 2. 将Set集合转为Iteratpr接口实例
              Iterator> iterator = set.iterator();
              // 3. 利用Iterator进行迭代输出
              while (iterator.hasNext()) {
                  Map.Entry entry = iterator.next();
                  System.out.println(entry.getKey() + ":" + entry.getValue());
              }
      //        输出:
      //        one:1
      //        two:2
      //        three:3
          }
      }
      
  • 利用foreach输出Map集合(集合的非标准输出):

    import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;
    public class JavaDemo{
        public static void main(String[] args) {
            Map map = new HashMap<>();
            map.put("one", 1);
            map.put("two", 2);
            map.put("three", 3);
            // 1. 将Map集合转为Set集合
            Set> set = map.entrySet();
            // 2. 利用foreach进行迭代输出
            for (Map.Entry entry : set) {
                System.out.println(entry.getKey() + ":" + entry.getValue());
            }
    //        输出:
    //        one:1
    //        two:2
    //        three:3
        }
    }
    

课时145:自定义Map的key类型

  • Map集合的key使用自定义类:

    import java.util.HashMap;
    import java.util.Map;
    class Person {
        private String name;
        private int age;
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        public String toString() {
            return "姓名:" + this.name + ",年龄:" + this.age;
        }
    }
    public class JavaDemo{
        public static void main(String[] args) {
            {
                Map map = new HashMap<>();
                map.put("林弱", new Person("林强", 20));
                System.out.println(map);
                System.out.println(map.get("林弱"));
    //            输出:
    //            {林弱=姓名:林强,年龄:20}
    //            姓名:林强,年龄:20
            }
            System.out.println("————————————");
            {
                Map map = new HashMap<>();
                Person linqiang = new Person("林强", 20);
                map.put(linqiang, "林弱");
                System.out.println(map);
                System.out.println(map.get(linqiang));
    //            输出:
    //            {姓名:林强,年龄:20=林弱}
    //            林弱
            }
            System.out.println("————————————");
            {
                Map map = new HashMap<>();
                map.put(new Person("林强", 20), "林弱");
                System.out.println(map);
                System.out.println(map.get(new Person("林强", 20)));
    //            输出:
    //            {姓名:林强,年龄:20=林弱}
    //            null
            }
        }
    }
    
  • Map集合的源码分析:

    • put()方法源码:

      public V put(K key, V value) {
          return putVal(hash(key), key, value, false, true);
      }
      

      可以发现,在保存数据时,利用hash()方法生成了一个hash码,而hash()方法中又调用了Object类的hashCode()方法。

    • get()方法源码:

      public V get(Object key) {
          Node e;
          return (e = getNode(hash(key), key)) == null ? null : e.value;
      }
      

      而在获取数据时,也仍然使用到了hash码。

      继续深入getNode()方法查找数据时,可以发现其调用了key的equals()方法。

    • 所以,如Map集合中的key为自定义类时,这个类中需要重写hashCode()与equals()方法,如此,便可以根据自定义类的key查找到对应数据。

  • 重写自定义类的hashCode()与equals()方法(果断编辑器直接生成):

    import java.util.HashMap;
    import java.util.Map;
    import java.util.Objects;
    class Person {
        private String name;
        private int age;
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        public String toString() {
            return "姓名:" + this.name + ",年龄:" + this.age;
        }
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Person person = (Person) o;
            return age == person.age &&
                    Objects.equals(name, person.name);
        }
        @Override
        public int hashCode() {
            return Objects.hash(name, age);
        }
    }
    public class JavaDemo{
        public static void main(String[] args) {
            Map map = new HashMap<>();
            map.put(new Person("林强", 20), "林弱");
            System.out.println(map);
            System.out.println(map.get(new Person("林强", 20)));
    //        输出:
    //        {姓名:林强,年龄:20=林弱}
    //        林弱
        }
    }
    
  • 虽然Map集合中可以使用自定义类作为key的类型,但常用的key类型就是String、Long、Integer,应该尽量使用系统类。

  • 面试题:HashMap是如何解决在进行数据操作时出现的Hash冲突(Hash码相同)?

    • 当出现Hash冲突,为了保证程序的正常执行,会在冲突的位置上将所有Hash冲突的内容转为链表。

      《Java语言高级特性(阿里云大学)》笔记 第28~34章 关于类集(文档+思维导图)_第18张图片

第33章:集合工具类

课时146:Stack栈操作

  • 栈:先进后出的数据结构。

  • 栈的基本操作形式:

    《Java语言高级特性(阿里云大学)》笔记 第28~34章 关于类集(文档+思维导图)_第19张图片

  • 在Java中,使用Stack来描述栈的操作,Stack类的定义:

    public class Stack
    extends Vector
    

    Stack类是Vector类的子类,但它并没有使用Vector类的方法。

  • Stack类的方法:

    • 入栈:public E push(E item)
    • 出栈:public E pop()
  • 示例:

    import java.util.Stack;
    public class JavaDemo{
        public static void main(String[] args) {
            Stack stack = new Stack<>();
            stack.push("A");
            stack.push("B");
            stack.push("C");
            System.out.println(stack.pop());
            System.out.println(stack.pop());
            System.out.println(stack.pop());
            System.out.println(stack.pop());
    //        输出:
    //        C
    //        B
    //        A
    //        Exception in thread "main" java.util.EmptyStackException
        }
    }
    

课时147:Queue队列

  • 队列:先进先出的数据结构。

  • 队列的基本操作形式:

    《Java语言高级特性(阿里云大学)》笔记 第28~34章 关于类集(文档+思维导图)_第20张图片

  • 在Java中,使用Queue来描述队列的操作,Queue接口的定义:

    public interface Queue
    extends Collection
    
  • Queue接口的方法:

    • 追加数据:
      • public boolean offer(E e) → 实际上调用的也是add()方法
      • public boolean add(E e)
    • 弹出数据:public E poll()
  • 示例:

    import java.util.LinkedList;
    import java.util.PriorityQueue;
    import java.util.Queue;
    public class JavaDemo{
        public static void main(String[] args) {
            // 方式一:使用LinkedList子类来实现Queue接口
    //        Queue queue = new LinkedList<>();
            // 方式二:使用PriorityQueue优先级队列子类来实现Queue接口
            Queue queue = new PriorityQueue<>();
            queue.offer("A");
            queue.offer("B");
            queue.offer("C");
            System.out.println(queue.poll());
            System.out.println(queue.poll());
            System.out.println(queue.poll());
            System.out.println(queue.poll());
    //        输出:
    //        A
    //        B
    //        C
    //        null
        }
    }
    

课时148:Properties属性操作

  • 国际化程序中使用的资源文件(*.properties),其存储结构与Map集合类似,但其保存的只能是字符串。所以为了方便属性的描述,java.util包中提供了Properties类。

  • Properties类的定义:

    public class Properties
    extends Hashtable
    
  • Properties类的方法:

    No 方法 类型 描述
    1 public Object setProperty(String key, String value) 普通 设置属性
    2 public String getProperty(String key) 普通 获取属性,key不存在时返回null
    3 public String getProperty(String key, String defaultValue) 普通 获取属性,key不存在时返回默认值
    4 public void store(OutputStream out, String comments) throws IOException 普通 通过输出流输出属性内容
    5 public void load(InputStream inStream) throws IOException 普通 通过输入流读取属性内容
  • 示例:

    import java.util.Properties;
    public class JavaDemo{
        public static void main(String[] args) {
            Properties properties = new Properties();
            properties.setProperty("name", "pikaqiu");
            System.out.println(properties.getProperty("name"));
            System.out.println(properties.getProperty("age"));
            System.out.println(properties.getProperty("age", "12"));
    //        输出:
    //        pikaqiu
    //        null
    //        12
        }
    }
    

    Properties类可以像Map集合那样进行内容的设置和获取,但它只能操作String类型。

  • Properties类最重要的功能:可以通过输出流输出属性、通过输入流读取属性:

    • 保存文件:

      import java.io.File;
      import java.io.FileOutputStream;
      import java.util.Properties;
      public class JavaDemo{
          public static void main(String[] args) throws Exception {
              Properties properties = new Properties();
              properties.setProperty("name", "pikaqiu");
              properties.setProperty("age", "16");
              String fileName = "D:" + File.separator + "test" + File.separator + "info.properties";
              properties.store(new FileOutputStream(new File(fileName)), "pikaqiu-info");
          }
      }
      

      info.properties文件内容:

      #pikaqiu-info
      #Sun Feb 14 22:11:48 CST 2021
      name=pikaqiu
      age=16
      
    • 读取文件:

      import java.io.File;
      import java.io.FileInputStream;
      import java.util.Properties;
      public class JavaDemo{
          public static void main(String[] args) throws Exception {
              Properties properties = new Properties();
              String fileName = "D:" + File.separator + "test" + File.separator + "info.properties";
              properties.load(new FileInputStream(new File(fileName)));
              System.out.println(properties.getProperty("name"));
              System.out.println(properties.getProperty("age"));
      //        输出:
      //        pikaqiu
      //        16
          }
      }
      

      ResourceBundle一般读取信息资源,Properties一般读取配置资源,主要在程序初始化准备时使用。

课时149:Collections工具类

  • Collections:是Java提供的操作集合数据的工具类,也就是利用它可以实现各个集合的操作。

    《Java语言高级特性(阿里云大学)》笔记 第28~34章 关于类集(文档+思维导图)_第21张图片

  • 示例:

    • 使用Collections操作List集合:

      import java.util.ArrayList;
      import java.util.Collections;
      import java.util.List;
      public class JavaDemo{
          public static void main(String[] args) {
              List list = new ArrayList<>();
              Collections.addAll(list, "Hello", "World", "Pikaqiu");
              System.out.println(list); // 输出:[Hello, World, Pikaqiu]
              Collections.reverse(list); // 实现反转
              System.out.println(list); // 输出:[Pikaqiu, World, Hello]
              Collections.sort(list); // 实现排序
              System.out.println(list); // 输出:[Hello, Pikaqiu, World]
              // binarySearch():二分查找,查找前需要先sort
              System.out.println(Collections.binarySearch(list, "World")); // 输出:2
          }
      }
      
  • 面试题:请解释Collection与Collections的区别?

    • Collection:是集合接口;
    • Collections:是操作集合的工具类。

第34章:Stream数据流

课时150:Stream基本操作

  • 从JDK1.8开始,进入到了大数据时代,所以在类集里也支持有数据的流式分析处理操作,为此就专门提供了Stream的接口,同时在Collection接口里也提供有为此接口实例化的方法:

    • 获取并行的Stream接口对象:default Stream parallelStream()
    • 获取Stream接口对象:default Stream stream()
  • Stream主要功能:

    • 进行数据的分析处理
    • 主要针对集合中的数据进行分析处理
  • Stream接口的方法:

    • 分页:
      • 设置获取的最大数据量:public Stream limit(long maxSize)
      • 跳过指定数据量进行读取:public Stream skip(long n)
  • 示例:

    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.stream.Collectors;
    import java.util.stream.Stream;
    public class JavaDemo{
        public static void main(String[] args) {
            List list = new ArrayList<>();
            Collections.addAll(list, "Java", "JavaScript", "Ruby", "PHP", "Python", "GO", "Json", "JSP");
            Stream stream = list.stream(); // 获取Stream接口对象
    //        System.out.println(stream.count()); // 获取个数,返回的为long型
            { // 通过函数式编程进行流式处理
    //            System.out.println(stream.filter(
    //                    (ele)->ele.toLowerCase().contains("j")
    //            ).count()); // 输出:4
            }
            { // 数据采集
    //            List result = stream.filter(
    //                    (ele)->ele.toLowerCase().contains("j")
    //            ).collect(Collectors.toList());
    //            System.out.println(result); // 输出:[Java, JavaScript, Json, JSP]
            }
            { // 数据分页,在采集之前分页
                List result = stream.filter(
                        (ele)->ele.toLowerCase().contains("j")
                ).skip(2).limit(2).collect(Collectors.toList());
                System.out.println(result); // 输出:[Json, JSP]
            }
        }
    }
    

课时151:MapReduce基础模型

  • MapReduce基础模型:

    • Map:处理
    • Reduce:分析

    在数据分析之前,必须要对数据进行合理的处理,然后才可以做统计分析操作。

  • 示例:

    import java.util.ArrayList;
    import java.util.DoubleSummaryStatistics;
    import java.util.List;
    class Order { // 订单类
        private String name; // 商品名称
        private double price; // 商品价格
        private int amount; // 商品数量
        public Order(String name, double price, int amount) {
            this.name = name;
            this.price = price;
            this.amount = amount;
        }
        public String getName() {
            return this.name;
        }
        public double getPrice() {
            return this.price;
        }
        public int getAmount() {
            return this.amount;
        }
    }
    public class JavaDemo{
        public static void main(String[] args) {
            List listOrder = new ArrayList<>();
            listOrder.add(new Order("小强娃娃", 9.9, 10));
            listOrder.add(new Order("林弱充气娃娃", 2987.9, 3));
            listOrder.add(new Order("不强牌笔记本电脑", 8987, 8));
            listOrder.add(new Order("弱强茶杯", 2.9, 800));
            listOrder.add(new Order("阿强牌煎饼", 0.9, 138));
            DoubleSummaryStatistics stat = listOrder.stream().filter(
                    (ele)->ele.getName().contains("强")
            ).mapToDouble(
                    (orderObject)->orderObject.getPrice() * orderObject.getAmount()
            ).summaryStatistics();
            System.out.println("购买数量:" + stat.getCount());
            System.out.println("购买总价:" + stat.getSum());
            System.out.println("平均花费:" + stat.getAverage());
            System.out.println("最高花费:" + stat.getMax());
            System.out.println("最低花费:" + stat.getMin());
    //        输出:
    //        购买数量:4
    //        购买总价:74439.2
    //        平均花费:18609.8
    //        最高花费:71896.0
    //        最低花费:99.0
            System.out.println(stat);
    //        输出:
    //        DoubleSummaryStatistics{count=4, sum=74439.200000, min=99.000000, average=18609.800000, max=71896.000000}
        }
    }
    
  • 这些分析操作只是JDK本身提供的支持,但实际中并不这样进行操作,因为如果所有的数据都保存在内存中,而当数据量很大时,就崩掉了。

————————————————————————————————————

加餐:

加餐一:Collection接口的父子类关系图:

《Java语言高级特性(阿里云大学)》笔记 第28~34章 关于类集(文档+思维导图)_第22张图片

加餐二:Map接口的父子类关系图:

《Java语言高级特性(阿里云大学)》笔记 第28~34章 关于类集(文档+思维导图)_第23张图片

————————————————————————————————————

思维导图

你可能感兴趣的:(java,阿里云大学,java)