5java集合框架

发布于:2025-05-15 ⋅ 阅读:(19) ⋅ 点赞:(0)

 集合框架

Java 集合框架是 Java 提供的一组用于存储和操作数据的类和接口,它提供了高效、灵活且安全的数据存储和处理方式。

1. 集合框架的架构

Java 集合框架主要由两大接口派生而出:Collection 和 Map

  • Collection 接口:存储一组对象,它有三个主要的子接口:ListSet 和 Queue
  • Map 接口:存储键值对,键是唯一的,每个键对应一个值。
  • 其继承关系如下

     
    Collection
    ├── List(有序,允许重复)
    │   ├── ArrayList
    │   ├── LinkedList
    │   └── Vector
    ├── Set(无序,元素唯一)
    │   ├── HashSet
    │   ├── LinkedHashSet
    │   └── TreeSet
    └── Queue(队列结构)
        ├── LinkedList(可作队列)
        └── PriorityQueue(优先级队列)
    
    Map
    ├── HashMap
    ├── LinkedHashMap
    ├── TreeMap
    └── Hashtable(线程安全但过时)

2. Collection 接口及其子接口

2.1 List 接口

List 是有序的集合,允许存储重复的元素。常见的实现类有 ArrayListLinkedList 和 Vector

  • ArrayList:基于动态数组实现,支持随机访问,插入和删除操作效率较低。
import java.util.ArrayList;
import java.util.List;

public class ArrayListExample {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("apple");
        list.add("banana");
        System.out.println(list.get(0)); // 输出: apple
    }
}
  • LinkedList:基于双向链表实现,插入和删除操作效率较高,随机访问效率较低。

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

public class LinkedListExample {
    public static void main(String[] args) {
        List<String> list = new LinkedList<>();
        list.add("apple");
        list.add("banana");
        System.out.println(list.get(0)); // 输出: apple
    }
}
  • Vector:与 ArrayList 类似,但它是线程安全的,不过性能相对较低。(已逐渐被替代)
2.2 Set 接口

Set 是不允许存储重复元素的集合。常见的实现类有 HashSetTreeSet 和 LinkedHashSet

  • HashSet:基于哈希表实现,不保证元素的顺序。
import java.util.HashSet;
import java.util.Set;

public class HashSetExample {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        set.add("apple");
        set.add("banana");
        System.out.println(set.contains("apple")); // 输出: true
    }
}

HashSet 集合的主要特性之一是不允许存储重复元素。判断元素是否重复,依赖于元素的 hashCode() 和 equals() 方法。

  • 原理:当向 HashSet 中添加元素时,首先会调用元素的 hashCode() 方法计算哈希码,根据哈希码确定元素在内部存储结构(哈希表)中的位置。如果该位置已经有其他元素,则会调用 equals() 方法来比较这两个元素是否相等。若相等,则认为是重复元素,不会添加到集合中。
  • 示例:以下是一个自定义类的示例,展示了正确重写 hashCode() 和 equals() 方法的重要性。
import java.util.HashSet;

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 重写 hashCode 方法
    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }

    // 重写 equals 方法
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age && (name != null ? name.equals(person.name) : person.name == null);
    }

    public static void main(String[] args) {
        HashSet<Person> set = new HashSet<>();
        Person p1 = new Person("Alice", 20);
        Person p2 = new Person("Alice", 20);
        set.add(p1);
        set.add(p2);
        System.out.println(set.size()); // 输出 1,因为 p1 和 p2 被认为是重复元素
    }
}

2. 空元素的添加

HashSet 允许添加一个 null 元素。因为 null 的 hashCode() 始终为 0,且在 HashSet 中只会存在一个 null 元素。

import java.util.HashSet;

public class NullElementInHashSet {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<>();
        set.add(null);
        set.add(null);
        System.out.println(set.size()); // 输出 1,因为只允许有一个 null 元素
    }
}

  • TreeSet:基于红黑树实现,元素会按照比较器(元素所属的类必须实现 java.lang.Comparable 接口,并实现 compareTo() 方法)进行排序。如果元素所属的类没有实现 Comparable 接口,可以使用定制排序。定制排序需要在创建 TreeSet 时传入一个 Comparator 对象,Comparator 接口定义了 compare() 方法,用于指定元素之间的比较规则。
import java.util.TreeSet;
import java.util.Set;

public class TreeSetExample {
    public static void main(String[] args) {
        Set<String> set = new TreeSet<>();
        set.add("apple");
        set.add("banana");
        System.out.println(set.first()); // 输出: apple
    }
}
import java.util.Comparator;
import java.util.TreeSet;

public class CustomSortingExample {
    public static void main(String[] args) {
        // 创建一个 Comparator 对象,按照字符串长度进行比较
        Comparator<String> lengthComparator = new Comparator<String>() {
            @Override
            public int compare(String s1, String s2) {
                return Integer.compare(s1.length(), s2.length());
            }
        };

        // 使用定制的 Comparator 创建 TreeSet
        TreeSet<String> treeSet = new TreeSet<>(lengthComparator);
        treeSet.add("apple");
        treeSet.add("banana");
        treeSet.add("cherry");
        treeSet.add("date");

        // 遍历 TreeSet,元素会按照字符串长度从小到大排序
        for (String str : treeSet) {
            System.out.println(str);
        }
    }
}

  • LinkedHashSet:基于哈希表和链表实现,保证元素的插入顺序。
import java.util.LinkedHashSet;

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

        // 添加元素到 LinkedHashSet
        linkedHashSet.add("apple");
        linkedHashSet.add("banana");
        linkedHashSet.add("cherry");
        // 尝试添加重复元素,不会被添加
        linkedHashSet.add("apple");

        // 输出 LinkedHashSet 的元素,元素顺序与插入顺序一致
        System.out.println("LinkedHashSet 中的元素:");
        for (String element : linkedHashSet) {
            System.out.println(element);
        }

        // 检查元素是否存在
        boolean containsBanana = linkedHashSet.contains("banana");
        System.out.println("是否包含 banana: " + containsBanana);

        // 移除元素
        linkedHashSet.remove("cherry");
        System.out.println("移除 cherry 后,LinkedHashSet 中的元素:");
        for (String element : linkedHashSet) {
            System.out.println(element);
        }

        // 检查集合是否为空
        boolean isEmpty = linkedHashSet.isEmpty();
        System.out.println("LinkedHashSet 是否为空: " + isEmpty);

        // 获取集合的大小
        int size = linkedHashSet.size();
        System.out.println("LinkedHashSet 的大小: " + size);
    }
}    

2.3 Queue 接口

Queue 是队列接口,遵循先进先出(FIFO)原则。常见的实现类有 LinkedList(也实现了 Queue 接口)和 PriorityQueue

  • PriorityQueue:基于堆实现。
  • 元素按自然序或Comparator排序(使用场景:任务优先级处理、堆排序)
import java.util.PriorityQueue;
import java.util.Queue;

public class PriorityQueueExample {
    public static void main(String[] args) {
        Queue<Integer> queue = new PriorityQueue<>();
        queue.add(3);
        queue.add(1);
        queue.add(2);
        System.out.println(queue.poll()); // 输出: 1
    }
}

3. Map 接口及其实现类

Map 存储键值对,键是唯一的。常见的实现类有 HashMapTreeMap 和 LinkedHashMap

  • HashMap:基于哈希表实现,不保证键值对的顺序。
import java.util.HashMap;
import java.util.Map;

public class HashMapExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("apple", 1);
        map.put("banana", 2);
        System.out.println(map.get("apple")); // 输出: 1
    }
}

  • TreeMap:基于红黑树实现,键会按照自然顺序或指定的比较器进行排序。
import java.util.TreeMap;
import java.util.Map;

public class TreeMapExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new TreeMap<>();
        map.put("apple", 1);
        map.put("banana", 2);
        System.out.println(map.firstKey()); // 输出: apple
    }
}
  • LinkedHashMap:基于哈希表和链表实现,保证键值对的插入顺序。 是 Java 中 Map 接口的一个实现类,它继承自 HashMap,同时维护了一个双向链表,用来记录元素的插入顺序或者访问顺序
  • 当构造函数中设置 accessOrder=true 时,每次调用 get() 或 put() 方法访问元素时,该元素会被移动到双向链表的​​尾部​​(即最近访问的元素排在最后)
        LinkedHashMap<String, Integer> map = new LinkedHashMap<>(16, 0.75f, true);
        map.put("A", 1);
        map.put("B", 2);
        map.put("C", 3);

遍历结果:A → B → C
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
// 访问元素 "B"
        Integer b = map.get("B");
        System.out.println("============访问了B元素的值:"+b);

// 遍历结果:A → C → B(B 被移动到末尾)
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
构建 LRU 缓存

通过重写 removeEldestEntry 方法,可自动淘汰最久未访问的元素

LinkedHashMap<Integer, String> lruCache = new LinkedHashMap<>(3, 0.75f, true) {
    @Override
    protected boolean removeEldestEntry(Map.Entry<Integer, String> eldest) {
        return size() > 3; // 容量超过3时移除最旧元素
    }
};

lruCache.put(1, "One");
lruCache.put(2, "Two");
lruCache.put(3, "Three");
lruCache.get(1); // 访问元素1,使其保留
lruCache.put(4, "Four"); // 触发淘汰,移除元素2
System.out.println(lruCache); // 输出:{1=One, 3=Three, 4=Four}

LinkedHashMap 本身非线程安全,多线程环境下需通过 Collections.synchronizedMap() 或并发包(如 ConcurrentHashMap)实现同步

4. 集合的遍历

可以使用迭代器、for-each 循环或 Java 8 的 Stream API 来遍历集合。

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

public class CollectionTraversal {
    public static void main(String[] args) {
        List<String> c = new ArrayList<>();
        c.add("apple");
        c.add("banana");

        // 使用迭代器遍历
        Iterator<String> iterator = c.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }

        // 使用 for-each 循环遍历
        for (String element : c) {
            System.out.println(element);
        }

        // 使用 Stream API 遍历
        c.stream().forEach(System.out::println);
    }
}

集合 forEach 遍历

5. 线程安全的集合

Java 提供了一些线程安全的集合类,如 VectorHashtable,以及 Collections.synchronizedXXX 方法返回的同步集合,还有 Java 并发包中的 ConcurrentHashMapCopyOnWriteArrayList 等。

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

public class ThreadSafeCollection {
    public static void main(String[] args) {
        // 使用 Collections.synchronizedMap 方法创建线程安全的 Map
        Map<String, Integer> map = new HashMap<>();
        Map<String, Integer> synchronizedMap = Collections.synchronizedMap(map);
    }
}

Java 集合框架提供了丰富的类和接口,以满足不同的需求。在实际开发中,需要根据具体的场景选择合适的集合类。

Stream流

1. Stream 是 Java 8 引入的一个新的抽象概念,

它代表了一个来自数据源的元素序列,支持聚合操作。数据源可以是集合、数组、I/O 通道等。Stream 操作分为中间操作和终端操作。

2. Stream 流的创建

2.1 从集合创建

可以通过集合的 stream() 或 parallelStream() 方法创建 Stream 流。

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

public class StreamCreationFromCollection {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("apple");
        list.add("banana");
        list.add("cherry");

        // 创建顺序流
        Stream<String> sequentialStream = list.stream();
        // 创建并行流
        Stream<String> parallelStream = list.parallelStream();
    }
}
2.2 从数组创建

使用 Arrays.stream() 方法从数组创建 Stream 流。

import java.util.Arrays;
import java.util.stream.Stream;

public class StreamCreationFromArray {
    public static void main(String[] args) {
        String[] array = {"apple", "banana", "cherry"};
        Stream<String> stream = Arrays.stream(array);
    }
}
2.3 使用 Stream.of() 方法

可以直接使用 Stream.of() 方法创建 Stream 流。

import java.util.stream.Stream;

public class StreamCreationUsingOf {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("apple", "banana", "cherry");
    }
}

3. 中间操作

中间操作会返回一个新的 Stream,允许进行链式调用。常见的中间操作有:

3.1 filter()

用于过滤满足条件的元素。

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

public class FilterExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
        List<Integer> evenNumbers = numbers.stream()
               .filter(n -> n % 2 == 0)
               .collect(Collectors.toList());
        System.out.println(evenNumbers);
    }
}
3.2 map()

用于对元素进行转换。

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

public class MapExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3);
        List<Integer> squaredNumbers = numbers.stream()
               .map(n -> n * n)
               .collect(Collectors.toList());
        System.out.println(squaredNumbers);
    }
}
3.3 sorted()

用于对元素进行排序。

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

public class SortedExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(3, 1, 2);
        List<Integer> sortedNumbers = numbers.stream()
               .sorted()
               .collect(Collectors.toList());
        System.out.println(sortedNumbers);
    }
}
3.4 distinct()

用于去除重复元素。

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

public class DistinctExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3);
        List<Integer> distinctNumbers = numbers.stream()
               .distinct()
               .collect(Collectors.toList());
        System.out.println(distinctNumbers);
    }
}

4. 终端操作

终端操作会触发 Stream 流的处理,并产生一个结果或副作用。常见的终端操作有:

4.1 forEach()

用于对每个元素执行操作。

import java.util.Arrays;
import java.util.List;

public class ForEachExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3);
        numbers.stream()
               .forEach(n -> System.out.println(n));
    }
}
4.2 collect()

用于将 Stream 流中的元素收集到一个集合中。

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

public class CollectExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3);
        List<Integer> squaredNumbers = numbers.stream()
               .map(n -> n * n)
               .collect(Collectors.toList());
        System.out.println(squaredNumbers);
    }
}
4.3 reduce()

用于将 Stream 流中的元素进行合并。

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class ReduceExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3);
        Optional<Integer> sum = numbers.stream()
               .reduce((a, b) -> a + b);
        sum.ifPresent(System.out::println);
    }
}
4.4 count()

用于统计 Stream 流中元素的数量。

import java.util.Arrays;
import java.util.List;

public class CountExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3);
        long count = numbers.stream().count();
        System.out.println(count);
    }
}

5. 并行流

并行流可以利用多核处理器的优势,并行处理 Stream 流中的元素,提高处理性能。但需要注意线程安全问题。

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

public class ParallelStreamExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
        List<Integer> squaredNumbers = numbers.parallelStream()
               .map(n -> n * n)
               .collect(Collectors.toList());
        System.out.println(squaredNumbers);
    }
}


网站公告

今日签到

点亮在社区的每一天
去签到