Java 集合框架概述:Collection 与 Map 接口体系

好的,各位程序猿、攻城狮、代码界的艺术家们,今天咱们来聊聊Java集合框架这个“老朋友”。别看它“老”,用起来可是相当“骚”气!咱们要像老司机一样,把Collection和Map这两大接口体系摸得门儿清,这样才能在代码的世界里驰骋自如,写出高效优雅的程序。

一、Java集合框架:一个江湖,两种势力

你可以把Java集合框架想象成一个武林,里面高手如云,秘籍无数。而Collection和Map,就是这个武林中两大势力。

  • Collection势力:单身贵族的聚集地

    Collection接口代表的是一组对象,每个对象都是独立的个体。你可以把它看作一个单身俱乐部,里面的每个成员都是自由的灵魂,彼此之间没有必然的联系。Collection下面又分了三个分支:

    • List:有序可重复的队伍

      List接口就像一支训练有素的军队,里面的元素按照特定的顺序排列,而且允许有重复的士兵。ArrayList和LinkedList就是这支军队里最著名的两个兵种。

      • ArrayList:速度型选手

        ArrayList底层是基于数组实现的,所以它在随机访问元素时速度飞快,就像博尔特一样。但是,在插入和删除元素时,需要移动大量的元素,就像搬家一样费劲。

        List<String> arrayList = new ArrayList<>();
        arrayList.add("Java");
        arrayList.add("Python");
        arrayList.add("C++");
        System.out.println(arrayList.get(1)); // 输出:Python
        arrayList.add(1, "JavaScript"); // 在索引1处插入元素
        System.out.println(arrayList); // 输出:[Java, JavaScript, Python, C++]
      • LinkedList:灵活型选手

        LinkedList底层是基于链表实现的,所以它在插入和删除元素时非常灵活,就像泥鳅一样滑溜。但是,在随机访问元素时,需要从头开始遍历,就像大海捞针一样费劲。

        List<String> linkedList = new LinkedList<>();
        linkedList.add("Java");
        linkedList.add("Python");
        linkedList.add("C++");
        linkedList.addFirst("JavaScript"); // 在列表头部添加元素
        linkedList.addLast("Go"); // 在列表尾部添加元素
        System.out.println(linkedList.getFirst()); // 输出:JavaScript
        System.out.println(linkedList.getLast()); // 输出:Go

      List的特点:

      特点 ArrayList LinkedList
      底层实现 数组 链表
      访问速度 随机访问快 随机访问慢
      增删速度 增删慢 增删快
      内存占用 占用连续空间 占用分散空间
      适用场景 频繁访问元素 频繁增删元素
    • Set:独一无二的集合

      Set接口就像一个高冷的俱乐部,里面的每个成员都是独一无二的,不允许有重复的。HashSet和TreeSet就是这个俱乐部里最受欢迎的两个派系。

      • HashSet:无序的快速查找

        HashSet底层是基于哈希表实现的,所以它在查找元素时速度非常快,就像警察抓小偷一样高效。但是,HashSet里面的元素是无序的,就像一群自由散漫的艺术家。

        Set<String> hashSet = new HashSet<>();
        hashSet.add("Java");
        hashSet.add("Python");
        hashSet.add("C++");
        hashSet.add("Java"); // 重复元素,不会被添加
        System.out.println(hashSet.contains("Python")); // 输出:true
        System.out.println(hashSet); // 输出:[Java, Python, C++] (顺序不确定)
      • TreeSet:有序的自动排序

        TreeSet底层是基于红黑树实现的,所以它里面的元素是有序的,就像参加选美比赛的佳丽一样,按照一定的规则排列。但是,TreeSet在插入和删除元素时,需要维护树的结构,所以速度相对较慢。

        Set<String> treeSet = new TreeSet<>();
        treeSet.add("Java");
        treeSet.add("Python");
        treeSet.add("C++");
        System.out.println(treeSet); // 输出:[C++, Java, Python] (按照字母顺序排序)
        
        Set<Integer> treeSet2 = new TreeSet<>((a, b) -> b - a); // 自定义排序规则,降序
        treeSet2.add(10);
        treeSet2.add(5);
        treeSet2.add(15);
        System.out.println(treeSet2); // 输出:[15, 10, 5]

      Set的特点:

      特点 HashSet TreeSet
      底层实现 哈希表 红黑树
      元素顺序 无序 有序
      查找速度 查找快 查找相对慢
      元素是否重复 不允许重复 不允许重复
      适用场景 需要快速查找元素 需要有序存储元素
    • Queue:先进先出的队列

      Queue接口就像一个排队等候的队伍,里面的元素按照先进先出的原则排列。PriorityQueue和ArrayDeque就是这个队伍里比较特殊的两个成员。

      • PriorityQueue:优先级队列

        PriorityQueue可以根据元素的优先级进行排序,优先级高的元素会优先出队。就像医院里的急诊病人,会优先得到治疗。

        Queue<Integer> priorityQueue = new PriorityQueue<>();
        priorityQueue.add(10);
        priorityQueue.add(5);
        priorityQueue.add(15);
        System.out.println(priorityQueue.poll()); // 输出:5 (默认按照从小到大排序)
        
        Queue<Integer> priorityQueue2 = new PriorityQueue<>((a, b) -> b - a); // 自定义优先级,降序
        priorityQueue2.add(10);
        priorityQueue2.add(5);
        priorityQueue2.add(15);
        System.out.println(priorityQueue2.poll()); // 输出:15
      • ArrayDeque:双端队列

        ArrayDeque是一个双端队列,可以在队列的头部和尾部进行插入和删除操作。就像一个可以双向行驶的隧道。

        Deque<String> arrayDeque = new ArrayDeque<>();
        arrayDeque.addFirst("Java");
        arrayDeque.addLast("Python");
        arrayDeque.offerFirst("JavaScript");
        arrayDeque.offerLast("Go");
        System.out.println(arrayDeque.pollFirst()); // 输出:JavaScript
        System.out.println(arrayDeque.pollLast()); // 输出:Go

      Queue的特点:

      特点 PriorityQueue ArrayDeque
      元素顺序 优先级排序 先进先出
      插入位置 根据优先级 头部和尾部
      删除位置 优先级最高的元素 头部和尾部
      适用场景 需要优先级排序 需要双端操作
  • Map势力:情侣的聚集地

    Map接口代表的是一组键值对,每个键都对应一个值。你可以把它看作一个婚姻介绍所,里面的每对情侣都是紧密联系的,通过键可以找到对应的值。HashMap和TreeMap就是这个介绍所里最受欢迎的两个服务项目。

    • HashMap:无序的快速查找

      HashMap底层是基于哈希表实现的,所以它在查找元素时速度非常快,就像搜索引擎一样高效。但是,HashMap里面的元素是无序的,就像一群自由恋爱的年轻人。

      Map<String, Integer> hashMap = new HashMap<>();
      hashMap.put("Java", 1);
      hashMap.put("Python", 2);
      hashMap.put("C++", 3);
      System.out.println(hashMap.get("Python")); // 输出:2
      System.out.println(hashMap.containsKey("Java")); // 输出:true
      System.out.println(hashMap.containsValue(2)); // 输出:true
      
      // 遍历HashMap
      for (Map.Entry<String, Integer> entry : hashMap.entrySet()) {
          System.out.println(entry.getKey() + ": " + entry.getValue());
      }
      
      // 使用keySet()遍历
      for(String key : hashMap.keySet()){
          System.out.println(key + ": " + hashMap.get(key));
      }
      
      // 使用values()遍历
      for(Integer value : hashMap.values()){
          System.out.println(value);
      }
    • TreeMap:有序的自动排序

      TreeMap底层是基于红黑树实现的,所以它里面的元素是有序的,就像参加相亲大会的情侣一样,按照一定的规则排列。但是,TreeMap在插入和删除元素时,需要维护树的结构,所以速度相对较慢。

      Map<String, Integer> treeMap = new TreeMap<>();
      treeMap.put("Java", 1);
      treeMap.put("Python", 2);
      treeMap.put("C++", 3);
      System.out.println(treeMap); // 输出:{C++=3, Java=1, Python=2} (按照键的字母顺序排序)
      
      Map<String, Integer> treeMap2 = new TreeMap<>((a, b) -> b.compareTo(a)); // 自定义排序规则,键的降序
      treeMap2.put("Java", 1);
      treeMap2.put("Python", 2);
      treeMap2.put("C++", 3);
      System.out.println(treeMap2); // 输出:{Python=2, Java=1, C++=3}

    Map的特点:

    特点 HashMap TreeMap
    底层实现 哈希表 红黑树
    元素顺序 无序 有序
    查找速度 查找快 查找相对慢
    键是否重复 不允许重复 不允许重复
    适用场景 需要快速查找元素 需要有序存储元素

二、Collection与Map的区别:单身和情侣的区别

Collection和Map最大的区别在于,Collection存储的是单个对象,而Map存储的是键值对。你可以把Collection想象成一个单身俱乐部,里面的每个成员都是独立的个体,而Map想象成一个婚姻介绍所,里面的每对情侣都是紧密联系的。

三、选择合适的集合:因地制宜,量体裁衣

在实际开发中,选择合适的集合非常重要。就像选择合适的武器一样,用对了可以事半功倍,用错了可能会适得其反。

  • 如果需要存储一组有序且可重复的元素,可以选择List。

    • 如果需要频繁访问元素,可以选择ArrayList。
    • 如果需要频繁插入和删除元素,可以选择LinkedList。
  • 如果需要存储一组唯一且无序的元素,可以选择Set。

    • 如果需要快速查找元素,可以选择HashSet。
    • 如果需要有序存储元素,可以选择TreeSet。
  • 如果需要存储一组键值对,可以选择Map。

    • 如果需要快速查找元素,可以选择HashMap。
    • 如果需要有序存储元素,可以选择TreeMap。
  • 其他特殊场景:

    • 需要优先级队列时,选择PriorityQueue。
    • 需要双端队列时,选择ArrayDeque。
    • 需要线程安全的集合时,可以选择ConcurrentHashMap、CopyOnWriteArrayList等。

四、实战演练:代码才是硬道理

光说不练假把式,咱们来几个实战演练,看看如何运用这些集合。

  • 统计字符串中每个字符出现的次数

    String str = "Hello World!";
    Map<Character, Integer> charCountMap = new HashMap<>();
    for (char c : str.toCharArray()) {
        charCountMap.put(c, charCountMap.getOrDefault(c, 0) + 1);
    }
    System.out.println(charCountMap); // 输出:{ =1, d=1, r=1, H=1, e=1, l=3, W=1, o=2, !=1}
  • 去除List中的重复元素

    List<String> list = new ArrayList<>();
    list.add("Java");
    list.add("Python");
    list.add("Java");
    list.add("C++");
    Set<String> uniqueSet = new HashSet<>(list);
    List<String> uniqueList = new ArrayList<>(uniqueSet);
    System.out.println(uniqueList); // 输出:[Java, Python, C++] (顺序不确定)
  • 对学生成绩进行排序(按照成绩从高到低)

    class Student {
        String name;
        int score;
    
        public Student(String name, int score) {
            this.name = name;
            this.score = score;
        }
    
        public String getName() {
            return name;
        }
    
        public int getScore() {
            return score;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + ''' +
                    ", score=" + score +
                    '}';
        }
    }
    
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>();
        students.add(new Student("Alice", 80));
        students.add(new Student("Bob", 90));
        students.add(new Student("Charlie", 70));
    
        // 使用Collections.sort()进行排序
        Collections.sort(students, (a, b) -> b.getScore() - a.getScore());
    
        System.out.println(students); // 输出:[Student{name='Bob', score=90}, Student{name='Alice', score=80}, Student{name='Charlie', score=70}]
    }

五、总结:掌握集合,走向人生巅峰

Java集合框架是Java编程中非常重要的一个组成部分。掌握了Collection和Map这两大接口体系,就相当于掌握了Java编程的半壁江山。希望这篇文章能帮助大家更好地理解Java集合框架,并在实际开发中灵活运用,写出更加高效优雅的代码。

记住,代码的世界是无限的,学习永无止境。加油,各位未来的编程大师们!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注