JavaSE--集合+Stream流总结

引言

  • 集合的学习,首先需要知道怎么使用集合,往里面存储数据,会集合常用的若干方法,主要是增删改查插遍历,最后需要知道各种集合的特点以及底层源码是如何实现的。

概述:

JavaSE--集合+Stream流总结_第1张图片

Java中集合主要分为CollectionMap两种体系:

1.Collection接口 : 储存单列数据;
2.Map接口: 储存双列数据,保存键值对key-value类数据,有点像数学中的函数;

聊到Collection接口,我们就来看看其常用方法:

  1. boolean add(E e):添加一个元素

  2. void clear():清空集合中所有的元素

  3. boolean remove(E e):移除集合中指定的元素

  4. boolean contains(E e):检查集合中是否包含指定的对象

  5. boolean isEmpty():判断集合是否为空

  6. void size():判断集合中元素的个数

  7. Object[] toArray():把集合元素存储到数组中

  8. boolean equals(E e) : 判断两个集合是否相等
    JavaSE--集合+Stream流总结_第2张图片
    在这里插入图片描述
    在这里插入图片描述

关于asList()方法,需要注意:
JavaSE--集合+Stream流总结_第3张图片

Collection接口含有两个子接口:

  • List接口 : 储存的元素都是有序且可以重复的,相当于“动态数组”;
  • Set接口: 储存的元素是无序且不能重复;

List接口中含有三个实现类:

  • ArrayList: List的主要实现类,线程不安全,效率高,底层使用Object数组进行存储(没用泛型前);
  • LinkedList: 底层使用双向链表,频繁的插入删除操作时此优于ArrayList
  • Vector: List的古老实现类,线程安全,效率低,底层使用Object数组。

对于List接口,我们看一下其常用方法:

JavaSE--集合+Stream流总结_第4张图片

  • 其实只要记得增删改查插和迭代的方法即可;

并且需要注意:
JavaSE--集合+Stream流总结_第5张图片

关于ArrayList的源码分析:

jdk7中:

  • 我们在ArrayList a = new ArrayList()时,其实为底层创建了长度为10的Object数组,每次add相当于将添加的元素变成对象后添加进数组里,一旦数组不够用了,会扩容,默认扩容为扩容为原先的1.5倍,且将原先的数据再复制到新数组里。

jdk8中:

  • ArrayList a = new ArrayList()时,没有真正的在底层new出数组,只是先声明,等到第一次add时再真正的创建数组,其他与上述相同;

jdk7中ArrayList对象的创建相当于单例模式中的饿汉式,8中相当于单例模式中的懒汉式,延迟数组创建节省内存。

关于LinkedList的源码分析:

JavaSE--集合+Stream流总结_第6张图片

关于Vector的源码分析:

在这里插入图片描述

Map接口下方是两个实现类和一个子接口:

  1. HashMap: 作为Map接口的主要实现类,线程不安全,效率高,可以存储为null的键值对key-value,jdk7以前底层使用数组+链表jdk8使用数组+链表+红黑树存储;
  • 其子类为: LinkedHashMap:
    特点是保证可以按照元素添加的顺序进行遍历,原因是给每次添加的元素再加上两个引用,一个指向其前一个元素,一个指向后一个元素,频繁的遍历操作时此优于HashMap
  1. Hashtable: 古老实现类,线程安全但效率低,不能存储为null的键值对key-value;
  2. SortedMap: 其实现类为: TreeMap
    保证按照添加的key-value进行排序,排序的依照是根据key的自然排序或定制排序,底层使用红黑树存储;

关于Map结构的理解:
在这里插入图片描述

HashMap的底层实现原理简述:

jdk7时:

  • HashMap a = new HashMap();实例化时底层创建了长度16的Entry数组。每次执行map.put(key1,value1)操作时,会先调用key1的hashCode方法计算哈希值,此哈希值经过某种算法可以得到此值在Entry数组中的具体存放位置:
  1. 如果此位置为空,那么直接存放,结束;
  2. 如果不为空,需要与此位置上的所有元素进行哈希值是否相等的判断:
  • 如果哈希值都不相等,则直接添加进去即可,按照“七上八下”的思想;
  • 如果相等,则进行equals方法的判断,不相等则按照“七上八下”的思想添加进去即可,相等则用新的value替换旧value。

在不断的添加过程中,会涉及到扩容问题,当超出临界值(且要存放的位置非空)时,扩容,默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来;

jdk8中:

jdk8相较于jdk7在底层实现方面的不同:

  1. new HashMap():底层没有创建一个长度为16的数组
  2. jdk8底层的数组是:Node[],而非Entry[]
  3. 首次调用put()方法时,底层创建长度为16的数组
  4. jdk7底层结构只有:数组+链表jdk8中底层结构:数组+链表+红黑树
    当数组的某一个索引位置上的元素以链表形式存在的数据个数>8且当前数组的长度>64时,此时此索引位置上的所有数据改为使用红黑树存储。

七上八下

JavaSE--集合+Stream流总结_第7张图片
JavaSE--集合+Stream流总结_第8张图片
JavaSE--集合+Stream流总结_第9张图片

LinkedHashMap的底层实现原理简述:

JavaSE--集合+Stream流总结_第10张图片

Map接口中的常用方法:

JavaSE--集合+Stream流总结_第11张图片

总结:

JavaSE--集合+Stream流总结_第12张图片

Set接口中的实现类:

  1. HashSet: Set接口的主要实现类,线程不安全,可以存放null;
  2. LinkedHashSet:HashSet的子类,遍历数组可以按照添加的顺序遍历,频繁的遍历时优于HashSet
  3. TreeSet:;可以按照添加的对象的某种属性对元素进行排序;

Set接口中没有定义新方法,其使用的都是Collection的方法。

HashSet底层源码分析:

JavaSE--集合+Stream流总结_第13张图片
HashSet底层使用数组+链表存储。
JavaSE--集合+Stream流总结_第14张图片

要点:

  • Set中添加的数据,其所在类一定要重写hashCode方法和equals方法;
  • 重写的hashCodeequals方法要保持一致性,相同的对象应该具有相同的散列码;

LinkedHashSet底层源码分析:

JavaSE--集合+Stream流总结_第15张图片

TreeSet底层源码分析:

JavaSE--集合+Stream流总结_第16张图片
JavaSE--集合+Stream流总结_第17张图片
在这里插入图片描述

关于HashSet需要注意:

JavaSE--集合+Stream流总结_第18张图片

最后介绍一下Collections工具类的一些用于操作集合的静态方法:

JavaSE--集合+Stream流总结_第19张图片
JavaSE--集合+Stream流总结_第20张图片

需要注意:
JavaSE--集合+Stream流总结_第21张图片

Stream

我们也可以使用强大的Stream来对集合进行操作,通过Stream流对集合进行操作更加的容易。
JavaSE--集合+Stream流总结_第22张图片

首先明白Stream的操作流程:

JavaSE--集合+Stream流总结_第23张图片

创建Stream

我们有四种方式创建Stream流:

  1. 通过集合创建:
		List<Integer> list = new ArrayList<>();
        //集合创建
        Stream<Integer> stream = list.stream();
        //创建一个顺序流
        Stream<Integer> integerStream = list.parallelStream();
        //创建一个并行流
  1. 通过数组:
int[] a = new int[]{
     1,2,3};
IntStream stream1 = Arrays.stream(a);
  1. 通过Streamof方法
Stream<Integer> integerStream1 = Stream.of(1, 2, 3);
  1. 创建无限流

JavaSE--集合+Stream流总结_第24张图片

Stream的中间操作

我们先创建一个Employee类用来演示,并填充一下集合创建Stream:

   List<Employee> list = new ArrayList<>();
    //集合创建
    Employee jack = new Employee("jack", 16, 2000);
    Employee mike = new Employee("mike", 17, 4666);
    Employee aili = new Employee("aili", 22, 3000);
    Employee sherry = new Employee("sherry", 31, 10000);
    list.add(jack);
    list.add(mike);
    list.add(aili);
    list.add(sherry);
    Stream<Employee> stream = list.stream();
    //创建一个顺序流

1. 筛选与切片

  • 接收流(从流中排除某些元素):
stream.filter(e -> e.salary>6000).forEach(System.out::println);

结果:

Employee{
     name='sherry', age=31, salary=10000}
  • 截断流(使元素不超过指定数量):
stream.limit(2).forEach(System.out::println);

结果:

Employee{
     name='jack', age=16, salary=2000}
Employee{
     name='mike', age=17, salary=4666}
  • 跳过流(跳过n个元素,返回去掉前n个元素后的流,若元素不足n个返回空流):
stream.skip(2).forEach(System.out::println);

结果:

Employee{
     name='aili', age=22, salary=3000}
Employee{
     name='sherry', age=31, salary=10000}
  • 筛选流(通过流内元素的equals方法和hashCode方法去除重复元素):
list.add(new Employee("sherry", 31, 10000));
Stream<Employee> stream = list.stream();
stream.distinct().forEach(System.out::println);

结果:
Employee{
     name='jack', age=16, salary=2000}
Employee{
     name='mike', age=17, salary=4666}
Employee{
     name='aili', age=22, salary=3000}
Employee{
     name='sherry', age=31, salary=10000}

2. 映射

  • map(Function f)—接收一个函数作为参数,将元素转换为其他形式或提取信息,该函数会应用到每一个元素上,使得其映射为一个新元素。
stream.map(e -> e.getName()).forEach(System.out::println);

结果:
jack
mike
aili
sherry
  • flatMap(Function f)—接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

3. 排序

  • sorted()----产生一个新流,按照自然排序

  • sorted(Comparator com)产生一个新流,按照比较器顺序排序(定制排序);

Stream的终止操作

1. 匹配与查找

 //        aLLMatch(Predicate p)——检查是否匹配所有元素。
//        练习:是否所有的员工的年龄都大于18
        boolean allMatch = list.stream().allMatch(e -> e.getAge() > 18);System.out.println(allMatch);
//        anyMatch(Predicate p)—检查是否至少匹配一个元素。
//        练习:是否存在员工的工资大于1eeee
        boolean anyMatch = list.stream().anyMatch(e -> e.getSalary() >10000) ;System.out.println(anyMatch);

//        noneMatch(Predicate p)—-检查是否没有匹配的元素。
//        练习:是否存在员工姓“she”
        boolean noneMatch = list.stream().noneMatch(e -> e.getName( ).startsWith("she"));
//        findFirst——返回第一个元素
        Optional<Employee> employee = list.stream().findFirst();
        System.out.println(employee);
//        findAny—返回当前流中的任意元素
        Optional<Employee> employee1 = list.stream().findAny();
        System.out.println(employee1);
  • 需要主要的是每次执行终止操作后此流将不可再被使用,因此每次都需要重写造一次Stream流。

2. 归约

//            reduce(T identity,BinaryOperator)---可以将流中元素反复结合起来,得到一个值,返回T
//            练习1:计算1-10的自然数的和
            List<Integer> list1 = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
            Integer sum = list1.stream( ).reduce( 0 ,(a,b) -> Integer.sum(a,b));
            System.out.println(sum);

//            reduce(BinaryOperator)一可以将流中元素反复结合起来,得到一个值。返回Optional
//            练习2:计算公司所有员工工资的总和
//            这里的list是原先用来装Employee的集合
            Stream<Integer> integerStream = list.stream().map(e -> e.getSalary());
            Optional<Integer> sumMoney = integerStream.reduce((a, b) -> Integer.sum(a,b) );
            System.out.println(sumMoney);

3. 收集

collect(Collector c)

  • 将流转换为其他形式。接收一个Collector接口的实现,用于给Stream`中元素做汇总的方法。

例:

List<Employee> collect = list.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());

即将原先的集合变成Stream流之后经过筛选后再返回原先一样类型的集合,重新收集
  • 即我们使用collect方法只是将流转换一下形式,接着再使用Collectors类中的静态方法对元素或元素的属性进行选择性收集。

Collectors实用类方法:

JavaSE--集合+Stream流总结_第25张图片

你可能感兴趣的:(Java学习,#,集合,#,强大的Stream,java,集合)