Java集合框架

500 阅读7分钟

概述

  1. JDK1.2时候,Java的设计者们对Java存储数据的容器进行了大刀阔斧的改革。推出了庞大的Java集合框架体系。
  2. Java集合框架拥有非常庞大的容器体系,通过不同的实现对不同场景的元素存取达到高性能。
  3. Java集合拥有2大常用分支,一支是Collection体系的存储单个元素的集合,另一分支存储着Key——Value健值对形式的Map体系。
  4. Collection体系中,又拥有3大常用分支分为List,Queue,Set。
  5. 而Map体系中,分为无序Key-Value和有序Key-Value形式2大类。
  6. 集合的子类非常多,本文将介绍常用的集合实现类。
  7. 下图展示了集合的继承关系,通过掌握继承关系图,可以更加容易学习集合框架。

Java常用集合类架构图

Java Map 集合类架构图

Collection 接口

  1. 整个Collection接口是集合的根接口,Collection用来描述一组对象,描述集合这个概念
  2. 同时Collection也提供了基本操作集合的方法
  3. 一些 Collection 允许有重复的元素,而另一些则不允许。一些 Collection 是有序的,而另一些则是无序的
  4. Collection子接口分为常用的三大分支:List(有序集合)Queue(队列)Set(无序不可重复)
  5. Collection 规范了集合的常用方法,包含添加,删除,是否包含,迭代器等常用功能。

List 接口

  1. List接口用来描述一组有序集合,集合记录元素插入顺序
  2. List接口允许用户通过索引对元素精准控制,包括插入,修改,访问元素。
  3. List接口通常允许元素重复
  4. List 接口在Collection接口基础上,提供了更多通过索引操作元素

ArrayList

  1. List 接口的可变数组的实现类,其拥有父接口List有序集合,索引控制,重复元素的等特点。
  2. 允许插入null所在内的元素。
  3. 此类还提供一些方法来操作内部用来存储列表的数组的大小。
  4. 在添加元素前,ArrayList使用 ensureCapacity 操作来增加容量
        // 1. 创建ArrayList对象
        ArrayList<String> list = new ArrayList<>();

        //2. 添加设置元素的值
        list.add("hello");
        list.add(1, "world");
        list.add("java");

        //3. 删除元素
        list.remove("world");
        list.remove(0);

        //4. 修改元素的值
        list.set(0,"Hello");

        //5. 查询相关
        list.get(0);

        list.forEach((String e) -> {
            System.out.println("e = " + e);
        });

Vector

  1. Vector 是JDK1.0提供的集合类。
  2. Vector 的底层实现与ArrayList相同,并提供了ArrayList相同的功能。
  3. Vector 所有方法都通过增加synchronized来实现线程安全
  4. Vector 是线程安全的,但同时效率相比ArrayList较低,在JDK1.2中,ArrayList是Vector的替代方法,推荐使用ArrayList

LinkedList

  1. List接口的双向链表实现。
  2. 其拥有父接口List有序集合,索引控制,重复元素的等特点。
  3. LinkedList 在操作列表,头元素,尾元素 (get、remove、insert)时,提供了统一的命名方式,所以LinekedList 这些操作允许将链表当作队列双端队列使用。
  4. LinekedList所有操作都是按照双向链表方式执行的,当按索引获取/修改/插入元素时将从开头或结尾遍历链表(从靠近指定索引的一端)
  5. LinkedList 由于双向链表的特性,以及实现了Queue,和Dequeue接口,将而外提供栈,和双端队列的特点。

6. 作为队列使用的LinkedList

        LinkedList<String> queue = new LinkedList<>();
        // 向队列头插入元素
        queue.offer(" world");
        // 默认 linked last
        queue.offer("hello");
        queue.offerFirst("java");

        //queue = [java,  world, hello]
        System.out.println("queue = " + queue);

        // 获取 队列 头、尾元素,但不移除
        String v = queue.peek();
        v = queue.peekFirst();
        v = queue.peekLast();

        // 获取 队列 头、尾元素,并且移除出队列
        v = queue.poll();
        v = queue.pollLast();
  1. 作为栈使用的LinkedList
        LinkedList<String> stack = new LinkedList<>();

        stack.push("hello");
        stack.push("world");
        
        stack.pop();
        stack.pop();

Queue & Deque

  1. Queue队列维护着一个先进先出(First In First Out)数据结构
  2. Queue接口与List、Set同一级别,都是继承了Collection接口。
  3. Deque一个线性 Collection,支持在两端插入和移除元素。名称 deque 是“double ended queue(双端队列)”的缩写。
  4. 此接口定义在双端队列两端访问元素的方法。提供插入、移除和检查元素的方法。
  5. LinkedList使用双向链表实现了基本的队列操作

Set

  1. Set集合在Collection集合基础上 提供 不包含重复元素的功能。
  2. Set不包含满足 e1.equals(e2)(el==e2) 的元素,并且最多包含一个 null 元素。
  3. Set集合一般不维护着元素的插入顺序
  4. 同时Set集合不提供对索引精准控制。
  5. Set实现类底层采用与之名称对应Map实现类实现,通过将元素存储在Map的Key中,Value采用全局静态Object的实例。
  6. Set接口提供着和Collection相同的方法列表规定。

HashSet

  1. Set集合基于哈希表实现类---对应HashMap。
  2. HashSet内部使用HashMap的key来存储元素,value使用静态的Object对象实例。
  3. HashSet提供了Set接口的所有方法的实现,在判断是否包含某元素删除插入元素上,拥有卓越的性能。

LinkedHashSet

  1. LinkedHashSet 可预知迭代顺序Set接口的实现类,基于哈希表+双向链表实现---对应LinkedHashMap。
  2. LinkedHashSet 继承自 HashSet
  3. 通过调用父类接收3个参数构造方法,在内部初始化LinkedHashMap存储元素,所以其实现代码写在父类里,但父类HashMap此初始化方法是 默认 修饰符,只有包访问权限,所以只能子类初始化。
  4. 与 HashSet 的不同之外在于,后者在哈希表的基础上,增加了一个维护所有条目的双向链表,此链表定义了迭代顺序,即插入元素顺序。

TreeSet

  1. TreeSet 可排序的Set接口的实现类---对应TreeMap。
  2. TreeSet 内部使用了TreeMap(红黑树)实现排序功能,同样将元素作为TreeMap的Key。
  3. TreeSet 默认使用元素的自然排序,元素需要实现Comparable接口。
  4. TreeSet 同时允许,通过传递比较器(Comparator)实现类,自定义排序。
        // TreeSet 自然排序
        TreeSet<Integer> set = new TreeSet<>();
        set.add(10);
        set.add(7);
        set.add(2);
        set.add(9);
        // set = [2, 7, 9, 10]
        System.out.println("set = " + set);
        // 实现  Comparator 接口,自定义排序
        TreeSet<Student> set = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                int num = o1.getAge() - o2.getAge();
                num = num == 0 ? o1.getName().compareTo(o2.getName()) : num;
                return num;
            }
        });

Map<K,V>

  1. Map接口是Map体系的根接口。
  2. Map特点是将键映射到值的对象,每个键最多只能映射到一个值
  3. Map中同样相同的健只能存储一个。

HashMap<K,V>

  1. HashMap是Map接口基于哈希表的实现类。
  2. 其拥有Map接口的所有实现方法。
  3. HashMap不保证映射的顺序,即插入元素的顺序。
  4. 关于哈希表的实现,请参考。

LinkedHashMap<K,V>

  1. LinkedHashMap是Map接口 哈希表和链表的实现。
  2. 其拥有Map接口所有的方法,并具有可预知的迭代顺序。
  3. 后者维护着一个运行于所有条目的双重链表。此链表定义了迭代顺序,该迭代顺序通常就是将键插入到映射中的顺序

Hashtable<K,V>

  1. Hashtable 与 HashMap同样是基于哈希表实现类。
  2. Hashtable 是JDK1.0推出的键值映射集合,在JDK1.2后此类就被改进以实现 Map 接口。
  3. Hashtable不保证映射的顺序,即插入元素的顺序。
  4. 与HashMap不同的是,Hashtable所有方法都是线程同步的。

Properties

  1. Properties 类是Hashtable的子类,其同样是键值映射集合。
  2. 不同的是 Properties类表示了一个持久的属性集
  3. Properties 可以配合IO流,从流中读取或将数据写入流中
  4. Properties 中每个键及其对应值都是一个字符串
  5. 使用 Properties 时,不建议使用 put 和 putAll 方法,因为他们允许插入String之外的值
  6. Java开发中,常常将一些配置信息存放在.properties文件中,而使用Properties类的load方法进行加载。

7. 使用Properties存储键值对,并存储到文件中。

        Properties p = new Properties();
        p.setProperty("name","李洛克");
        p.setProperty("age","18");

        // 将 properties 保存到 classPath 路径下
        String classPath = ClassLoader.getSystemResource("").getPath();

        OutputStream os = new FileOutputStream(classPath + "test.properties");

        p.store(os,"注释");
        
        // 所谓classpath 就是指使用 javac -d classpath 的路径
        // 在idea中就是 project compiler output path
        // maven 工程会自动将 标记为 resources 文件夹下文件拷贝到classpath路径下
  1. 读取文件中properties的内容
        // 从 classPath 下加载 properties文件
        InputStream is = ClassLoader.getSystemResourceAsStream("test.properties");
        Properties p = new Properties();
        p.load(is);
        System.out.println(p.getProperty("name"));
        System.out.println(p.getProperty("age"));

TreeMap<K,V>

  1. 可自然排序和自定义规则排序的Map集合。
  2. TreeMap 是基于红黑树(Red-Black tree)的 NavigableMap 接口的实现。
  3. 使用TreeMap实现排序方法
        // 自定义元素排序,按照key的长度 进行排序
        Map<String, Integer> map = new TreeMap<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.length() - o2.length();
            }
        });

        map.put("a",1);
        map.put("bbbb",1);
        map.put("ccc",1);
        // map = {a=1, ccc=1, bbbb=1}
        System.out.println("map = " + map);