ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

07容器和数据结构

07容器和数据结构

泛型

泛型是 Java 5 引入的一种机制,允许在定义类、接口和方法时使用类型参数。通过泛型,可以在编译时检查类型安全,并且消除强制类型转换的需要。

泛型类

使用前

public class TestGeneric { public static void main(String[] args) { MyCollection mc = new MyCollection(); mc.set("齐", 0); mc.set(888, 1); Integer a = (Integer) mc.get(1); System.out.println(a); // 888 String b = (String) mc.get(0); System.out.println(b); // 齐 } } class MyCollection { private Object[] objs = new Object[5]; public void set(Object obj, int index) { objs[index] = obj; } public Object get(int index) { return objs[index]; } }

使用后

public class TestGeneric { public static void main(String[] args) { MyCollection<String> mc = new MyCollection<>(); mc.set("齐", 0); String b = mc.get(0); System.out.println(b); // 齐 } } class MyCollection<E> { private Object[] objs = new Object[5]; public void set(E e, int index) { objs[index] = e; } @SuppressWarnings("unchecked") public E get(int index) { return (E) objs[index]; } }
  • 定义一个泛型类时,使用尖括号<E,T,V>声明类型参数。
  • 使用该泛型类时,可以指定具体的类型
  • 不能使用基本类型作为泛型参数:如Box<int>是非法的,应使用包装类Integer。

泛型方法

泛型方法允许在方法签名中使用类型参数。

public static <T> void print(T item) { System.out.println(item); }

调用时可自动推断类型:

print("Hello"); // T 被推断为 String print(42); // T 被推断为 Integer

泛型接口

public interface Comparable<T> { int compareTo(T other); }

实现该接口时需指定具体类型:

public class Person implements Comparable<Person> { private String name; public Person(String name) { this.name = name; } @Override public int compareTo(Person other) { return this.name.compareTo(other.name); } }

集合类

Java集合框架提供了一套用于存储和操作一组对象的标准接口与实现类。这些类位于java.util包中,是Java编程中处理数据集合的核心工具。

核心接口

  • Collection:所有集合类的根接口,定义了基本的集合操作,如添加、删除、遍历等。
  • List:有序集合,允许重复元素,支持通过索引访问元素。
  • Set:不包含重复元素的集合,无序(部分实现如 TreeSet 保持排序)。
  • Map:键值对映射结构,不包含重复键,每个键对应一个值。

Collection接口

Collection接口是Java集合框架的根接口,位于java.util包中,定义了所有集合类的基本行为和操作。它为存储和操作一组对象提供了统一的抽象,是List、Set具体集合类型的共同父接口,所以方法是通用的。

Collection接口的子接口

List接口

  • 特点:有序、可重复。
  • 实现类:ArrayList、LinkedList、Vector(基本被替代)。
  • 支持通过索引访问元素,允许插入重复值。

Set接口

  • 特点:无序、不可重复。
  • 实现类:HashSet、LinkedHashSet、TreeSet。
  • 基于哈希表或红黑树实现,保证元素唯一性。

常用方法

  • add(E element)
    将指定元素添加到集合中,如果集合不接受该元素则抛出异常。返回值为布尔类型,表示是否成功添加。

  • addAll(Collection<? extends E> c)
    将指定集合中的所有元素添加到当前集合中。若集合支持批量操作,则返回true表示至少有一个元素被添加。

  • clear()
    移除集合中的所有元素,使集合变为空。

  • contains(Object obj)
    判断集合是否包含指定元素,使用equals()方法进行比较。

  • containsAll(Collection<?> c)
    判断集合是否包含指定集合中的所有元素。

  • equals(Object o)
    比较当前集合与另一个对象是否相等,基于元素内容和顺序(某些实现如 List 会考虑顺序)。

  • hashCode()
    返回集合的哈希码值,用于在哈希表结构中定位。

  • isEmpty()
    判断集合是否为空,即不包含任何元素。

  • iterator()
    返回一个迭代器,用于遍历集合中的元素。

  • remove(Object o)
    移除集合中第一个匹配指定元素的实例,若存在则返回true,否则返回false。

  • removeAll(Collection<?> c)
    移除当前集合中与指定集合中所有元素相同的元素。

  • retainAll(Collection<?> c)
    保留当前集合中与指定集合共有的元素,移除其他元素。

  • size()
    返回集合中元素的数量。

  • toArray()
    将集合转换为一个数组,返回类型为Object[]。

  • toArray(T[] a)
    将集合转换为指定类型的数组,若数组长度不足则自动扩容,若过长则截断。

示例

public class TestList { public static void main(String[] args) { Collection<String> c = new ArrayList<>(); System.out.println(c.size()); // 0 System.out.println(c.isEmpty()); // true c.add("张三"); c.add("李四"); System.out.println(c); // [张三, 李四] System.out.println(c.size()); // 2 System.out.println(c.contains("王五")); // false Object[] objs = c.toArray(); System.out.println(objs); // [Ljava.lang.Object;@f6f4d33 c.remove("李四"); System.out.println(c); // [张三] c.clear(); System.out.println(c.size()); // 0 } }

添加元素到列表

List<String> list01 = new ArrayList<>(Arrays.asList("aa", "bb", "cc")); List<String> list02 = new ArrayList<>(Arrays.asList("aa", "dd", "ee")); list01.addAll(list02); System.out.println("list01:" + list01); // 输出: list01:[aa, bb, cc, aa, dd, ee]

移除列表中所有与指定列表相同的元素

list01.removeAll(list02); System.out.println("list01:" + list01); // 输出: list01:[bb, cc]

保留两个列表共有的元素

list01.retainAll(list02); System.out.println("list01:" + list01); // 输出: list01:[aa]

判断一个列表是否包含另一个列表的所有元素

System.out.println(list01.containsAll(list02)); // 输出: false

List 接口

List 是 Java 集合框架中的一个重要接口,位于java.util包中。它继承自Collection接口,用于存储有序的、可重复的元素集合。与 Set 不同,List 允许元素重复,并且每个元素都有明确的位置索引(从 0 开始)。

核心特性

  • 有序性:元素按照插入顺序进行存储,可以通过索引访问。
  • 允许重复:同一个值可以多次出现在 List 中。
  • 支持索引操作:可通过下标快速定位和修改元素。
  • 可变大小:长度可动态调整。

常见实现类

实现类特点
ArrayList基于动态数组实现,查询快,增删慢(尤其在中间插入/删除时),非线程安全。
LinkedList基于双向链表实现,插入和删除效率高,但随机访问较慢,非线程安全。
Vector线程安全的动态数组,性能较低,已逐渐被ArrayList取代。
Stack继承自Vector,实现后进先出(LIFO)的数据结构,不推荐用于一般场景。

常用方法

List接口继承了Collection接口,因此包含了Collection接口的所有方法。此外List接口还定义了两种方法:

  • get(index):获得指定索引位置的元素
  • set(int index,Object obj):将集合中指定索引位置的对象修改为指定对象

ArrayList类

通过ArrayList类可以实例化List接口

List<E> list = new ArrayList<>();
List<String> list = new ArrayList<>(); list.add("A"); list.add("B"); list.add("C"); list.add("D"); System.out.println(list); // [A, B, C, D] list.add(2, "飞"); System.out.println(list); // [A, B, 飞, C, D] list.remove(2); System.out.println(list); // [A, B, C, D] list.set(2, "飞"); System.out.println(list); // [A, B, 飞, D] System.out.println(list.get(2)); // 飞 list.set(3, "你"); System.out.println(list); // [A, B, 飞, 你] list.add("C"); list.add("B"); list.add("A"); System.out.println(list); // [A, B, 飞, 你, C, B, A] System.out.println(list.indexOf("B")); // 1 System.out.println(list.lastIndexOf("B")); // 5

LinkedList 类

LinkedList 是 Java 集合框架中的一种双向链表实现,属于 java.util 包。它实现了 List 接口,支持在列表任意位置插入和删除元素,且具有动态大小特性。

底层数据结构

内部通过节点(Node)实现,每个节点包含三个部分:

  • 存储的元素值(item)
  • 指向下一个节点的引用(next)
  • 指向前一个节点的引用(prev)

这种双向结构使得在链表两端进行操作的时间复杂度为 O(1),而在中间插入或删除时需遍历到目标位置,时间复杂度为 O(n)。

与 ArrayList 的对比

特性LinkedListArrayList
插入/删除性能在任意位置插入删除较快(尤其在中间),但需遍历定位在末尾插入删除快;中间插入删除慢(需移动后续元素)
查询性能较慢,需按索引顺序遍历快,支持随机访问(O(1))
内存开销每个元素额外占用指针空间(前后指针)仅存储元素本身,内存紧凑
适用场景频繁插入删除、非频繁随机访问频繁读取、较少插入删除

LinkedList 特有方法

除了继承自List接口的方法外,LinkedList还提供了额外的特有方法,主要用于支持其作为双向链表的数据结构特性。这些方法在操作链表节点时具有更高的灵活性和效率。

添加与删除节点的特有方法
  • addFirst(E e):将指定元素插入到链表的开头。
  • addLast(E e):将指定元素插入到链表的末尾。
  • removeFirst():移除并返回链表的第一个元素。
  • removeLast():移除并返回链表的最后一个元素。
  • pollFirst():移除并返回链表的第一个元素,若链表为空则返回null。
  • pollLast():移除并返回链表的最后一个元素,若链表为空则返回null。
  • removeFirstOccurrence(Object o):移除链表中第一次出现的指定元素。
  • removeLastOccurrence(Object o):移除链表中最后一次出现的指定元素。
获取与访问节点的特有方法
  • getFirst():返回链表的第一个元素,若链表为空则抛出NoSuchElementException。
  • getLast():返回链表的最后一个元素,若链表为空则抛出NoSuchElementException。
  • peekFirst():返回链表的第一个元素,若链表为空则返回null。
  • peekLast():返回链表的最后一个元素,若链表为空则返回null。
双向遍历与栈/队列行为支持
  • descendingIterator():返回一个逆序迭代器,用于从后往前遍历链表。
  • 支持作为栈使用:通过push(E e)(压入栈顶)和pop()(弹出栈顶)实现栈行为。
  • 支持作为队列使用:通过offer,poll,peek等方法实现队列行为。

Set 接口

Set 是 Java 集合框架中的一个重要接口,继承自 Collection 接口。它用于存储不包含重复元素的唯一值集合,即集合中的每个元素都是唯一的,不允许出现重复项。

核心特性

  • 无序性:Set 中的元素没有固定的顺序,插入顺序不一定与遍历顺序一致。
  • 唯一性:任意两个元素通过 equals() 方法比较时必须返回 false,否则视为重复元素。
  • 不支持索引访问:由于无序性,无法通过下标访问元素。

常见实现类

1. HashSet

  • 基于哈希表实现,底层使用 HashMap。
  • 元素的存取速度快,平均时间复杂度为 O(1)。
  • 元素无序,不保证迭代顺序。
  • 允许 null 值。
  • 要求元素重写 equals() 与 hashCode() 方法以确保唯一性。
Set<String> set = new HashSet<>(); set.add("apple"); set.add("banana"); set.add("apple"); // 重复添加,不会生效

2. LinkedHashSet

  • 继承自 HashSet,但维护一个双向链表来记录插入顺序。
  • 插入顺序与遍历顺序一致。
  • 性能略低于 HashSet,但可预测遍历顺序。
  • 适用于需要保持插入顺序且去重的场景。
Set<String> linkedSet = new LinkedHashSet<>(); linkedSet.add("first"); linkedSet.add("second"); linkedSet.add("first"); // 重复,忽略 // 遍历时输出顺序为 first, second

3. TreeSet

  • 基于红黑树(平衡二叉搜索树)实现。
  • 元素按自然排序或自定义比较器排序。
  • 支持有序遍历,提供排序功能。
  • 不允许 null 值(除非比较器允许)。
  • 插入、删除、查找操作的时间复杂度为 O(log n)。
Set<Integer> treeSet = new TreeSet<>(); treeSet.add(5); treeSet.add(2); treeSet.add(8); // 遍历输出:2, 5, 8
  • 若只需快速去重且不关心顺序 → 使用HashSet。
  • 若需保持插入顺序 → 使用LinkedHashSet。
  • 若需要自动排序或范围查询 → 使用TreeSet。

主要方法

方法说明
boolean add(E e)向集合中添加元素,若已存在则返回 false
boolean remove(Object o)移除指定元素,若存在则返回 true
boolean contains(Object o)判断是否包含指定元素
int size()返回集合中元素个数
boolean isEmpty()判断集合是否为空
Iterator<E> iterator()返回迭代器,用于遍历集合
void clear()清空集合
  • 所有实现类都要求放入的对象正确重写equals()与hashCode()(尤其是使用 HashSet 时)。
  • 在 TreeSet 中,对象必须实现 Comparable 接口,或传入 Comparator。
  • 避免在遍历时修改集合结构,可能导致 ConcurrentModificationException。

Map 接口

Map 接口是 Java 集合框架中的核心接口之一,用于存储键(key)——值(value)映射关系。每个键对应一个唯一的值,键不允许重复,但值可以重复。Map 不继承自 Collection 接口,因此它不支持集合的通用操作如迭代器遍历(除非通过 keySet()、values() 或 entrySet() 获取视图)。

核心特性

  • 键唯一性:同一个键只能对应一个值,若插入重复键,新值会覆盖旧值。
  • 支持 null 键和 null 值:大多数实现允许键或值为 null,但具体行为取决于实现类。
  • 无序性:默认情况下,Map 的元素不保证顺序,除非使用有序实现类(如 TreeMap、LinkedHashMap)。
  • 通用操作:提供获取、添加、删除、判断是否存在等方法。

常用方法

1. put(K key, V value)

将指定的键值对插入到Map中,若键已存在则替换其对应的值。

Map<String, Integer> map = new HashMap<>(); map.put("apple", 1); map.put("banana", 2); map.put("apple", 3); // 替换原有值 System.out.println(map); // {apple=3, banana=2}
2. get(Object key)

根据键获取对应的值,若键不存在则返回null。

Integer value = map.get("apple"); System.out.println(value); // 3 System.out.println(map.get("orange")); // null
3. remove(Object key)

移除指定键及其对应的值,若键不存在则无操作。

map.remove("banana"); System.out.println(map); // {apple=3}
4. containsKey(Object key)

判断Map中是否包含指定的键,返回布尔值。

boolean hasApple = map.containsKey("apple"); System.out.println(hasApple); // true boolean hasOrange = map.containsKey("orange"); System.out.println(hasOrange); // false
5. containsValue(Object value)

判断Map中是否包含指定的值,返回布尔值。

boolean hasValue3 = map.containsValue(3); System.out.println(hasValue3); // true boolean hasValue5 = map.containsValue(5); System.out.println(hasValue5); // false
6. isEmpty()

判断Map是否为空,即是否不包含任何键值对。

System.out.println(map.isEmpty()); // false map.clear(); System.out.println(map.isEmpty()); // true
7. size()

返回Map中键值对的数量。

System.out.println(map.size()); // 1(在添加后)
8. clear()

清空Map中所有键值对。

map.clear(); System.out.println(map.size()); // 0
9. keySet()

返回Map中所有键的集合视图,可用于遍历键。

Set<String> keys = map.keySet(); for (String key : keys) { System.out.println(key + ": " + map.get(key)); }
10. values()

返回Map中所有值的集合视图,可用于遍历值。

Collection<Integer> values = map.values(); for (Integer value : values) { System.out.println(value); }
11. entrySet()

返回Map中所有键值对的集合视图,常用于遍历键值对。

Set<Map.Entry<String, Integer>> entries = map.entrySet(); for (Map.Entry<String, Integer> entry : entries) { System.out.println(entry.getKey() + " -> " + entry.getValue()); }

常见实现类

1. HashMap
  • 基于哈希表实现,不保证顺序。
  • 允许键和值为 null。
  • 平均时间复杂度:插入、查找、删除为 O(1)。
  • 线程不安全,多线程环境下需手动同步或使用 ConcurrentHashMap。
3. TreeMap
  • 基于红黑树实现,按键的自然顺序或自定义比较器排序。
    • 键必须实现 Comparable 接口,或提供 Comparator。`
  • 时间复杂度:插入、查找、删除为 O(log n)。
  • 提供范围查询功能,如firstKey()、lastKey()、subMap()。
  • 不允许键对象为null。
  • 键对象应正确重写equals()和hashCode()方法,否则可能导致无法正确查找或存储。
  • 在循环中修改 Map 时,应使用迭代器或entrySet()遍历,避免直接修改导致ConcurrentModificationException。
  • 多线程环境下应优先选择线程安全的实现类,如ConcurrentHashMap。

迭代器

迭代器(Iterator)是 Java 集合框架中用于遍历集合元素的接口。它提供了一种统一的方式来访问集合中的每个元素,而无需暴露底层数据结构。

Iterator 接口的主要方法

  • boolean hasNext():判断是否还有下一个元素。
  • E next():返回下一个元素,并移动到下一个位置。
  • void remove():移除上一次调用next()返回的元素。

使用 Iterator 遍历集合

import java.util.*; List<String> list = Arrays.asList("apple", "banana", "cherry"); Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String item = iterator.next(); System.out.println(item); }

在 for-each 循环中使用迭代器

虽然 for-each 循环内部也使用了迭代器,但开发者通常不需要显式调用。若需在遍历时进行删除操作,必须使用迭代器的remove()方法。

安全删除元素

直接在遍历过程中调用集合的remove()会抛出ConcurrentModificationException。正确做法是使用迭代器的remove():

List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c", "d")); Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String item = iterator.next(); if (item.equals("b")) { iterator.remove(); // 安全删除 } }

遍历 Map 集合

由于Map不直接实现Iterable,需要通过entrySet()获取键值对集合,再使用迭代器:

Map<String, Integer> map = new HashMap<>(); map.put("one", 1); map.put("two", 2); map.put("three", 3); Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<String, Integer> entry = iterator.next(); System.out.println(entry.getKey() + " : " + entry.getValue()); }
  • 迭代器只能被使用一次,多次调用iterator()会创建新的迭代器实例。
  • 若在迭代过程中集合被结构性修改(如添加或删除元素),且未通过迭代器自身的方法操作,则会触发ConcurrentModificationException。
  • 推荐在遍历集合时始终使用迭代器进行增删操作,以保证线程安全与一致性。

Collection工具类

Java中的Collections工具类位于java.util包下,是集合框架的重要组成部分。它提供了一系列静态方法,用于操作或返回集合,如排序、查找、同步、反转、填充等。该工具类不包含任何构造函数,所有方法均为静态方法,可直接通过类名调用。

常用方法分类与说明

1. 排序相关方法

  • sort(List<T> list):对指定列表按自然顺序升序排序。
  • sort(List<T> list, Comparator<? super T> c):使用指定比较器对列表进行排序。
  • 示例:
    List<Integer> numbers = Arrays.asList(5, 2, 8, 1); Collections.sort(numbers); // [1, 2, 5, 8]

2. 查找与搜索方法

  • binarySearch(List<? extends Comparable<? super T>> list, T key):在已排序的列表中使用二分查找法查找指定元素,返回索引。
  • binarySearch(List<? extends Comparable<? super T>> list, T key, Comparator<? super T> c):带自定义比较器的二分查找。
  • max(Collection<? extends T> coll):返回集合中的最大元素。
  • min(Collection<? extends T> coll):返回集合中的最小元素。
  • 示例:
    List<Integer> list = Arrays.asList(3, 1, 4, 1, 5); Collections.sort(list); int index = Collections.binarySearch(list, 4); // 返回 2

3. 集合操作方法

  • reverse(List<?> list):反转列表中元素的顺序。
  • shuffle(List<?> list):随机打乱列表中元素的顺序。
  • rotate(List<?> list, int distance):将列表循环移动指定距离。
  • fill(List<? super T> list, T obj):将列表中所有元素替换为指定值。
  • 示例:
    List<String> names = Arrays.asList("A", "B", "C"); Collections.reverse(names); // ["C", "B", "A"] Collections.shuffle(names); // 随机排列 Collections.fill(names, "X"); // 全部变为 "X"

4. 同步与不可变集合

  • synchronizedList(List<T> list):返回一个线程安全的列表包装器。
  • synchronizedSet(Set<T> set):返回一个线程安全的集合包装器。
  • synchronizedMap(Map<K,V> map):返回一个线程安全的映射包装器。
  • unmodifiableList(List<? extends T> list):返回一个不可修改的列表视图。
  • unmodifiableSet(Set<? extends T> set):返回一个不可修改的集合视图。
  • unmodifiableMap(Map<? extends K,? extends V> m):返回一个不可修改的映射视图。
  • 示例:
    List<String> syncList = Collections.synchronizedList(new ArrayList<>()); List<String> unmodList = Collections.unmodifiableList(Arrays.asList("a", "b"));

5. 便利方法

  • emptyList():返回空的不可变列表。
  • emptySet():返回空的不可变集合。
  • emptyMap():返回空的不可变映射。
  • singletonList(T o):返回只包含一个元素的不可变列表。
  • singletonSet(T o):返回只包含一个元素的不可变集合。
  • singletonMap(K key, V value):返回只包含一个键值对的不可变映射。
  • 示例:
    List<String> empty = Collections.emptyList(); Set<String> single = Collections.singleton("hello"); Map<String, Integer> map = Collections.singletonMap("key", 100);

6. 交换与复制

  • swap(List<?> list, int i, int j):交换列表中两个位置的元素。
  • copy(List<? super T> dest, List<? extends T> src):将源列表的内容复制到目标列表中,要求目标列表长度至少等于源列表。
  • 示例:
    List<Integer> nums = Arrays.asList(1, 2, 3, 4); Collections.swap(nums, 0, 3); // [4, 2, 3, 1] List<Integer> dest = new ArrayList<>(4); Collections.copy(dest, nums); // 复制内容
  • 所有不可变集合方法(如unmodifiable*)返回的是原始集合的视图,若原始集合被修改,视图也会随之变化。
  • 使用unmodifiable集合时,若尝试修改会抛出UnsupportedOperationException。
  • synchronized方法返回的包装器仅保证单个操作的线程安全,多个连续操作仍需外部同步。
  • binarySearch要求列表必须已排序,否则结果不确定。
返回列表