一文搞懂java集合框架

一文搞懂java集合框架

在这里插入图片描述

什么是集合?

Java集合框架主要包括两个部分:接口和实现类。其中,接口定义了一系列的方法,而实现类则是实现了这些接口中定义的方法。Java集合框架中的接口主要分为三大类:List(列表)、Set(集合)和Map(映射)。

有什么特点?

  • 可以动态保存任意多个对象
  • 提供了⼀系列方便的操作对象的方法
  • 使用集合添加、删除新元素的示意代码更简介

框架图

在这里插入图片描述

Java集合类主要由两个根接口Collection和Map派生出来的,Collection派生出了三个子接口:List、Set、Queue(Java5新增的队列),因此Java集合大致也可分成List、Set、Queue、Map四种接口体系,(注意:Map不是Collection的子接口)。

其中List代表了有序可重复集合,可直接根据元素的索引来访问;Set代表无序不可重复集合,只能根据元素本身来访问;Queue是队列集合;Map代表的是存储key-value对的集合,可根据元素的key来访问value。

Collection

基本介绍:

  1. Collection实现子类可以存放多个元素,每个元素可以是Object
  2. Collection的实现类,有些可以存放重复的元素,有些不可以
  3. Collection的实现类,有些是有序的(List),有些不是有序(Set)
  4. Collection接口没有直接的实现子类,是通过它的子接口Set 和 List 来实现的

接口常用方法

  • add:添加单个元素
  • remove:删除指定元素
  • contains:查找元素是否存在
  • size:获取元素个数
  • isEmpty:判断是否为空
  • clear:清空
  • addAll:添加多个元素
  • containsAll:查找多个元素是否都存在
  • removeAll:删除多个元素

使用代码示例

public static void main(String[] args) {
    
    
        List list = new ArrayList();
        list.add("jack");
        list.add(10);
        list.add(true);
        list.remove(true);
        System.out.println("list = " + list);
        System.out.println(list.size());
        System.out.println(list.isEmpty());
        list.clear();
        System.out.println("list = " + list);


        ArrayList list2 = new ArrayList();
        list2.add("红楼梦");
        list2.add("三国演义");
        list.addAll(list2);
        System.out.println("list=" + list);
        // containsAll:查找多个元素是否都存在
        System.out.println(list.containsAll(list2));
    }

在这里插入图片描述

List

基本介绍

  1. List集合类中元素有序(即添加顺序和取出顺序⼀致)、且可重复
  2. List集合中的每个元素都有其对应的顺序索引,即支持索引
  3. List容器中的元素都对应⼀个整数型的序号记载其在容器中的位置,可以根据序号存取容器元素
  4. 常用: ArrayList、LinkedList、Vector

常用方法

  • void add(int index, Object ele):在index位置插⼊ele元素
  • boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
  • Object get(int index):获取指定index位置的元素
  • int indexOf(Object obj):返回obj在集合中首次出现的位置
  • int lastindexOf(Object obj):返回obj在当前集合中末次出现的位置
  • Object remove(int index):移除指定index位置的元素,井返回此元素
  • Object set(int index, Object ele):设置指定index位置的元素为ele,相当于是替换
  • List sublist(int fromlndex, int tolndex):返回从fromlndex到tolndex位置的⼦集合

使用代码示例

 public static void main(String[] args) {
    
    
        // 创建一个ArrayList对象
        List<String> list = new ArrayList<>();

        // 添加元素
        list.add("apple");
        list.add("banana");
        list.add("cherry");
        list.add("date");
        list.add("elderberry");
        System.out.println(list );
        // 获取元素
        String firstElement = list.get(0);
        String lastElement = list.get(list.size() - 1);

        // 修改元素
        list.set(3, "dragonfruit");

        // 删除元素
        list.remove("banana");
        list.remove(2);

        // 判断元素是否存在
        boolean hasApple = list.contains("apple");

        // 获取元素的位置
        int index = list.indexOf("date");
        System.out.println(index);

        // 获取子列表
        List<String> subList = list.subList(1, 3);

        // 清空列表
        list.clear();

        // 判断列表是否为空
        boolean isEmpty = list.isEmpty();
        System.out.println(isEmpty);

         //快速遍历列表
        for (String element : subList) {
    
    
            System.out.println(element);
        }
        //普通遍历方法
        for (int i = 0; i < subList.size(); i++) {
    
    
            System.out.println(subList.get(i));
        }
    }

在这里插入图片描述

ArrayList

注意事项和细节

  1. 允许所有元素包括null加入
  2. 由数组来实现数据存储
  3. 基本等同于Vector,除了是ArrayList线程不安全的但是执行效率高,在多线程下不建议使用ArrayList。

Vector

注意事项和细节

  1. Vector底层是⼀个对象数组, protected Object[] elementData
  2. Vector 是线程同步的,即线程安全,Vector类的操作⽅法带有synchronized

ArrayList和Vector

ArrayList和Vector都是Java中常用的List实现类,它们都实现了List接口,具有类似的功能,但在实现细节和性能上有所不同。

ArrayList是一个基于数组实现的动态数组,它支持随机访问和快速遍历,并且可以动态添加和删除元素。由于ArrayList的内部实现是基于数组,因此它的随机访问和修改操作的时间复杂度为O(1),但在插入和删除操作时需要移动数组中的元素,因此时间复杂度为O(n)。ArrayList的优点是在随机访问和修改操作时性能较好,适合用于读取和修改操作比较频繁的场景

Vector也是一个动态数组,但它是线程安全的,因此在多线程环境下使用更加可靠。Vector的内部实现与ArrayList类似,但它的每个操作都是同步的,可以保证多个线程同时访问时的安全性。由于Vector的每个操作都需要进行同步,因此在性能上可能比ArrayList略慢一些。Vector的优点是可以在多线程环境下安全地使用,适合用于需要线程安全性的场景
在这里插入图片描述

如何创建与使用它们

import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

public class ListExample {
    
    
    public static void main(String[] args) {
    
    
        // 创建一个ArrayList对象
        List<String> arrayList = new ArrayList<>();

        // 向列表中添加元素
        arrayList.add("apple");
        arrayList.add("banana");
        arrayList.add("cherry");

        // 输出列表中的元素
        System.out.println("ArrayList elements: " + arrayList);

        // 获取列表中的元素
        String element = arrayList.get(1);
        System.out.println("Element at index 1: " + element);

        // 修改列表中的元素
        arrayList.set(2, "date");
        System.out.println("ArrayList elements after modification: " + arrayList);

        // 删除列表中的元素
        arrayList.remove("banana");
        System.out.println("ArrayList elements after removal: " + arrayList);

        // 创建一个Vector对象
        Vector<String> vector = new Vector<>();

        // 向列表中添加元素
        vector.add("apple");
        vector.add("banana");
        vector.add("cherry");

        // 输出列表中的元素
        System.out.println("Vector elements: " + vector);

        // 获取列表中的元素
        String firstElement = vector.firstElement();
        System.out.println("First element: " + firstElement);

        // 修改列表中的元素
        vector.set(2, "date");
        System.out.println("Vector elements after modification: " + vector);

        // 删除列表中的元素
        vector.remove("banana");
        System.out.println("Vector elements after removal: " + vector);
    }
}

在这里插入图片描述

LinkedList

注意事项和细节

  1. LinkedList底层实现了双向链表和双端队列特点
  2. 可以添加任意元素包括null
  3. 线程不安全,没有实现同步

代码使用示例

import java.util.LinkedList;
import java.util.List;

public class LinkedListExample {
    
    
    public static void main(String[] args) {
    
    
        // 创建一个LinkedList对象
        List<String> linkedList = new LinkedList<>();

        // 向列表中添加元素
        linkedList.add("apple");
        linkedList.add("banana");
        linkedList.add("cherry");

        // 输出列表中的元素
        System.out.println("LinkedList elements: " + linkedList);

        // 获取列表中的元素
        String element = linkedList.get(1);
        System.out.println("Element at index 1: " + element);

        // 修改列表中的元素
        linkedList.set(2, "date");
        System.out.println("LinkedList elements after modification: " + linkedList);

        // 删除列表中的元素
        linkedList.remove("banana");
        System.out.println("LinkedList elements after removal: " + linkedList);

        // 在列表中插入元素
        linkedList.add(1, "orange");
        System.out.println("LinkedList elements after insertion: " + linkedList);

        // 遍历列表中的元素
        for (String e : linkedList) {
    
    
            System.out.println(e);
        }
    }
}

ArrayList和LinkedList

  1. 如果我们改查的操作多,选择ArrayList
  2. 如果我们增删的操作多,选择LinkedList
  3. ⼀般来说,在程序中,80%-90%都是查询,因此⼤部分情况下会选择ArrayList

在这里插入图片描述

Set

基本介绍

  1. Set是Java中的一个集合接口,它继承自Collection接口,表示一个不允许重复元素的集合。
  2. Set集合中的元素是无序的,不可以通过下标访问,而是需要使用迭代器或者foreach循环遍历集合中的元素。
  3. Set集合有多个实现类,其中最常见的为HashSet、TreeSet和LinkedHashSet。
  4. HashSet是基于哈希表实现的Set,它具有快速的插入、删除和查找操作,但不保证元素的顺序。
  5. TreeSet是基于红黑树实现的Set,它可以对元素进行排序,并提供了一些额外的操作,如获取子集和范围查找。由于元素需要进行排序,因此插入、删除和查找操作的性能可能比HashSet略慢。
  6. LinkedHashSet是基于哈希表和双向链表实现的Set,它保留了元素插入的顺序,并具有HashSet的快速操作。

特点

Set的特点是不允许重复元素,因此它常用于需要去重的场景,如统计单词出现的次数、过滤重复的数据等。它也可以用于对元素进行排序和快速查找。需要注意的是,Set对元素的去重是基于元素的equals和hashCode方法实现的,在使用自定义类时需要注意实现这两个方法。

常用方法

  • add:添加单个元素
  • remove:删除指定元素
  • contains:查找元素是否存在
  • size:获取元素个数
  • isEmpty:判断是否为空
  • clear:清空
  • addAll:添加多个元素
  • containsAll:查找多个元素是否都存在
  • removeAll:删除多个元素

使用示例代码

import java.util.HashSet;
import java.util.Set;

public class SetExample {
    
    
    public static void main(String[] args) {
    
    
        // 创建一个HashSet对象
        Set<String> set = new HashSet<>();

        // 向Set中添加元素
        set.add("apple");
        set.add("banana");
        set.add("cherry");
        set.add("apple"); // 重复元素不会被添加

        // 判断Set中是否包含元素
        boolean containsApple = set.contains("apple");
        System.out.println("Set contains apple: " + containsApple);

        // 删除Set中的元素
        set.remove("banana");

        // 遍历Set中的元素
        for (String element : set) {
    
    
            System.out.println(element);
        }

        // 获取Set中元素的个数
        int size = set.size();
        System.out.println("Set size: " + size);

        // 判断Set是否为空
        boolean isEmpty = set.isEmpty();
        System.out.println("Set is empty: " + isEmpty);

        // 清空Set中的所有元素
        set.clear();
    }
}

HashSet

注意事项和细节

  1. Hashset实现了Set接口
  2. Hashset实际上是HashMap
  3. 可以存放null值,但是只能有⼀个null
  4. Hashset不保证元素是有序的,取决于hash后,再确定索引的结果
  5. 不能有重复元素/对象

LinkedHashSet

注意事项和细节

  1. LinkedHashset 是Hashset 的⼦类
  2. LinkedHashSet 底层是⼀个 LinkedHashMap,底层维护了⼀个 数组+双向链表
  3. LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,同时使⽤链表维护元素的次序(图), 这使得元素看起来是以插⼊顺序保存的
  4. LinkedHashSet 不允许添重复元素

TreeSet

TreeSet 是基于红黑树实现的有序集合,它可以对元素进行排序,并提供了一些额外的操作,如获取子集和范围查找。由于元素需要进行排序,因此插入、删除和查找操作的性能可能比 HashSet 和 LinkedHashSet 稍慢。如果需要对元素进行排序,那么可以选择使用 TreeSet。

三者的异同与使用

ashSet、LinkedHashSet 和 TreeSet 都是实现了 Set 接口的集合类,它们都用于存储不重复的元素,并提供了对元素的快速访问和操作,但它们的实现方式不同,具有不同的特点和适用场景。

  1. HashSet

    HashSet 基于哈希表实现,它的元素是无序的,不保证元素的顺序。HashSet 的优点是在查找、添加和删除元素时速度非常快,时间复杂度为 O(1),但它不保证元素的顺序。如果不需要关心元素的顺序,而且需要快速地查找、添加和删除元素,那么 HashSet 是一个不错的选择。

  2. LinkedHashSet

    LinkedHashSet 是 HashSet 的子类,它基于哈希表和双向链表来实现,它保留了元素插入的顺序,并具有 HashSet 的快速操作。LinkedHashSet 的优点是可以按照元素的插入顺序进行遍历,因此如果需要按照元素的插入顺序来遍历集合,那么可以选择使用 LinkedHashSet。

  3. TreeSet

    TreeSet 是基于红黑树实现的有序集合,它可以对元素进行排序,并提供了一些额外的操作,如获取子集和范围查找。由于元素需要进行排序,因此插入、删除和查找操作的性能可能比** HashSet 和 LinkedHashSet 稍慢**。如果需要对元素进行排序,那么可以选择使用 TreeSet。

    示例代码

import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;

public class SetExample {
    
    
    public static void main(String[] args) {
    
    
        // 使用 HashSet
        Set<String> hashSet = new HashSet<>();
        hashSet.add("apple");
        hashSet.add("banana");
        hashSet.add("cherry");
        hashSet.add("apple");
        System.out.println("HashSet: " + hashSet);

        // 使用 LinkedHashSet
        Set<String> linkedHashSet = new LinkedHashSet<>();
        linkedHashSet.add("apple");
        linkedHashSet.add("banana");
        linkedHashSet.add("cherry");
        linkedHashSet.add("apple");
        System.out.println("LinkedHashSet: " + linkedHashSet);

        // 使用 TreeSet
        Set<String> treeSet = new TreeSet<>();
        treeSet.add("apple");
        treeSet.add("banana");
        treeSet.add("cherry");
        treeSet.add("apple");
        System.out.println("TreeSet: " + treeSet);
    }
}

综上所述,可以根据具体的场景和需求来选择使用哪个集合类:

  • 如果不需要关心元素的顺序,而且需要快速地查找、添加和删除元素,那么可以选择使用 HashSet。
  • 如果需要按照元素的插入顺序来遍历集合,那么可以选择使用 LinkedHashSet。
  • 如果需要对元素进行排序,那么可以选择使用 TreeSet。

Map

注意事项和细节

  1. Map与Collection井列存在,⽤于保存具有映射关系的数据
  2. Map 中的key 和 value 可以是任何引⽤类型的数据,会封装到HashMap$Node对象中
  3. Map 中的key 不允许重复,原因和HashSet ⼀样
  4. Map 中的**value 可以重复 **
  5. Map 的key可以为null,value也可以为null,key为null只有能有⼀个,value为null可以为多个
  6. 常用String类作为Map的key
  7. key 和 value 之间存在单向⼀对⼀关系,即通过指定的 key 总能找到对应的 value 8. Map存放数据的key-value示意图,⼀对 k-y是放在⼀个Node中的,有因为Node 实现了 Entry 接口

在这里插入图片描述

常用方法

  • put:添加
  • remove:根据键删除映射关系
  • get:根据键获取值
  • size:获取元素个数
  • put:添加
  • remove:根据键删除映射关系
  • get:根据键获取值
  • size:获取元素个数

使用示例代码

import java.util.HashMap;
import java.util.Map;

public class MapExample {
    
    
    public static void main(String[] args) {
    
    
        // 创建 HashMap
        Map<String, Integer> map = new HashMap<>();

        // 添加键值对
        map.put("apple", 3);
        map.put("banana", 2);
        map.put("cherry", 5);

        // 获取键对应的值
        int appleCount = map.get("apple");
        System.out.println("The count of apple is " + appleCount);

        // 判断是否包含特定的键
        boolean hasBanana = map.containsKey("banana");
        System.out.println("Does the map contains banana? " + hasBanana);

        // 删除键值对
        map.remove("cherry");

        // 遍历键值对
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
    
    
            String key = entry.getKey();
            int value = entry.getValue();
            System.out.println(key + ": " + value);
        }
    }
}

在这个示例中,我们首先创建一个 HashMap 对象,然后使用 put() 方法添加三个键值对。我们使用 get() 方法获取键对应的值,使用 containsKey() 方法判断是否包含特定的键,使用 remove() 方法删除键值对。最后,我们使用 entrySet() 方法遍历所有的键值对,并打印出每个键和对应的值。

HashMap

注意事项和细节

  1. HashMap是Map 接⼝使⽤频率最⾼的实现类
  2. Hashap 是以 key-val 对的⽅式来存储数据(HashMap$Node类型)
  3. key 不能重复,但是值可以重复,允许使⽤null和null值
  4. 如果添加相同的key,则会覆盖原来的key-val ,等同于修改(key不会替换,val会替换)
  5. 与HashSet⼀样,不保证映射的顺序,因为底层是以hash表的⽅式来存储的(jdk8的hashMap 底层 数组 +链表+红⿊树)
  6. HashMap没有实现同步,因此是线程不安全的,方法没有做同步互斥的操作,没有synchronized

Hashtable

注意事项和细节

  1. 存放的元素是键值对:即K-V
  2. hashtable的键和值都不能为null, 否则会抛出NulPointerException
  3. hashTable 使用方法基本上和HashMap⼀样
  4. hashTable 是线程安全的(synchronized),hashMap 是线程不安全的

TreeMap

注意事项和细节

  1. 自然排序和自定义排序

    TreeMap 可以使用自然排序或自定义排序,如果使用自然排序,则键必须实现 Comparable 接口;如果使用自定义排序,则需要传入一个实现了 Comparator 接口的比较器对象。

  2. 键不能为 null

    TreeMap 的键不能为 null,否则会抛出 NullPointerException 异常。

  3. 线程不安全

    TreeMap 是非线程安全的,如果需要在多线程环境下使用,需要进行适当的同步处理。

  4. 性能

    由于 TreeMap 是基于红黑树实现的,因此它的插入、删除和查找操作的时间复杂度为 O(log n),比 HashMap 和 LinkedHashMap 稍慢。

  5. 遍历

    TreeMap 提供了多种遍历方式,包括按照键的自然顺序遍历、按照键的降序遍历、按照键的升序遍历等。

  6. subMap 方法

    TreeMap 提供了 subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) 方法,用于获取部分键值对的子映射。其中 fromKey 和 toKey 分别表示子映射的起始键和结束键,而 fromInclusive 和 toInclusive 表示是否包含起始键和结束键。

三者的异同与使用

异同点:

  • HashMap 和 Hashtable 都是基于哈希表实现的映射表,而 TreeMap 则是基于红黑树实现的有序映射表。
  • HashMap 允许键和值为 null,而 Hashtable 不允许键或值为 null。TreeMap 不允许键为 null,但允许值为 null。
  • HashMap 不是线程安全的,而 Hashtable 是线程安全的。TreeMap 不是线程安全的。
  • HashMap 的查询、插入和删除操作的时间复杂度为 O(1),而 TreeMap 的时间复杂度为 O(log n),其中 n 表示映射表中的元素个数。
  • HashMap 的键是无序的,而 TreeMap 的键是有序的。
  • HashMap 允许使用自定义对象作为键,需要实现 hashCode() 和 equals() 方法,而 TreeMap 需要使用实现了 Comparable 接口或传入比较器对象进行排序。

使用场景:

  • HashMap:适用于快速查找、添加和删除键值对的场景。由于 HashMap 的插入、删除和查找操作的时间复杂度为 O(1),因此在需要频繁进行插入、删除和查找操作的场景中,使用 HashMap 是非常合适的。例如,缓存系统、路由表等。
  • Hashtable:由于 Hashtable 是线程安全的,因此适用于多线程环境下需要频繁进行插入、删除和查找操作的场景。但由于其相对较慢的查询速度和不允许键或值为 null 的限制,Hashtable 的使用场景相对较少,可以使用 ConcurrentHashMap 替代。
  • TreeMap:适用于需要按照键的顺序进行排序的场景。由于 TreeMap 的键是有序的,因此可以使用 TreeMap 进行范围查找、排序等操作。例如,需要将元素按照键的顺序进行遍历、查找最大/小值等场景。但由于 TreeMap 的时间复杂度相对较慢,因此在需要频繁进行插入、删除和查找操作的场景中,使用 TreeMap 不是很合适。

总结

  • 先判断存储的类型(⼀组对象[单列]或⼀组键值对[双列])
  • ⼀组对象[单列]: Collection接⼝
    • 允许重复:List、
      • 增删多:LinkedList [底层维护双向链表]
        改查多:ArrayList [底层維护 Object类型的可变数组]
      • 不允许重复:Set
        ⽆序:HashSet [底层是HashMap,维护了⼀个哈希表,即(数组+链表+红⿊树)]
        排序:Treeset []
        插⼊和取出顺序⼀致:LinkedHashSet [底层维护数组+双向链表]
    • ⼀组键[值对双列]:Map
      • 键⽆序:HashMap [底层是:哈希表 jdk7:数组+链表,jdk8:数组+链表+红⿊树]
      • 键排序:TreeMap []
      • 键插入和取出顺序⼀致:LinkedHashMap
        在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/m0_60496161/article/details/130331098