zoukankan      html  css  js  c++  java
  • Java集合笔记

    Java集合类

    概述:

    ​ 在编程时,常常需要集中存放多个数据,在使用数组存放数据的话,因数组长度定义后就无法改变,也无法保存具有映射关系的的数据,所以数组就显得无能为力了,为了保存数量不确定的数据,以及保存具有映射关系的数据,Java提供了集合类,所有集合类都位于java.util包下,在java5之后还在java.util.concurrent包下提供了支持多线程的集合。

    集合和数组

    • 数组(存储一组元素最快的数据结构):

      • 长度确定,一旦被创建,大小不可改变,下标区间[0,length)
      • 同一数组元素必须相同,且有序,可以为任意类型,包括基本数据类型和引用数据类型(存储引用变量)
      • 数组属于引用类型,数组也是对象
    • 集合:

      • 集合的顶级接口分为Collection接口和Map接口

      • Collection接口下面派生出Set接口、List接口、Queue接口

        • Set下有HashSet、LinkedHashSet、TreeSet
        • List下有ArrayList、Vector、LinkedList
        • Queue下有ArrayDeque
      • Map下有HashTable、LinkedHashMap、HashMap、TreeMap

    Collection接口

    List接口:

    特点:元素有序、可重复。集合中每个元素都有其对应的索引,默认按元素添加顺序设置索引。

    • ArrayList

      优点:查询速度块。

      缺点:增删改效率低。

      底层数据结构:数组。

      扩容机制:1.5倍扩容。

    • LinkedList

      优点:增删改速度快。

      缺点:查询效率低。

      底层数据结构:双向链表。

      扩容机制:链表不需要扩容。

    Set接口:

    特点:程序以可依次把多个对象丢进Set集合中,而Set通常无法记住添加顺序,若向Set中添加相同的元素,则add()会返回false。

    • HashSet

      优点:HashSet按照Hash算法存储元素,因此具有很好的存取和查找性能。

      底层数据结构:哈希表。

      其它:

      • 不能保证元素的排列顺序。
      • 集合元素可以为null。
      • 向HashSet添加元素时,HashSet会调用对象的HashCode,然后根据其HashCode来决定对象的位置。
      • 如果两个对象通过equals()方法返回true,但HashCode返回值不想等,元素依然可以添加成功。
      • HashSet判断两个对象相等的依据是equals()比较相等hashCode()返回值相同
        • 如果equals()返回true,hashCode()返回false,元素会添加成功,但与set规则相冲突。
        • 如果equals()返回false,hashCode()返回true,元素会添加成功,但在同一位置用链式结构存储,性能会下降。
    • LinkedHashSet

      优点:可以保证数据的录入顺序。

      底层数据结构:哈希表和链表。

      其它:

      • 当遍历LinkedHashSet集合里的元素时,会按元素的添加顺序访问集合元素。
      • LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet。
    • TreeSet

      优点:可保证元素是排序状态。

      底层数据结构:红黑树。

      其它:

      • TreeSet通过自然排序定制排序来保证元素的排序状态。
      • 自然排序:当对象添加至TreeSet之后,TreeSet会调用对象的comparaTo方法来判断对象间的大小关系,并按升序排序。
        • 问题:
          1. 当添加至TreeSet集合中的对象没有实现Comparable接口的情况下,第一个添加的元素不会报异常,第二个元素就会引发ClassCastException异常。(调用comparaTo方法对比时,无法对比,其它类似情况也会出现此异常)
          2. TreeSet判断对象是否相等的唯一条件是comparaTo方法是否返回为0,如果添加多次同一个重写了comparaTo方法,并一直返回非0的对象的话,添加也是可以成功的,但本质是同意个对象,修改任何一个数据,也会全部修改。
          3. 当修改了TreeSet集合中的元素后,TreeSet也不会重新进行排序。
        • 总之:想要TreeSet正常运作的话,TreeSet只能添加同一中类型的对象。
      • 定制排序:需要Comparator接口的帮助,由Comparator接口负责元素集合的排序逻辑。

      因为每次添加数据都会进行排序处理,所以TreeSet相对于HashSet和LinkedHashSet要慢得多!

    Map接口

    • Map接口有三个比较重要的实现类,分别为HashMap、TreeMap、HashTable。
    • Map用于保存具有映射关系的数据,因此Map集合中保存着两组值,一组key一组value,key和value都可以是任何引用类型的数据,key不允许重复。
    • TreeMap是有序的,HashMap和HashTable是无序的。
    • Hashtable是线程安全的,HashMap是线程不安全的。
    • HashMap效率高,Hashtable效率低。(如果没有兼容性需求,建议使用HashMap)
    • Hashtable不允许null值,HashMap允许null值(key、value都可以)
    • 在java8中对于HashMap进行了优化,之前HashMap底层结构为数组加链表,当同一位置有两个对象时就会产生Hash碰撞,当Hash碰撞多了之后,会严重影响效率。在Java8之后,当链表长度大于8且Map元素大于64后,底层数据结构会变为数组加红黑树。根据对比来确定位置的情况少了很多,除了添加操作其余效率要高很多。

    集合工具类

    Iterator遍历

    Iterator接口提供了以下方法操作集合

    • hasNext():是否存在下一个
    • next():返回集合下一个元素
    • remove():删除上一个next返回的元素
    • forEachRemaining():使用这和方法可用Lambda表达式遍历元素。
    List<String> list = Arrays.asList(strings);  //数组转换集合
            Iterator<String> iterator = list.iterator();  //调用iterator
            while (iterator.hasNext()){   //是否存在下一个元素(用于终止循环)
                String next = iterator.next();  //拿出元素
                System.out.println(next);
            }
    //--------------------------------------------------------------------
    List<String> ls = Arrays.asList(strings);  //数组转换集合
            Iterator<String> iterator = ls.iterator();  //调用Iterator方法
            iterator.forEachRemaining(s -> {  //调用forEachRemaining使用Lambda表达式
                System.out.println(s);
            });
    

    Lambda遍历

    Iterable接口是Collection的父接口,为函数式接口,默认方法为forEach(),所以Collection子接口的List和Set都可以使用ForEach()方法+Lambda表达式。

     List<String> ls = Arrays.asList(strings);  //数组转换集合
            ls.forEach(l->{  //Lambda表达式遍历l
                System.out.println(l);
            });
    

    Stream流

    Java8中的Stream流与inputStream、outputStream是完全不同的概念,stream适用于对集合迭代器的增强,使之能够更高效率的完成聚合操作(过滤、排序、统计分组)或大批量数据操作,stream流与lambda表达式结合后编码效率大大提高,可读性会更强。火箭代码----->火车代码

    集合讲的是数据,流讲的是计算

    1. 首先把数据源(数组、集合...)转换为流。
    2. 对流进行流水线式的中间操作。
    3. 操作后将产生全新的流,和原来的数据源没有任何关系。

    创建Stream流

    	//创建Stream
        public void test1(){
            //1.通过Collection系列集合提供的stream()(串行流)或parallelStream()(并行流)
            List<String> list = new ArrayList<>();
            Stream<String> stream1 = list.stream();
    
            //2.通过Arrays中的静态方法stream获取数组流
            String[] strings = new String[10];
            Stream<String> stream = Arrays.stream(strings);
    
            //3.通过Stream类中的静态方法of()方法
            Stream<String> aa = Stream.of("aa", "bb", "cc");
    
            //4.创建无限流
            //迭代                            //这是无限流
            Stream<Integer> iterate = Stream.iterate(0, (x) -> x + 2);
                  //这是中间操作  //这是截止操作
            iterate.limit(10).forEach(System.out::println);
    
            //生成
            Stream.generate(()->(Math.random()))
                    .limit(20)
                    .forEach(System.out::println);
        }
    

    中间操作

    多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何处理!而在终止操作时一次性全部处理,称为“惰性求值”

    Stream会对数据进行遍历操作,这是由Stream API完成的,称为内部迭代

    筛选
    • filter:接收lambda,从流中排除某些元素。

      public void test(){
              //根据lambda表达式过滤
                  //中间操作
              emps.stream().filter((e) -> e.getAge() > 30)
                  //终止操作,没有终止操作,中间操作不会执行!
                  .forEach(emp -> System.out.println(emp));
          }
      
    • limit:截断流,使其中元素不超过给定数量。当找到给定次数的数据后,就不再执行。

       public void test2(){
           //使用limit(3)后会取出三个元素
              emps.stream().limit(3).forEach((e)-> System.out.println(e));
          }
      
    • skip:跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流。

       public void test3(){
              emps.stream().skip(2).forEach((e)-> System.out.println(e));
          }
      
    • distinct:筛选去重,通过流所生成的hashCode()equals()去除重复元素。

      public void test4(){
              emps.stream().distinct().forEach(e-> System.out.println(e));
          }
      
    映射
    • map:接收Lambda,将元素转换成其它形式或提取信息。接收一个函数作为参数,改函数会被应用到每个元素上,并映射成为一个新元素。

      public void test5(){
              emps.stream().map((emp) -> emp.getName()).forEach((emp)-> System.out.println(emp));
          }
      
    • flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

    排序
    • sorted():自然排序 按对象指定的Comparable方式排序

      public void test6(){
              List<String> list = Arrays.asList("ccc","bbb","sss","aaa");
              list.stream().sorted().forEach(l-> System.out.println(l));
          }
      
    • sorted(C c):定制排序 Comparator 按自己传递的新的方式去排

        public void test8(){
             emps.stream().sorted((e1,e2)->{
                 if (e1.getAge().equals(e2.getAge())){
                     return e1.getName().compareTo(e2.getName());
                 }else {
                     return e1.getAge().compareTo(e2.getAge());
                 }
             }).forEach(e-> System.out.println(e));
          }
      
    终止
    • allMatch:检查是否匹配所有元素

       public void test11(){
              //allMatch是不是匹配所有元素(对象中Status()枚举中是否全为BUSY)
              boolean b = emps.stream().allMatch(emp -> emp.getStatus().equals(Emp.Status.BUSY));
              System.out.println(b);
          }
      
    • anyMatch:检查是否有一个匹配元素

       public void test12(){
              boolean b = emps.stream().anyMatch(emp -> emp.getStatus().equals(Emp.Status.BUSY));
              System.out.println(b);
          }
      
    • noneMatch:检查是否没有匹配所有元素

      public void test13(){
              boolean b = emps.stream().noneMatch(emp -> emp.getStatus().equals(Emp.Status.BUSY));
              System.out.println(b);
          }
      
    • findFirst:返回第一个元素Optional<>(为了防止空指针异常,Stream用Optional封装了对象,get方法可取出对象)

        public void test14(){
              //findFirst返回了个Optional<Emp>
              Optional<Emp> b = emps.stream().sorted((e1,e2)->{
                  if (e1.getAge().equals(e2.getAge())){
                      return e1.getName().compareTo(e2.getName());
                  }else {
                      return e1.getAge().compareTo(e2.getAge());
                  }
              }).findFirst();
              Emp emp = b.get();
              System.out.println(emp);
          }
      
    • findAny:返回当前流中任意元素

       public void test15(){
              //allMatch是不是匹配所有元素
          	//parallelStream多线程流stream单线程流
              Optional<Emp> b = emps.parallelStream().findAny();
              Emp emp = b.get();
              System.out.println(emp);
          }
      
    • count:返回流中元素总数

       public void test16(){
              long b = emps.parallelStream().count();
              System.out.println(b);
          }
      
      
    • max/min:返回流中最大/小值

      public void test17(){
              //自己决定根据什么选的最大/小值
              Optional<Emp> max = emps.parallelStream().max((e1, e2) -> Integer.compare(e1.getAge(), e2.getAge()));
              System.out.println(max);
          }
      

    Collections操作集合

    排序

    • reverse(List list):反转指定List集合中元素顺序。
    • shuffle(List list):对指定List集合进行随机排序
    • sort():
      1. 形参传入List:根据元素的自然顺序对List集合进行升序排序。
      2. 形参传入List,Comparator:根据指定Comparator产生的顺序对List集合进行排序。
        • Comparator:自定义顺序(lambda表达式)
    • swap(List list,int i,int j):将指定List集合中的i处元素和j处元素交换。
    • rotate(List,int distance):
      • 当distance为正数时,将集合的distance个元素整体移到面。
      • 当distance为负数时,将集合的distance个元素整体移到面。
            List<Emp> emps = new ArrayList<>(); //对象集合
            emps.add(new Emp(1,"小王",23));
            emps.add(new Emp(2,"小张",24));
            emps.add(new Emp(3,"小李",18));
            emps.add(new Emp(4,"小孙",21));
            List<Integer> num = new ArrayList<>();//数字集合
            num.add(1);
            num.add(0);
            num.add(-1);
            num.add(5);        
    //操作--------------------------------------------------------------------------------
            //反转
            Collections.reverse(emps);
            //随机
            Collections.shuffle(emps);
            //自然排序(Integer)
            Collections.sort(num);
            //定制排序:年龄从大到小
            Collections.sort(emps,(e1,e2)->{
                //Integer提供了compare方法,可比按数字大小进行排序,默认升序,Integer前加-(负号)即降序
                int compare = Integer.compare(e1.getAge(), e2.getAge());
                return compare;
            });
            //交换集合中低三个和第四个元素的位置
            Collections.swap(emps,2,3);
            //将集合最后两个元素剪切至集合最前面
            Collections.rotate(emps,2);
    

    附:Integer提供了compare方法,可比按数字大小进行排序,默认升序,Integer前加-(负号)即降序

  • 相关阅读:
    查询死锁和处理死锁(SqlServer)
    日期函数(SqlServer)
    [Shell] echo/输出 中引用命令
    Github 团队协作基本流程与命令操作 图解git工作流程
    HTML 引入 CSS、JS 的三种方式
    JavaScript 字符串匹配 | JS 的正则用法 | 从后边匹配
    Sublime + Chrome 本地调试 CSS 选择器
    常用 CSS 选择器
    使用 Sublime 或其他编辑器调试 Tampermonkey 油猴脚本
    使用 chrome 扩展 Vimium 实现快捷键关闭其他标签页
  • 原文地址:https://www.cnblogs.com/sxblogs/p/13046415.html
Copyright © 2011-2022 走看看