MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Java集合框架入门

2021-10-256.3k 阅读

Java集合框架概述

Java集合框架(Java Collection Framework)是Java提供的一组用于存储和操作对象集合的类和接口。它提供了统一的方式来处理各种类型的集合,使得开发人员能够高效地管理和操作数据。集合框架的设计目标是提供一种灵活、高效且易于使用的数据结构和算法体系。

集合框架主要包含两个接口体系:Collection接口和Map接口。Collection接口是集合层次结构中的根接口,它定义了集合操作的基本方法,如添加元素、删除元素、查询元素等。而Map接口则用于存储键值对,提供了根据键来快速查找值的功能。

集合框架的优势

  1. 提高代码复用性:通过使用集合框架提供的标准接口和实现类,开发人员可以避免重复编写数据结构和算法相关的代码。例如,在需要使用列表数据结构时,直接使用ArrayListLinkedList,而无需自己实现一个列表。
  2. 增强代码可读性:集合框架提供了清晰、易懂的接口和方法命名,使得代码更易于理解和维护。例如,使用add()方法添加元素,remove()方法删除元素,从方法名就可以清楚地知道其功能。
  3. 提高性能:集合框架的实现类经过精心优化,在不同的应用场景下能够提供高效的性能。例如,ArrayList适合随机访问,而LinkedList适合频繁的插入和删除操作。

Collection接口

Collection接口是Java集合框架中最基本的接口,它定义了一组用于操作集合的方法。所有具体的集合类,如ListSet等,都直接或间接实现了Collection接口。

Collection接口的主要方法

  1. 添加元素
    • boolean add(E e):将指定元素添加到集合中。如果集合因为此操作而发生改变,则返回true;否则返回false。例如:
import java.util.ArrayList;
import java.util.Collection;

public class CollectionExample {
    public static void main(String[] args) {
        Collection<String> collection = new ArrayList<>();
        boolean result = collection.add("Hello");
        System.out.println("添加结果: " + result);
    }
}
- `boolean addAll(Collection<? extends E> c)`:将指定集合中的所有元素添加到当前集合中。如果当前集合因为此操作而发生改变,则返回`true`;否则返回`false`。

2. 删除元素: - boolean remove(Object o):从集合中移除指定元素。如果集合中存在该元素并成功移除,则返回true;否则返回false。 - boolean removeAll(Collection<?> c):从集合中移除指定集合中包含的所有元素。如果当前集合因为此操作而发生改变,则返回true;否则返回false。 3. 查询元素: - boolean contains(Object o):判断集合中是否包含指定元素。如果集合包含该元素,则返回true;否则返回false。 - boolean containsAll(Collection<?> c):判断集合中是否包含指定集合中的所有元素。如果包含,则返回true;否则返回false。 4. 其他方法: - int size():返回集合中元素的数量。 - void clear():移除集合中的所有元素,使集合变为空集合。 - boolean isEmpty():判断集合是否为空。如果集合不包含任何元素,则返回true;否则返回false。 - Object[] toArray():返回一个包含集合中所有元素的数组。

List接口

List接口继承自Collection接口,它代表一个有序的集合,允许存储重复的元素。List中的元素有明确的顺序,可以通过索引访问元素。

List接口的主要方法

  1. 根据索引操作元素
    • E get(int index):返回指定索引位置的元素。例如:
import java.util.ArrayList;
import java.util.List;

public class ListExample {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Apple");
        list.add("Banana");
        String element = list.get(1);
        System.out.println("索引1处的元素: " + element);
    }
}
- `E set(int index, E element)`:用指定元素替换指定索引位置的元素,并返回被替换的元素。
- `void add(int index, E element)`:将指定元素插入到指定索引位置。
- `E remove(int index)`:移除指定索引位置的元素,并返回被移除的元素。

2. 查找元素索引: - int indexOf(Object o):返回指定元素在列表中第一次出现的索引;如果列表不包含该元素,则返回 -1。 - int lastIndexOf(Object o):返回指定元素在列表中最后一次出现的索引;如果列表不包含该元素,则返回 -1。

List接口的实现类

  1. ArrayListArrayListList接口的动态数组实现。它允许快速随机访问元素,因为其内部使用数组来存储元素。在添加和删除元素时,如果涉及到数组扩容或元素移动,性能会受到一定影响。例如:
import java.util.ArrayList;
import java.util.List;

public class ArrayListExample {
    public static void main(String[] args) {
        List<Integer> arrayList = new ArrayList<>();
        arrayList.add(10);
        arrayList.add(20);
        System.out.println("ArrayList: " + arrayList);
    }
}
  1. LinkedListLinkedListList接口的链表实现。它在插入和删除元素时性能较好,因为不需要移动大量元素,只需要修改链表的指针。但随机访问元素的性能相对较差,因为需要从链表头或尾开始遍历。例如:
import java.util.LinkedList;
import java.util.List;

public class LinkedListExample {
    public static void main(String[] args) {
        List<String> linkedList = new LinkedList<>();
        linkedList.add("One");
        linkedList.add("Two");
        System.out.println("LinkedList: " + linkedList);
    }
}

Set接口

Set接口也继承自Collection接口,它代表一个无序且不允许存储重复元素的集合。当试图向Set中添加重复元素时,添加操作将失败。

Set接口的主要方法

Set接口继承了Collection接口的所有方法,没有额外定义新的方法。但由于Set的特性,其实现类在添加重复元素时会有不同的行为。例如:

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

public class SetExample {
    public static void main(String[] args) {
        Set<Integer> set = new HashSet<>();
        set.add(10);
        set.add(20);
        boolean result = set.add(10);
        System.out.println("添加重复元素结果: " + result);
    }
}

Set接口的实现类

  1. HashSetHashSetSet接口的常用实现类,它基于哈希表实现。HashSet中的元素无序,并且通过元素的哈希码来确定元素的存储位置,以提高查找和插入的效率。例如:
import java.util.HashSet;
import java.util.Set;

public class HashSetExample {
    public static void main(String[] args) {
        Set<String> hashSet = new HashSet<>();
        hashSet.add("Red");
        hashSet.add("Green");
        System.out.println("HashSet: " + hashSet);
    }
}
  1. TreeSetTreeSetSet接口的另一个实现类,它基于红黑树实现。TreeSet中的元素是有序的(默认按照自然顺序排序,也可以通过传入自定义的比较器来指定排序方式)。例如:
import java.util.TreeSet;
import java.util.Set;

public class TreeSetExample {
    public static void main(String[] args) {
        Set<Integer> treeSet = new TreeSet<>();
        treeSet.add(30);
        treeSet.add(10);
        treeSet.add(20);
        System.out.println("TreeSet: " + treeSet);
    }
}

Map接口

Map接口用于存储键值对(key - value pairs),它提供了根据键来快速查找值的功能。一个Map中不能包含重复的键,每个键最多映射到一个值。

Map接口的主要方法

  1. 添加和替换键值对
    • V put(K key, V value):将指定的键值对存储到Map中。如果Map中已存在该键,则替换其对应的值,并返回旧值;如果不存在,则返回null。例如:
import java.util.HashMap;
import java.util.Map;

public class MapExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        Integer oldValue = map.put("One", 1);
        System.out.println("旧值: " + oldValue);
    }
}
- `void putAll(Map<? extends K,? extends V> m)`:将指定`Map`中的所有键值对复制到当前`Map`中。

2. 获取值: - V get(Object key):返回指定键所映射的值;如果Map中不包含该键的映射,则返回null。 - boolean containsKey(Object key):判断Map中是否包含指定的键。 - boolean containsValue(Object value):判断Map中是否包含指定的值。 3. 删除键值对: - V remove(Object key):移除指定键的键值对,并返回其对应的值;如果Map中不包含该键的映射,则返回null。 4. 其他方法: - int size():返回Map中键值对的数量。 - void clear():移除Map中的所有键值对。 - boolean isEmpty():判断Map是否为空。

Map接口的实现类

  1. HashMapHashMapMap接口的常用实现类,它基于哈希表实现。HashMap允许null键和null值,并且其键值对是无序的。例如:
import java.util.HashMap;
import java.util.Map;

public class HashMapExample {
    public static void main(String[] args) {
        Map<String, String> hashMap = new HashMap<>();
        hashMap.put("Name", "John");
        hashMap.put("Age", "30");
        System.out.println("HashMap: " + hashMap);
    }
}
  1. TreeMapTreeMapMap接口的另一个实现类,它基于红黑树实现。TreeMap中的键是有序的(默认按照自然顺序排序,也可以通过传入自定义的比较器来指定排序方式),不允许null键。例如:
import java.util.TreeMap;
import java.util.Map;

public class TreeMapExample {
    public static void main(String[] args) {
        Map<Integer, String> treeMap = new TreeMap<>();
        treeMap.put(3, "Three");
        treeMap.put(1, "One");
        treeMap.put(2, "Two");
        System.out.println("TreeMap: " + treeMap);
    }
}
  1. LinkedHashMapLinkedHashMap继承自HashMap,它维护插入顺序或访问顺序。如果按照插入顺序,元素将按照插入的先后顺序排列;如果按照访问顺序,最近访问的元素将被移到链表末尾。例如:
import java.util.LinkedHashMap;
import java.util.Map;

public class LinkedHashMapExample {
    public static void main(String[] args) {
        Map<String, Integer> linkedHashMap = new LinkedHashMap<>(16, 0.75f, true);
        linkedHashMap.put("A", 1);
        linkedHashMap.put("B", 2);
        linkedHashMap.get("A");
        System.out.println("LinkedHashMap: " + linkedHashMap);
    }
}

迭代集合

在Java集合框架中,经常需要遍历集合中的元素。有多种方式可以实现集合的迭代。

使用Iterator接口

Iterator接口是Java集合框架中用于遍历集合元素的接口。所有实现了Collection接口的集合类都可以通过iterator()方法获取一个Iterator对象。例如:

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class IteratorExample {
    public static void main(String[] args) {
        Collection<String> collection = new ArrayList<>();
        collection.add("Apple");
        collection.add("Banana");
        Iterator<String> iterator = collection.iterator();
        while (iterator.hasNext()) {
            String element = iterator.next();
            System.out.println(element);
        }
    }
}

使用增强for循环

增强for循环(也称为for - each循环)是Java 5.0引入的一种更简洁的遍历集合的方式。它适用于实现了Iterable接口的集合类,包括Collection接口的所有实现类。例如:

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

public class ForEachExample {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(10);
        list.add(20);
        for (Integer element : list) {
            System.out.println(element);
        }
    }
}

使用Stream API

Java 8引入了Stream API,它提供了一种更函数式的方式来处理集合。可以将集合转换为流,然后进行各种操作,如过滤、映射、归约等。例如:

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class StreamExample {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(10);
        list.add(20);
        List<Integer> newList = list.stream()
                                   .map(e -> e * 2)
                                   .collect(Collectors.toList());
        System.out.println(newList);
    }
}

选择合适的集合类

在实际开发中,选择合适的集合类对于程序的性能和功能至关重要。以下是一些选择集合类的建议:

根据是否需要顺序

  1. 需要顺序:如果需要保持元素的插入顺序或根据索引访问元素,应选择List接口的实现类,如ArrayListLinkedList。如果需要频繁随机访问元素,ArrayList是更好的选择;如果需要频繁插入和删除元素,LinkedList更为合适。
  2. 不需要顺序:如果不需要保持元素的顺序,并且不允许重复元素,应选择Set接口的实现类,如HashSet。如果需要元素有序(自然顺序或自定义顺序),则选择TreeSet

根据是否存储键值对

  1. 存储键值对:如果需要存储键值对,并根据键来快速查找值,应选择Map接口的实现类。如果键值对不需要有序,HashMap是常用的选择;如果需要键有序,选择TreeMap;如果需要保持插入顺序或访问顺序,选择LinkedHashMap

根据性能需求

  1. 快速随机访问ArrayListHashMap在随机访问方面表现较好,因为它们基于数组或哈希表实现。
  2. 频繁插入和删除LinkedListLinkedHashMap在频繁插入和删除元素时性能更好,因为它们基于链表结构,不需要大量移动元素。

总结

Java集合框架提供了丰富的接口和实现类,能够满足各种数据存储和操作的需求。通过深入理解集合框架的概念、接口和实现类的特点,开发人员可以选择最合适的集合类来优化程序性能和代码结构。在实际使用中,要注意根据具体的应用场景,如数据的顺序要求、是否允许重复、性能需求等,合理选择集合类,并熟练掌握集合的各种操作方法,以提高开发效率和程序质量。同时,随着Java的不断发展,集合框架也在不断完善和扩展,开发人员应持续关注新的特性和改进,以更好地应用于实际项目中。