Java集合框架:数据存储与操作的利器

引言

Java集合框架是Java语言中用于存储和操作数据的一组核心接口和实现类。它提供了一套标准化的方法来处理各种数据结构,使开发人员能够专注于业务逻辑而不是底层数据结构的实现。本文将深入探讨Java集合框架的各个组成部分,并通过实例展示如何高效地使用它们。

一、集合框架概览

1.1 集合框架层次结构

Java集合框架主要分为两大分支:

  • Collection接口:单列数据集合,包括List、Set和Queue

  • Map接口:双列数据集合,存储键值对

java

复制代码
// 集合框架主要接口关系图
// Collection (接口)
//   ├── List (接口)
//   │    ├── ArrayList (实现类)
//   │    ├── LinkedList (实现类)
//   │    └── Vector (实现类,线程安全)
//   │         └── Stack (子类)
//   ├── Set (接口)
//   │    ├── HashSet (实现类)
//   │    │    └── LinkedHashSet (子类)
//   │    └── TreeSet (实现类)
//   └── Queue (接口)
//        ├── PriorityQueue (实现类)
//        └── Deque (接口)
//             └── ArrayDeque (实现类)
//
// Map (接口)
//   ├── HashMap (实现类)
//   │    └── LinkedHashMap (子类)
//   ├── TreeMap (实现类)
//   └── Hashtable (实现类,线程安全)
//        └── Properties (子类)

1.2 集合框架的优势

  1. 标准化:统一的API设计,易于学习和使用

  2. 高性能:针对不同场景优化的实现

  3. 类型安全:通过泛型确保类型安全

  4. 可扩展:易于扩展和自定义实现

  5. 算法支持:内置排序、搜索等算法

二、Collection接口及其实现

2.1 List接口:有序、可重复的集合

java

复制代码
import java.util.*;

public class ListExample {
    public static void main(String[] args) {
        // 1. ArrayList - 基于动态数组,随机访问快
        List<String> arrayList = new ArrayList<>();
        arrayList.add("Java");
        arrayList.add("Python");
        arrayList.add("C++");
        arrayList.add(1, "JavaScript"); // 在指定位置插入
        
        System.out.println("ArrayList内容: " + arrayList);
        System.out.println("第二个元素: " + arrayList.get(1));
        System.out.println("元素数量: " + arrayList.size());
        
        // ArrayList的遍历方式
        System.out.println("\nArrayList遍历:");
        // 方式1:普通for循环
        for (int i = 0; i < arrayList.size(); i++) {
            System.out.print(arrayList.get(i) + " ");
        }
        
        // 方式2:增强for循环
        System.out.println("\n增强for循环:");
        for (String language : arrayList) {
            System.out.print(language + " ");
        }
        
        // 方式3:迭代器
        System.out.println("\n迭代器遍历:");
        Iterator<String> iterator = arrayList.iterator();
        while (iterator.hasNext()) {
            System.out.print(iterator.next() + " ");
        }
        
        // 方式4:Lambda表达式(Java 8+)
        System.out.println("\nLambda表达式遍历:");
        arrayList.forEach(lang -> System.out.print(lang + " "));
        
        // 方式5:方法引用
        System.out.println("\n方法引用遍历:");
        arrayList.forEach(System.out::print);
        
        // 2. LinkedList - 基于双向链表,插入删除快
        List<Integer> linkedList = new LinkedList<>();
        linkedList.add(10);
        linkedList.add(20);
        linkedList.add(30);
        linkedList.addFirst(5);  // 在开头添加
        linkedList.addLast(40);  // 在末尾添加
        
        System.out.println("\n\nLinkedList内容: " + linkedList);
        System.out.println("第一个元素: " + ((LinkedList<Integer>) linkedList).getFirst());
        System.out.println("最后一个元素: " + ((LinkedList<Integer>) linkedList).getLast());
        
        // LinkedList作为队列使用
        Queue<Integer> queue = new LinkedList<>(linkedList);
        System.out.println("\n队列操作:");
        System.out.println("队首元素: " + queue.peek());
        System.out.println("出队元素: " + queue.poll());
        System.out.println("剩余队列: " + queue);
        
        // LinkedList作为栈使用
        Deque<Integer> stack = new LinkedList<>();
        stack.push(1);  // 入栈
        stack.push(2);
        stack.push(3);
        System.out.println("\n栈操作:");
        System.out.println("栈顶元素: " + stack.peek());
        System.out.println("出栈元素: " + stack.pop());
        System.out.println("剩余栈: " + stack);
        
        // 3. Vector - 线程安全的ArrayList(已过时,不推荐使用)
        List<String> vector = new Vector<>();
        vector.add("线程安全");
        vector.add("但不推荐使用");
        System.out.println("\nVector: " + vector);
    }
}

2.2 Set接口:无序、不可重复的集合

java

复制代码
import java.util.*;

public class SetExample {
    public static void main(String[] args) {
        // 1. HashSet - 基于哈希表,无序
        Set<String> hashSet = new HashSet<>();
        hashSet.add("Apple");
        hashSet.add("Banana");
        hashSet.add("Orange");
        hashSet.add("Apple"); // 重复元素不会被添加
        
        System.out.println("HashSet: " + hashSet);
        System.out.println("包含Banana? " + hashSet.contains("Banana"));
        System.out.println("Set大小: " + hashSet.size());
        
        // 2. LinkedHashSet - 保持插入顺序的HashSet
        Set<String> linkedHashSet = new LinkedHashSet<>();
        linkedHashSet.add("第一");
        linkedHashSet.add("第二");
        linkedHashSet.add("第三");
        linkedHashSet.add("第一"); // 不会重复添加
        
        System.out.println("\nLinkedHashSet: " + linkedHashSet);
        
        // 3. TreeSet - 基于红黑树,自动排序
        Set<Integer> treeSet = new TreeSet<>();
        treeSet.add(5);
        treeSet.add(2);
        treeSet.add(8);
        treeSet.add(1);
        treeSet.add(5); // 不会重复添加
        
        System.out.println("\nTreeSet(自然排序): " + treeSet);
        
        // 自定义排序的TreeSet
        Set<String> customTreeSet = new TreeSet<>((s1, s2) -> 
            s2.compareTo(s1)); // 降序排序
        customTreeSet.add("Java");
        customTreeSet.add("Python");
        customTreeSet.add("C++");
        
        System.out.println("TreeSet(自定义降序): " + customTreeSet);
        
        // Set操作:交集、并集、差集
        Set<Integer> set1 = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));
        Set<Integer> set2 = new HashSet<>(Arrays.asList(4, 5, 6, 7, 8));
        
        // 并集
        Set<Integer> union = new HashSet<>(set1);
        union.addAll(set2);
        System.out.println("\n并集: " + union);
        
        // 交集
        Set<Integer> intersection = new HashSet<>(set1);
        intersection.retainAll(set2);
        System.out.println("交集: " + intersection);
        
        // 差集(set1 - set2)
        Set<Integer> difference = new HashSet<>(set1);
        difference.removeAll(set2);
        System.out.println("差集(set1-set2): " + difference);
    }
}

2.3 Queue接口:队列实现

java

复制代码
import java.util.*;

public class QueueExample {
    public static void main(String[] args) {
        // 1. PriorityQueue - 优先级队列
        Queue<Integer> priorityQueue = new PriorityQueue<>();
        priorityQueue.offer(5);
        priorityQueue.offer(1);
        priorityQueue.offer(3);
        priorityQueue.offer(2);
        priorityQueue.offer(4);
        
        System.out.println("PriorityQueue元素: " + priorityQueue);
        System.out.println("按优先级出队:");
        while (!priorityQueue.isEmpty()) {
            System.out.print(priorityQueue.poll() + " ");
        }
        
        // 自定义优先级的PriorityQueue
        Queue<String> customPriorityQueue = new PriorityQueue<>(
            (s1, s2) -> s2.length() - s1.length() // 按字符串长度降序
        );
        customPriorityQueue.offer("Java");
        customPriorityQueue.offer("Python");
        customPriorityQueue.offer("C");
        customPriorityQueue.offer("JavaScript");
        
        System.out.println("\n\n按字符串长度优先级出队:");
        while (!customPriorityQueue.isEmpty()) {
            System.out.print(customPriorityQueue.poll() + " ");
        }
        
        // 2. ArrayDeque - 双端队列
        Deque<String> deque = new ArrayDeque<>();
        deque.addFirst("前端");  // 添加到队首
        deque.addLast("后端");   // 添加到队尾
        deque.offerFirst("移动端");
        deque.offerLast("数据库");
        
        System.out.println("\n\nArrayDeque内容: " + deque);
        System.out.println("队首元素: " + deque.peekFirst());
        System.out.println("队尾元素: " + deque.peekLast());
        System.out.println("移除队首: " + deque.pollFirst());
        System.out.println("移除队尾: " + deque.pollLast());
        System.out.println("剩余队列: " + deque);
        
        // 使用ArrayDeque作为栈
        Deque<Integer> stack = new ArrayDeque<>();
        stack.push(10);  // 入栈
        stack.push(20);
        stack.push(30);
        System.out.println("\nArrayDeque作为栈:");
        System.out.println("栈顶元素: " + stack.peek());
        System.out.println("出栈: " + stack.pop());
        System.out.println("出栈后: " + stack);
    }
}

三、Map接口及其实现

java

复制代码
import java.util.*;

public class MapExample {
    public static void main(String[] args) {
        // 1. HashMap - 最常用的Map实现
        Map<String, Integer> hashMap = new HashMap<>();
        hashMap.put("Java", 95);
        hashMap.put("Python", 90);
        hashMap.put("C++", 88);
        hashMap.put("Java", 96); // 更新已有键的值
        hashMap.putIfAbsent("JavaScript", 92); // 仅当键不存在时放入
        
        System.out.println("HashMap内容: " + hashMap);
        System.out.println("Java的分数: " + hashMap.get("Java"));
        System.out.println("包含Python键? " + hashMap.containsKey("Python"));
        System.out.println("包含分数90? " + hashMap.containsValue(90));
        System.out.println("Map大小: " + hashMap.size());
        
        // HashMap遍历方式
        System.out.println("\nHashMap遍历:");
        
        // 方式1:遍历键集合
        System.out.println("遍历键:");
        for (String key : hashMap.keySet()) {
            System.out.println(key + ": " + hashMap.get(key));
        }
        
        // 方式2:遍历值集合
        System.out.println("\n遍历值:");
        for (Integer value : hashMap.values()) {
            System.out.println(value);
        }
        
        // 方式3:遍历键值对集合
        System.out.println("\n遍历键值对:");
        for (Map.Entry<String, Integer> entry : hashMap.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
        
        // 方式4:迭代器遍历
        System.out.println("\n迭代器遍历:");
        Iterator<Map.Entry<String, Integer>> iterator = 
            hashMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Integer> entry = iterator.next();
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
        
        // 方式5:Lambda表达式(Java 8+)
        System.out.println("\nLambda表达式遍历:");
        hashMap.forEach((key, value) -> 
            System.out.println(key + ": " + value));
        
        // 2. LinkedHashMap - 保持插入顺序的HashMap
        Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
        linkedHashMap.put("第一", 1);
        linkedHashMap.put("第二", 2);
        linkedHashMap.put("第三", 3);
        
        System.out.println("\nLinkedHashMap(保持插入顺序): " + linkedHashMap);
        
        // 3. TreeMap - 基于红黑树,按键排序
        Map<String, Integer> treeMap = new TreeMap<>();
        treeMap.put("Orange", 3);
        treeMap.put("Apple", 5);
        treeMap.put("Banana", 2);
        
        System.out.println("\nTreeMap(按键排序): " + treeMap);
        
        // 自定义排序的TreeMap
        Map<String, Integer> customTreeMap = new TreeMap<>(
            (s1, s2) -> s2.compareTo(s1) // 降序排序
        );
        customTreeMap.put("Java", 95);
        customTreeMap.put("Python", 90);
        customTreeMap.put("C++", 88);
        
        System.out.println("TreeMap(自定义降序): " + customTreeMap);
        
        // Map操作示例
        System.out.println("\nMap操作示例:");
        
        // 获取或默认值
        int javaScore = hashMap.getOrDefault("Java", 0);
        int rubyScore = hashMap.getOrDefault("Ruby", 0);
        System.out.println("Java分数: " + javaScore);
        System.out.println("Ruby分数(默认0): " + rubyScore);
        
        // 替换值
        hashMap.replace("Python", 90, 95);
        System.out.println("替换后Python分数: " + hashMap.get("Python"));
        
        // 合并值
        hashMap.merge("Java", 2, (oldValue, newValue) -> oldValue + newValue);
        System.out.println("合并后Java分数: " + hashMap.get("Java"));
        
        // 计算值(如果键不存在)
        hashMap.computeIfAbsent("Go", k -> 85);
        System.out.println("Go分数: " + hashMap.get("Go"));
        
        // 删除键
        hashMap.remove("C++");
        System.out.println("删除C++后: " + hashMap);
    }
}

四、集合工具类:Collections和Arrays

java

复制代码
import java.util.*;

public class CollectionsExample {
    public static void main(String[] args) {
        // 创建测试数据
        List<Integer> numbers = new ArrayList<>(Arrays.asList(5, 2, 8, 1, 9, 3));
        List<String> fruits = new ArrayList<>(Arrays.asList(
            "Apple", "Banana", "Orange", "Grape", "Mango"
        ));
        
        // 1. 排序操作
        System.out.println("原始列表: " + numbers);
        Collections.sort(numbers);
        System.out.println("排序后: " + numbers);
        
        // 自定义排序
        Collections.sort(fruits, (s1, s2) -> s2.compareTo(s1)); // 降序
        System.out.println("水果降序: " + fruits);
        
        // 2. 查找操作
        int index = Collections.binarySearch(numbers, 8);
        System.out.println("\n元素8的索引: " + index);
        
        // 3. 反转和随机排序
        Collections.reverse(numbers);
        System.out.println("反转后: " + numbers);
        
        Collections.shuffle(numbers);
        System.out.println("随机排序后: " + numbers);
        
        // 4. 最大最小值
        System.out.println("最大值: " + Collections.max(numbers));
        System.out.println("最小值: " + Collections.min(numbers));
        
        // 5. 填充和复制
        List<Integer> copy = new ArrayList<>(Collections.nCopies(5, 0));
        System.out.println("\n填充5个0: " + copy);
        
        Collections.fill(copy, 1);
        System.out.println("填充为1后: " + copy);
        
        // 6. 不可变集合
        List<String> unmodifiableList = 
            Collections.unmodifiableList(fruits);
        System.out.println("\n不可变列表: " + unmodifiableList);
        
        // 7. 同步集合(线程安全)
        List<String> synchronizedList = 
            Collections.synchronizedList(new ArrayList<>());
        synchronizedList.add("线程安全");
        System.out.println("同步列表: " + synchronizedList);
        
        // 8. Arrays工具类使用
        int[] array = {5, 2, 8, 1, 9};
        System.out.println("\n原始数组: " + Arrays.toString(array));
        
        Arrays.sort(array);
        System.out.println("排序后数组: " + Arrays.toString(array));
        
        int[] filledArray = new int[5];
        Arrays.fill(filledArray, 7);
        System.out.println("填充数组: " + Arrays.toString(filledArray));
        
        int[] copyArray = Arrays.copyOf(array, 3);
        System.out.println("复制前3个元素: " + Arrays.toString(copyArray));
    }
}

五、Java 8+新特性:Stream API

java

复制代码
import java.util.*;
import java.util.stream.*;

public class StreamExample {
    public static void main(String[] args) {
        List<String> languages = Arrays.asList(
            "Java", "Python", "JavaScript", "C++", "Go", "Ruby", "Swift"
        );
        
        // 1. 过滤和收集
        List<String> filtered = languages.stream()
            .filter(lang -> lang.startsWith("J"))  // 过滤以J开头的
            .collect(Collectors.toList());          // 收集到List
            
        System.out.println("以J开头的语言: " + filtered);
        
        // 2. 映射转换
        List<String> upperCase = languages.stream()
            .map(String::toUpperCase)               // 转为大写
            .collect(Collectors.toList());
            
        System.out.println("大写语言: " + upperCase);
        
        // 3. 排序
        List<String> sorted = languages.stream()
            .sorted()                               // 自然排序
            .collect(Collectors.toList());
            
        System.out.println("排序后: " + sorted);
        
        // 4. 限制和跳过
        List<String> limited = languages.stream()
            .limit(3)                               // 取前3个
            .collect(Collectors.toList());
            
        System.out.println("前3个语言: " + limited);
        
        // 5. 统计操作
        long count = languages.stream()
            .filter(lang -> lang.length() > 3)
            .count();
            
        System.out.println("长度大于3的语言数量: " + count);
        
        // 6. 查找操作
        Optional<String> first = languages.stream()
            .filter(lang -> lang.length() > 5)
            .findFirst();
            
        first.ifPresent(lang -> 
            System.out.println("第一个长度大于5的语言: " + lang));
        
        // 7. 去重
        List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 4, 5);
        List<Integer> distinct = numbers.stream()
            .distinct()                             // 去重
            .collect(Collectors.toList());
            
        System.out.println("去重后: " + distinct);
        
        // 8. 规约操作
        Optional<Integer> sum = numbers.stream()
            .reduce(Integer::sum);                  // 求和
            
        sum.ifPresent(s -> System.out.println("总和: " + s));
        
        // 9. 分组操作
        Map<Integer, List<String>> grouped = languages.stream()
            .collect(Collectors.groupingBy(String::length));
            
        System.out.println("按长度分组: " + grouped);
        
        // 10. 并行流
        List<String> parallelResult = languages.parallelStream()
            .filter(lang -> lang.length() > 2)
            .map(String::toUpperCase)
            .sorted()
            .collect(Collectors.toList());
            
        System.out.println("并行流处理结果: " + parallelResult);
    }
}

六、最佳实践和性能优化

6.1 集合选择指南

需求 推荐实现类 理由
快速随机访问 ArrayList 基于数组,O(1)访问
频繁插入删除 LinkedList 基于链表,O(1)插入删除
需要排序 TreeSet/TreeMap 自动排序
需要唯一元素 HashSet/HashMap 基于哈希表,O(1)查找
需要保持插入顺序 LinkedHashSet/LinkedHashMap 保持插入顺序
线程安全需求 CopyOnWriteArrayList, ConcurrentHashMap 并发安全

6.2 性能优化建议

java

复制代码
import java.util.*;

public class PerformanceTips {
    public static void main(String[] args) {
        // 1. 指定初始容量
        List<String> list = new ArrayList<>(1000); // 避免频繁扩容
        Map<String, Integer> map = new HashMap<>(1000, 0.75f);
        
        // 2. 使用增强for循环而非迭代器
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        
        // 推荐:增强for循环
        for (Integer num : numbers) {
            System.out.print(num + " ");
        }
        
        // 3. 使用entrySet遍历Map
        Map<String, Integer> scores = new HashMap<>();
        scores.put("Java", 95);
        scores.put("Python", 90);
        
        // 推荐:遍历entrySet
        for (Map.Entry<String, Integer> entry : scores.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
        
        // 不推荐:遍历keySet再get
        for (String key : scores.keySet()) {
            System.out.println(key + ": " + scores.get(key)); // 两次查找
        }
        
        // 4. 批量操作
        List<Integer> source = Arrays.asList(1, 2, 3, 4, 5);
        List<Integer> target = new ArrayList<>(source.size());
        
        // 推荐:批量添加
        target.addAll(source);
        
        // 5. 使用不可变集合
        List<String> immutableList = List.of("A", "B", "C"); // Java 9+
        Set<String> immutableSet = Set.of("A", "B", "C");
        Map<String, Integer> immutableMap = Map.of("A", 1, "B", 2);
        
        // 6. 并发场景使用并发集合
        Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();
        concurrentMap.put("Key1", 1);
        concurrentMap.putIfAbsent("Key2", 2);
    }
}

七、常见问题与解决方案

7.1 集合的线程安全问题

java

复制代码
import java.util.*;
import java.util.concurrent.*;

public class ThreadSafeCollections {
    public static void main(String[] args) throws InterruptedException {
        // 1. 非线程安全的HashMap
        Map<String, Integer> unsafeMap = new HashMap<>();
        
        // 2. 使用Collections.synchronizedMap包装
        Map<String, Integer> synchronizedMap = 
            Collections.synchronizedMap(new HashMap<>());
        
        // 3. 使用ConcurrentHashMap(推荐)
        Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();
        
        // 测试线程安全
        ExecutorService executor = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 1000; i++) {
            final int index = i;
            executor.execute(() -> {
                concurrentMap.put("key" + index, index);
            });
        }
        
        executor.shutdown();
        executor.awaitTermination(1, TimeUnit.SECONDS);
        
        System.out.println("ConcurrentHashMap大小: " + concurrentMap.size());
        
        // 4. CopyOnWriteArrayList用于读多写少的场景
        List<String> copyOnWriteList = new CopyOnWriteArrayList<>();
        copyOnWriteList.add("Java");
        copyOnWriteList.add("Python");
        
        // 迭代时安全,但写操作开销大
        for (String item : copyOnWriteList) {
            System.out.println(item);
        }
    }
}

7.2 避免ConcurrentModificationException

java

复制代码
import java.util.*;

public class ConcurrentModificationFix {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>(Arrays.asList(
            "Java", "Python", "C++", "JavaScript"
        ));
        
        // 错误示例:在迭代时修改集合
        try {
            for (String lang : list) {
                if (lang.equals("C++")) {
                    list.remove(lang); // 抛出ConcurrentModificationException
                }
            }
        } catch (ConcurrentModificationException e) {
            System.out.println("错误:迭代时修改集合");
        }
        
        // 正确方法1:使用迭代器的remove方法
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            String lang = iterator.next();
            if (lang.equals("C++")) {
                iterator.remove(); // 安全删除
            }
        }
        System.out.println("方法1处理后: " + list);
        
        // 正确方法2:使用Java 8+的removeIf
        list.add("C++"); // 恢复数据
        list.removeIf(lang -> lang.equals("C++"));
        System.out.println("方法2处理后: " + list);
        
        // 正确方法3:收集要删除的元素,最后统一删除
        list.add("C++"); // 恢复数据
        List<String> toRemove = new ArrayList<>();
        for (String lang : list) {
            if (lang.equals("C++")) {
                toRemove.add(lang);
            }
        }
        list.removeAll(toRemove);
        System.out.println("方法3处理后: " + list);
    }
}

总结

Java集合框架是Java编程中最重要的组成部分之一,掌握好集合框架的使用对于编写高效、可维护的代码至关重要。本文详细介绍了各种集合类的特点、使用场景和最佳实践:

  1. 根据需求选择合适的集合类

  2. 理解不同集合的性能特征

  3. 掌握集合的遍历和操作技巧

  4. 注意线程安全和并发修改问题

  5. 善用Java 8+的新特性如Stream API

记住,没有"最好"的集合,只有"最适合"的集合。在实际开发中,根据具体需求选择合适的集合实现,才能真正发挥集合框架的威力。

学习建议:

  • 深入理解各种集合的内部实现原理

  • 掌握Java 8+的Lambda表达式和Stream API

  • 学习并发集合的使用场景

  • 了解Java版本更新中集合框架的新特性

希望这篇博客能帮助您全面掌握Java集合框架,提升编程效率!

相关推荐
工业甲酰苯胺3 小时前
【面试题】数据库事务隔离与传播属性是什么?
java·数据库·oracle
我居然是兔子3 小时前
基于字符串的专项实验:解锁Java String类的隐藏细节
java·开发语言
jiayong233 小时前
Java 反射完全指南 - 原理与实战
java·开发语言
Alice4 小时前
FVCOM Debug
开发语言·javascript·ecmascript
Mongnewer4 小时前
JAVA从0到1走过的小路
java·java从0到1
qq_433554544 小时前
C++状压DP
开发语言·c++
XXYBMOOO4 小时前
全面解析 Qt `QMessageBox` 类及其常用方法
开发语言·qt·microsoft
知行合一。。。4 小时前
Python--02--流程控制语句
开发语言·python
朝花不迟暮4 小时前
go的文件操作
开发语言·后端·golang