【一天一个基础系列】- java之集合篇(上) | 七日打卡

231 阅读5分钟

简介

  • 为了保存数量不确定的数据,以及保存具有映射关系的数据(也被称为关联数组),Java提供了集合类,集合类主要负责保存、盛装其他数据,因此集合类也被称为容器类。所有的集合类都位于java.util包下,后来为了处理多线程环境下的并发安全问题,Java 5还在java.util.concurrent包下提供了一些多线程支持的集合类
    • 集合里只能保存对象,这个跟数组的区别
    • 集合类主要由两个接口派生而出
      • CollectionMap
  • 类的继承树图

Collection接口、子接口及其实现

Map体系的继承树

  • 四种集合

    • Set
    • List
    • Queue
    • Map
  • 常用的实现类:HashSet、TreeSet、ArrayList、ArrayDeque、LinkedList、HashMap、TreeMap等实现类

  • 访问方式

    • List:根据元素的索引来访问
    • Map:根据key来访问value
    • Set:只能根据元素本身来访问
  • 简单使用

    • lambda表达式遍历集合

      • java 8Iterable接口新增了一个forEach(Consumer action)默认方法,该方法所需参数的类型是一个函数式接口,而Iterable接口是Collection接口的父接口,因此Collection集合也可直接调用该方法
        //创建集合
        var books = new HashSet();
        books.add("java");
        books.add("c++");
        //使用foreach()遍历集合
        books.foreach(book -> System.out.println(book));
        
    • Iterator遍历集合元素

      • Iterator接口也是Java集合框架的成员,但它与Collection系列、Map系列的集合不一样:Collection系列集合、Map系列集合主要用于盛装其他对象,而Iterator则主要用于遍历(即迭代访问)Collection集合中的元素,Iterator对象也被称为迭代器,Iterator仅用于遍历集合,Iterator本身并不提供盛装对象的能力
        //遍历并打印
        var list = books.iterator();
        While(list.hasNext()){
        	var book = list.next();
        	System.out.println(book)
        }
        

        注意:Iterator必须依附于Collection对象,若有一个Iterator对象,则必然有一个与之关联的Collection对象

    • Lambda表达式遍历Iterator

      • Java 8Iterator新增了一个forEachRemaining(Consumer action)方法
        var list = books.iterator();
        //使用lambda表达式(目标类型Comsumer)来遍历集合元素
        books.forEachRemaining(book -> System.out.println(book));
        
    • foreach循环遍历集合元素

      • Java 5提供foreach循环迭代访问集合元素更加便捷
        for(var book : book){
        	System.out.println(book);
        }
        
    • Predicate操作集合

      • Java 8Collection集合新增了一个removeIf(Predicate filter)方法,该方法将会批量删除符合filter条件的所有元素。该方法需要一个Predicate(谓词)对象作为参数,Predicate也是函数式接口,因此可使用Lambda表达式作为参数
        List<String> list = new ArrayList<>();
        list.add("java");
        list.removeIf(book -> book.equals("java"));
        
    • Stream操作集合

      • Java 8还新增了Stream、IntStream、LongStream、DoubleStream等流式API,这些API代表多个支持串行和并行聚集操作的元素
        IntStream is = IntStream.builder()
        		.add(1)
        		.add(2)
        		.build();
        System.out.println(is.max().getAsInt());
        System.out.println(is.min().getAsInt());
        
      • Stream提供了大量的方法进行聚集操作,这些方法既可以是“中间的”(intermediate),也可以是“末端的”(terminal),具体可以参见API源码
    • Set集合

      • HashSet
        • 不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也有可能发生变化
        • HashSet不是同步的,如果多个线程同时访问一个HashSet,假设有两个或者两个以上线程同时修改了HashSet集合时,则必须通过代码来保证其同步,即是非线程安全
        • 集合元素值可以是null

      当向HashSet中添加可变对象时,必须十分小心。如果修改HashSet集合中的对象,有可能导致该对象与集合中的其他对象相等,从而导致HashSet无法准确访问该对象

      • LinkedHashSet
        • LinkedHashSet集合也是根据元素的hashCode值来决定元素的存储位置,但它同时使用链表维护元素的次序,这样使得元素看起来是以插入的顺序保存的。也就是说,当遍历LinkedHashSet集合里的元素时,LinkedHashSet将会按元素的添加顺序来访问集合里的元素

      虽然LinkedHashSet使用了链表记录集合元素的添加顺序,但LinkedHashSet依然是HashSet,因此它依然不允许集合元素重复

      • TreeSet
        • TreeSetSortedSet接口的实现类,正如SortedSet名字所暗示的,TreeSet可以确保集合元素处于排序状态
        • TreeSet有自然排序跟定制排序,自然排序调用自带的排序方法即可,定制排序需要通过Comparator接口的帮助,自行实现排序规则
        @Data
        @ToString
         @AllArgsConstructor
         @NoArgsConstructor
        class Clazz{
        int age;
        }
        
        //使用
        var tree = new TreeSet((o1,o2->{
        var c1 = (Clazz) o1;
        var c2 = (Clazz) o2;
        //根据age的属性决定,age越大,Clazz对象反而越小
        return c1.age > c2.age ? -1 : c1.age < c2.age ? 1 : 0;
        }));
        

      Java 9改进了TreeSet实现,如果采用自然排序的Set集合的元素没有实现Comparable接口,程序就会立即引发ClassCastException异常,如果希望TreeSet能正常运作,TreeSet只能添加同一种类型的对象

      • EnumSet
        • EnumSet是一个专为枚举类设计的集合类,EnumSet中的所有元素都必须是指定枚举类型的枚举值,该枚举类型在创建EnumSet时显式或隐式地指定。EnumSet的集合元素也是有序的,EnumSet以枚举值在Enum类内的定义顺序来决定集合元素的顺序
          • EnumSet在内部以位向量的形式存储,这种存储形式非常紧凑、高效,因此EnumSet对象占用内存很小,而且运行效率很好。尤其是进行批量操作(如调用containsAll()retainAll()方法)时,如果其参数也是EnumSet集合,则该批量操作的执行速度也非常快
          • EnumSet集合不允许加入null元素,如果试图插入null元素,EnumSet将抛出NullPointerException异常。如果只是想判断EnumSet是否包含null元素或试图删除null元素都不会抛出异常,只是删除操作将返回false,因为没有任何null元素被删除
    • Set实现类的性能分析

      • HashSet的性能总是比TreeSet好(特别是最常用的添加、查询元素等操作),因为TreeSet需要额外的红黑树算法来维护集合元素的次序
      • 对于普通的插入、删除操作,LinkedHashSetHashSet要略微慢一点,这是由维护链表所带来的额外开销造成的,但由于有了链表,遍历LinkedHashSet会更快
      • EnumSet是所有Set实现类中性能最好的,但它只能保存同一个枚举类的枚举值作为集合元素