泛型
泛型是 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)); // 输出: falseList 接口
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")); // 5LinkedList 类
LinkedList 是 Java 集合框架中的一种双向链表实现,属于 java.util 包。它实现了 List 接口,支持在列表任意位置插入和删除元素,且具有动态大小特性。
底层数据结构
内部通过节点(Node)实现,每个节点包含三个部分:
- 存储的元素值(item)
- 指向下一个节点的引用(next)
- 指向前一个节点的引用(prev)
这种双向结构使得在链表两端进行操作的时间复杂度为 O(1),而在中间插入或删除时需遍历到目标位置,时间复杂度为 O(n)。
与 ArrayList 的对比
| 特性 | LinkedList | ArrayList |
|---|---|---|
| 插入/删除性能 | 在任意位置插入删除较快(尤其在中间),但需遍历定位 | 在末尾插入删除快;中间插入删除慢(需移动后续元素) |
| 查询性能 | 较慢,需按索引顺序遍历 | 快,支持随机访问(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, second3. 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")); // null3. 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); // false5. containsValue(Object value)
判断Map中是否包含指定的值,返回布尔值。
boolean hasValue3 = map.containsValue(3); System.out.println(hasValue3); // true boolean hasValue5 = map.containsValue(5); System.out.println(hasValue5); // false6. isEmpty()
判断Map是否为空,即是否不包含任何键值对。
System.out.println(map.isEmpty()); // false map.clear(); System.out.println(map.isEmpty()); // true7. size()
返回Map中键值对的数量。
System.out.println(map.size()); // 1(在添加后)8. clear()
清空Map中所有键值对。
map.clear(); System.out.println(map.size()); // 09. 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要求列表必须已排序,否则结果不确定。