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

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

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

    Collection接口.PNG

第29章:List集合

课时125:List接口简介

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

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

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

    • ArrayList
    • LinkedList
    • Vector

    List子接口.PNG

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

    import java.util.List;
    public class JavaDemo{
        public static void main(String[] args) {
            List<String> 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<? super T> action)
    

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

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

课时126:ArrayList子类

  • ArrayList类的定义:

    public class ArrayList<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, Serializable
    

    ArrayList子类.PNG

  • 示例:

    import java.util.ArrayList;
    import java.util.List;
    public class JavaDemo{
        public static void main(String[] args) {
            List<String> 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<Person> 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<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, Serializable
    

    LinkedList子类.PNG

  • 示例:

    import java.util.LinkedList;
    import java.util.List;
    public class JavaDemo{
        public static void main(String[] args) {
            List<String> 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<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, Serializable
    

    Vector子类.PNG

  • 示例:

    import java.util.List;
    import java.util.Vector;
    public class JavaDemo{
        public static void main(String[] args) {
            List<String> 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<E> extends Collection<E>
    

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

    Set子接口.PNG

  • 示例:

    import java.util.Set;
    public class JavaDemo{
        public static void main(String[] args) {
            Set<String> 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<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, Serializable
    

    HashSet子类.PNG

  • 示例:

    import java.util.HashSet;
    import java.util.Set;
    public class JavaDemo{
        public static void main(String[] args) {
            Set<String> 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<E>
    extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, Serializable
    

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

    TreeSet子类.PNG

  • 示例:

    import java.util.Set;
    import java.util.TreeSet;
    public class JavaDemo{
        public static void main(String[] args) {
            Set<String> 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<Person> 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<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 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<Person> 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<Person> 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方法类型描述
1public boolean hasNext()普通判断是否还有数据
2public E next()普通获取下一个数据
3default void remove()普通删除数据

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

Iterator接口.png

  • 示例:

    import java.util.Iterator;
    import java.util.Set;
    public class JavaDemo{
        public static void main(String[] args) {
    //        Set<String> set = Set.of("Hello", "World", "Hello", "Pikaqiu"); // 有重复元素,抛出异常
            // 抛出异常:Exception in thread "main" java.lang.IllegalArgumentException: duplicate element: Hello
            Set<String> set = Set.of("Hello", "World", "Pikaqiu");
            Iterator<String> 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<String> set = Set.of("Hello", "World", "Pikaqiu");
              Iterator<String> 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<String> set = new HashSet<>();
              set.add("Hello");
              set.add("World");
              set.add("Pikaqiu");
              Iterator<String> 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<E>
    extends Iterator<E>
    

    ListIterator接口.png

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

  • ListIterator接口的操作方法:

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

    import java.util.ArrayList;
    import java.util.List;
    import java.util.ListIterator;
    public class JavaDemo{
        public static void main(String[] args) {
            List<String> list = new ArrayList<>();
            list.add("Hello");
            list.add("World");
            list.add("Pikaqiu");
            ListIterator<String> 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<E>
    

    Enumeration.PNG

  • 获取Enumeration对象:

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

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

    import java.util.Enumeration;
    import java.util.Vector;
    public class JavaDemo{
        public static void main(String[] args) {
            Vector<String> vector = new Vector<>();
            vector.add("Hello");
            vector.add("World");
            vector.add("Pikaqiu");
            Enumeration<String> 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<String> 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<K,V>
    
  • Map接口的核心操作方法:

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

    import java.util.Map;
    public class JavaDemo{
        public static void main(String[] args) {
            Map<String, Integer> map = Map.of("one", 1, "two", 2); // {one=1, two=2}
    //        Map<String, Integer> map = Map.of("one", 1, "two", 2, "one", 11); // 抛出异常
            // 抛出异常:Exception in thread "main" java.lang.IllegalArgumentException: duplicate key: one
    //        Map<String, Integer> 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<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable
    

    HashMap子类.PNG

  • 示例:

    import java.util.HashMap;
    import java.util.Map;
    public class JavaDemo{
        public static void main(String[] args) {
            Map<String, Integer> 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<String, Integer> 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<K,V>
    extends HashMap<K,V>
    implements Map<K,V>
    

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

    LinkedHashMap子类.PNG

  • 示例:

    import java.util.LinkedHashMap;
    import java.util.Map;
    public class JavaDemo{
        public static void main(String[] args) {
            Map<String, Integer> 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<K,V>
    extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, Serializable
    

    HashTable子类.PNG

  • 示例:

    import java.util.Hashtable;
    import java.util.Map;
    public class JavaDemo{
        public static void main(String[] args) {
            Map<String, Integer> 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<K,V> implements Map.Entry<K,V>
    
  • Map.Entry接口的定义:

    public static interface Map.Entry<K,V>
    

    Map.Entry内部接口.PNG

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

    • 创建Map.Entry对象:

      static <K,V> Map.Entry<K,V> 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<String, Integer> 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对象。

      Collection与Map数据存储.PNG

    • Map集合中提供的方法:

      • 将Map集合转成Set集合: Set<Map.Entry<K,V>> entrySet()

        Map集合转Set集合.PNG

    • 利用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<String, Integer> map = new HashMap<>();
              map.put("one", 1);
              map.put("two", 2);
              map.put("three", 3);
              // 1. 将Map集合转为Set集合
              Set<Map.Entry<String, Integer>> set = map.entrySet();
              // 2. 将Set集合转为Iteratpr接口实例
              Iterator<Map.Entry<String, Integer>> iterator = set.iterator();
              // 3. 利用Iterator进行迭代输出
              while (iterator.hasNext()) {
                  Map.Entry<String, Integer> 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<String, Integer> map = new HashMap<>();
            map.put("one", 1);
            map.put("two", 2);
            map.put("three", 3);
            // 1. 将Map集合转为Set集合
            Set<Map.Entry<String, Integer>> set = map.entrySet();
            // 2. 利用foreach进行迭代输出
            for (Map.Entry<String, Integer> 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<String, Person> map = new HashMap<>();
                map.put("林弱", new Person("林强", 20));
                System.out.println(map);
                System.out.println(map.get("林弱"));
    //            输出:
    //            {林弱=姓名:林强,年龄:20}
    //            姓名:林强,年龄:20
            }
            System.out.println("————————————");
            {
                Map<Person, String> 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<Person, String> 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<K,V> 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<Person, String> 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冲突的内容转为链表。

      Hash冲突的解决方法.PNG

第33章:集合工具类

课时146:Stack栈操作

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

  • 栈的基本操作形式:

    Stack栈.PNG

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

    public class Stack<E>
    extends Vector<E>
    

    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<String> 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队列

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

  • 队列的基本操作形式:

    Queue队列.PNG

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

    public interface Queue<E>
    extends Collection<E>
    
  • 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<String> queue = new LinkedList<>();
            // 方式二:使用PriorityQueue优先级队列子类来实现Queue接口
            Queue<String> 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<Object,Object>
    
  • Properties类的方法:

    No方法类型描述
    1public Object setProperty(String key, String value)普通设置属性
    2public String getProperty(String key)普通获取属性,key不存在时返回null
    3public String getProperty(String key, String defaultValue)普通获取属性,key不存在时返回默认值
    4public void store(OutputStream out, String comments) throws IOException普通通过输出流输出属性内容
    5public 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提供的操作集合数据的工具类,也就是利用它可以实现各个集合的操作。

    Collections工具类.PNG

  • 示例:

    • 使用Collections操作List集合:

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

Collection关系图

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

Map关系图

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

思维导图

《Java语言高级特性》笔记 第28~35章类集.png

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值