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前加-(负号)即降序

原文地址:https://www.cnblogs.com/sxblogs/p/13046415.html