Java 中的 集合框架(Collection Framework)提供了多种数据结构和算法的实现,以支持对数据的存储、操作和管理。在 java.util 包下,Collection 是所有集合类的根接口。Collection 接口下有两个最常用的子接口:List 和 Set。这两个接口分别代表了有序集合无序集合,它们各自有不同的特点和应用场景。深入理解这两个接口,可以帮助我们在编程中选择合适的集合类型。


1. List 接口

1.1 List 的特点

  • 有序性List 接口继承自 Collection,它代表一个有序的集合。意味着元素的插入顺序是有保证的,且可以通过索引访问元素。
  • 允许重复List 中可以包含重复的元素。
  • 支持索引操作:可以使用索引来访问、更新、删除元素。

1.2 常见实现类

  • ArrayList:底层使用动态数组实现,支持快速随机访问元素,查找速度快,但插入和删除操作较慢(尤其是在数组中间插入/删除)。
  • LinkedList:底层使用双向链表实现,适合频繁的插入和删除操作,但随机访问性能较差。
  • Vector:和 ArrayList 类似,但它是线程安全的,效率较低,已经较少使用。
  • Stack:继承自 Vector,实现了栈(LIFO)数据结构。

1.3 常用方法

  • add(E e):将元素添加到 List 的末尾。
  • add(int index, E element):将元素插入到指定位置。
  • get(int index):获取指定索引位置的元素。
  • remove(int index):移除指定位置的元素。
  • set(int index, E element):用新元素替换指定位置的元素。
  • contains(Object o):判断 List 中是否包含某个元素。
  • indexOf(Object o):返回元素第一次出现的索引位置。
  • size():返回 List 中元素的数量。

1.4 应用场景

  • 需要保持插入顺序:例如,在处理需要顺序遍历或索引访问的数据时,List 是最合适的选择。
  • 需要处理重复元素List 可以允许重复元素。
  • 频繁的元素查找:如果你需要通过索引快速访问元素,可以使用 ArrayList

2. Set 接口

2.1 Set 的特点

  • 无序性Set 接口代表一个没有顺序的集合。即使我们添加元素的顺序是有规律的,但集合中的元素顺序可能会发生变化。
  • 不允许重复Set 中不允许有重复元素。当你试图添加一个已存在的元素时,add() 方法会返回 false
  • 不支持索引:因为 Set 是无序的,所以不能通过索引来访问元素,只能使用迭代器进行遍历。

2.2 常见实现类

  • HashSet:底层使用哈希表(HashMap),元素的插入顺序是无序的。HashSet 提供较好的性能(常数时间复杂度的查找、插入和删除),但不能保证元素的顺序。
  • LinkedHashSet:继承自 HashSet,底层使用双向链表,保证元素的插入顺序(即按插入顺序遍历),但性能略低于 HashSet
  • TreeSet:底层使用红黑树实现,元素是按自然顺序或指定的比较器进行排序的。适合需要对元素进行排序的场景。

2.3 常用方法

  • add(E e):将元素添加到 Set 中,若元素已存在则不会添加。
  • remove(Object o):移除指定元素。
  • contains(Object o):判断 Set 中是否包含指定元素。
  • size():返回 Set 中元素的数量。
  • clear():清空 Set 中的所有元素。
  • iterator():返回一个迭代器,用于遍历 Set 中的元素。

2.4 应用场景

  • 去重操作Set 最常见的应用场景是数据去重。例如,如果你需要去掉一个集合中的重复元素,可以使用 Set
  • 不关心元素顺序:如果你不关心元素的顺序,只关心是否包含某个元素,Set 是合适的选择。
  • 自然排序或自定义排序:如果你需要对元素进行排序,可以使用 TreeSet,它自动将元素按顺序排列。

3. List 和 Set 的对比

特性ListSet
有序性保证插入顺序(可以通过索引访问)无序,不保证插入顺序
允许重复允许重复元素不允许重复元素
实现类ArrayListLinkedListVector 等HashSetLinkedHashSetTreeSet
访问方式可以通过索引访问元素只能使用迭代器遍历元素
性能随机访问快速,但插入和删除可能较慢适合频繁查找,不关心顺序,插入与删除较快
应用场景需要保持元素的顺序、支持重复元素、快速访问不关心顺序、需要去重、不允许重复元素

4. 总结

  • List 适合需要元素顺序和重复元素的场景,尤其是当你需要通过索引访问和频繁查询时。
  • Set 适合需要去重、无顺序要求的场景,尤其在需要高效查找时使用 HashSet,需要排序时使用 TreeSet

掌握 List 和 Set 接口及其常见实现类的特点,有助于你在不同的开发场景中做出更合适的选择。

好的!这里是 List 和 Set 接口的一些常见操作的代码示例,帮助你更好地理解它们的使用方式。


1. List 接口的代码示例

1.1 ArrayList 示例

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

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

        // 向 List 中添加元素
        list.add("Apple");
        list.add("Banana");
        list.add("Cherry");
        list.add("Apple"); // 可以重复

        // 使用索引获取元素
        System.out.println("Element at index 1: " + list.get(1)); // 输出: Banana

        // 移除元素
        list.remove("Banana");
        System.out.println("After removing Banana: " + list);

        // 更新元素
        list.set(0, "Orange");
        System.out.println("After setting index 0 to Orange: " + list);

        // 判断是否包含某元素
        boolean containsApple = list.contains("Apple");
        System.out.println("Contains Apple? " + containsApple); // 输出: true

        // 获取元素的索引
        int index = list.indexOf("Orange");
        System.out.println("Index of Orange: " + index); // 输出: 0

        // 打印 List
        System.out.println("List: " + list);
    }
}

1.2 LinkedList 示例

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

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

        // 向 List 中添加元素
        list.add("John");
        list.add("Jane");
        list.add("Jack");

        // 使用迭代器遍历
        for (String name : list) {
            System.out.println(name);
        }

        // 在指定位置插入元素
        list.add(1, "Doe");
        System.out.println("After inserting at index 1: " + list);

        // 删除指定元素
        list.remove("Jack");
        System.out.println("After removing Jack: " + list);
    }
}

2. Set 接口的代码示例

2.1 HashSet 示例

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

public class HashSetExample {
    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
        System.out.println("Set: " + set);

        // 检查元素是否存在
        boolean containsApple = set.contains("Apple");
        System.out.println("Contains Apple? " + containsApple); // 输出: true

        // 删除元素
        set.remove("Banana");
        System.out.println("After removing Banana: " + set);

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

        // 清空 Set
        set.clear();
        System.out.println("After clearing: " + set);
    }
}

2.2 LinkedHashSet 示例

import java.util.LinkedHashSet;
import java.util.Set;

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

        // 向 Set 中添加元素
        set.add("Apple");
        set.add("Banana");
        set.add("Cherry");
        set.add("Apple"); // 不会重复添加

        // 打印 Set(插入顺序会被保留)
        System.out.println("LinkedHashSet: " + set);

        // 删除指定元素
        set.remove("Banana");
        System.out.println("After removing Banana: " + set);

        // 获取元素个数
        System.out.println("Size: " + set.size());
    }
}

2.3 TreeSet 示例

import java.util.Set;
import java.util.TreeSet;

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

        // 向 Set 中添加元素(自动按自然顺序排序)
        set.add("Banana");
        set.add("Apple");
        set.add("Cherry");

        // 打印 Set(元素会自动排序)
        System.out.println("TreeSet: " + set);

        // 删除指定元素
        set.remove("Apple");
        System.out.println("After removing Apple: " + set);

        // 检查是否包含某元素
        boolean containsCherry = set.contains("Cherry");
        System.out.println("Contains Cherry? " + containsCherry); // 输出: true

        // 获取 Set 中的元素个数
        System.out.println("Size of TreeSet: " + set.size());
    }
}

3. List 和 Set 的对比

3.1 List 中允许重复元素

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

public class ListDuplicateExample {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Apple");
        list.add("Banana");
        list.add("Apple");  // 重复元素
        System.out.println("List with duplicates: " + list);
    }
}

3.2 Set 不允许重复元素

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

public class SetDuplicateExample {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        set.add("Apple");
        set.add("Banana");
        set.add("Apple");  // 重复元素不会添加
        System.out.println("Set without duplicates: " + set);
    }
}

4. 总结

  • List:支持元素重复,允许通过索引访问,适合需要顺序和索引操作的场景。
  • Set:不允许重复元素,元素无序(但 LinkedHashSet 可以保持插入顺序),适合需要去重和快速查找的场景。

通过上述代码示例,你可以看到 List 和 Set 的具体用法以及它们的区别。根据你的需求选择适合的集合类型,能让你的代码更加高效和清晰。