Java 随笔记: 集合与泛型
文章目录
- 1. 集合框架概述
- 2. 集合接口
- 2.1 Collection 接口
- 2.2 List 接口
- 2.3 Set 接口
- 2.4 Map 接口
- 3. 集合的常用操作
- 3.1 添加元素
- 3.2 删除元素
- 3.3 遍历元素
- 3.4 判断大小
- 3.5 判断是否为空
- 4. 迭代器
- 4.1 迭代器的作用
- 4.2 迭代器的使用
- 4.3 迭代器与增强 for 循环
- 4.4 迭代器的注意事项
- 5. 泛型概述
- 6. 泛型类
- 7. 泛型方法
- 8. 泛型边界
- 8.1 上界通配符
- 8.2 下界通配符
- 9. 泛型接口
- 10. 常用集合类
- 10.1 List 接口实现类
- 10.1.1 ArrayList
- 10.1.2 LinkedList
- 10.2 Set 接口实现类
- 10.2.1 HashSet
- 10.2.2 TreeSet
- 10.3 Map 接口实现类
- 10.3.1 HashMap
- 10.3.2 TreeMap
1. 集合框架概述
Java 集合框架提供了一套用于存储和管理对象的标准接口和实现类。它简化了开发过程,提高了代码的可读性和可维护性。
集合框架的核心概念:
- 接口 (Interface): 定义集合类的通用行为,例如添加元素、删除元素、遍历元素等。
- 实现类 (Implementation): 提供对接口的具体实现,例如 ArrayList、HashSet、HashMap 等。
- 迭代器 (Iterator): 用于遍历集合元素,提供了一种标准方式。
集合框架的主要优势:
- 代码复用: 提供了统一的接口,可以方便地使用不同的集合类。
- 类型安全: 泛型机制保证了集合中元素类型的安全。
- 高效性: 提供了各种高效的实现,满足不同场景的需求。
- 可扩展性: 可以根据需要创建新的集合类。
2. 集合接口
Java 集合框架中定义了几个主要的接口,这些接口定义了集合类的基本操作。
2.1 Collection 接口
Collection 是所有集合类的根接口,定义了集合类的基本操作,例如添加、删除、清空、判断大小等。
常用方法:
add(E e): 添加元素到集合中。Collection<String> collection = new ArrayList<>(); collection.add("Apple");remove(Object o): 从集合中删除元素。collection.remove("Apple");contains(Object o): 判断集合中是否包含指定元素。boolean containsApple = collection.contains("Apple");size(): 返回集合的大小。int size = collection.size();isEmpty(): 判断集合是否为空。boolean isEmpty = collection.isEmpty();iterator(): 返回迭代器,用于遍历集合元素。Iterator<String> iterator = collection.iterator();
2.2 List 接口
List 接口继承自 Collection 接口,它表示有序的集合,允许重复元素。
常用方法:
get(int index): 获取指定索引位置的元素。String element = list.get(1);set(int index, E element): 将指定索引位置的元素替换为新的元素。list.set(1, "Banana");add(int index, E element): 在指定索引位置插入元素。list.add(1, "Orange");remove(int index): 删除指定索引位置的元素。list.remove(1);indexOf(Object o): 返回指定元素在列表中的第一个索引位置。int index = list.indexOf("Banana");
2.3 Set 接口
Set 接口继承自 Collection 接口,它表示无序的集合,不允许重复元素。
常用方法:
add(E e): 添加元素到集合中,如果元素已存在,则不添加。Set<String> set = new HashSet<>(); boolean added = set.add("Apple");
2.4 Map 接口
Map 接口不继承自 Collection 接口,它表示键值对的映射关系,键不能重复,值可以重复。
常用方法:
put(K key, V value): 将键值对添加到映射关系中。map.put("Apple", 1);get(Object key): 获取指定键对应的值。int value = map.get("Apple");remove(Object key): 删除指定键的键值对。map.remove("Apple");containsKey(Object key): 判断映射关系中是否包含指定键。boolean containsKey = map.containsKey("Apple");containsValue(Object value): 判断映射关系中是否包含指定值。boolean containsValue = map.containsValue(1);
3. 集合的常用操作
Java 集合框架提供了多种操作集合的方法,包括添加元素、删除元素、遍历元素、判断集合大小以及判断集合是否为空。以下是对这些常用操作的详细说明和示例代码。
3.1 添加元素
使用 add() 方法可以将元素添加到集合中。对于 List 接口的实现类,元素将按添加的顺序排列;对于 Set 接口的实现类,不允许重复元素。
示例代码:
// 创建一个 ArrayList 集合
List<String> list = new ArrayList<>();// 添加元素
list.add("Java");
list.add("Python");
list.add("C++");System.out.println("集合中的元素:" + list);
输出结果:
集合中的元素:[Java, Python, C++]
3.2 删除元素
使用 remove() 方法可以删除指定的元素。对于 List 接口的实现类,可以根据索引位置或元素值删除;对于 Set 接口的实现类,只能根据元素值删除。
示例代码:
// 删除元素
list.remove("Python");System.out.println("集合中的元素:" + list);
输出结果:
集合中的元素:[Java, C++]
3.3 遍历元素
使用迭代器或增强型 for 循环可以遍历集合中的元素。
示例代码:
// 遍历元素
for (String language : list) {System.out.println(language);
}// 使用迭代器遍历元素
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {System.out.println(iterator.next());
}
输出结果:
Java
C++
Java
C++
3.4 判断大小
使用 size() 方法可以获取集合的大小,即集合中包含的元素数量。
示例代码:
System.out.println("集合的大小:" + list.size());
输出结果:
集合的大小:2
3.5 判断是否为空
使用 isEmpty() 方法可以判断集合是否为空。如果集合为空,该方法返回 true;否则返回 false。
示例代码:
System.out.println("集合是否为空:" + list.isEmpty());
输出结果:
集合是否为空:false
4. 迭代器
迭代器 (Iterator) 是 Java 集合框架中的一个重要概念,它提供了一种遍历集合元素的标准方式,而无需关心集合的具体实现。
4.1 迭代器的作用
- 提供标准的遍历方式: 迭代器统一了遍历不同集合类的接口,无论集合是
ArrayList、LinkedList还是HashSet,都可以使用相同的Iterator接口进行遍历。 - 隐藏集合实现细节: 使用迭代器可以屏蔽集合内部的实现细节,用户只需要关注如何迭代元素,而无需关心元素是如何存储和管理的。
- 支持元素的删除: 迭代器提供
remove()方法,可以在遍历过程中删除元素。
4.2 迭代器的使用
使用迭代器遍历集合的步骤如下:
- 获取迭代器: 通过调用集合的
iterator()方法获取迭代器对象。 - 循环遍历: 使用
hasNext()方法判断是否还有下一个元素,如果还有则调用next()方法获取下一个元素。 - 删除元素: 调用
remove()方法删除当前元素。
示例代码:
import java.util.ArrayList;
import java.util.Iterator;public class IteratorExample {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("Apple");list.add("Banana");list.add("Cherry");// 获取迭代器Iterator<String> iterator = list.iterator();// 循环遍历while (iterator.hasNext()) {String fruit = iterator.next();System.out.println(fruit);// 删除元素if (fruit.equals("Banana")) {iterator.remove();}}// 输出修改后的集合System.out.println(list);}
}
输出结果:
Apple
Banana
Cherry
[Apple, Cherry]
4.3 迭代器与增强 for 循环
Java 中的增强 for 循环 (for-each 循环) 也能够方便地遍历集合,其底层实际上就是使用迭代器实现的。
for (String fruit : list) {System.out.println(fruit);
}
4.4 迭代器的注意事项
- 迭代器只能单向遍历,不能向后移动。
- 在使用迭代器遍历集合时,不要直接修改集合,否则会导致迭代器失效。
- 如果需要在遍历过程中删除元素,只能使用
iterator.remove()方法,不能直接调用集合的remove()方法。
5. 泛型概述
泛型是 Java 中的一种机制,它允许编写与类型无关的代码,从而提高代码的可重用性和安全性。通过使用泛型,我们可以编写能够处理各种数据类型的代码,而无需显式地指定数据类型。
泛型带来的优势:
- 类型安全: 泛型可以帮助我们避免类型转换错误,因为编译器会进行类型检查。
- 代码可重用: 泛型可以让我们编写一次代码,然后在不同的数据类型上重复使用。
- 提高可读性: 泛型代码更易于理解,因为它明确地指定了数据类型。
泛型使用符号:
<T>: 表示一个类型参数,T 可以是任何类型。E: 表示元素类型,通常用于集合。K: 表示键类型,通常用于 Map。V: 表示值类型,通常用于 Map。
6. 泛型类
泛型类是带有类型参数的类。我们可以在类的声明中使用类型参数,然后在类的定义中使用这些参数来代替具体的类型。
示例代码:
// 定义泛型类 GenericBox,<T> 表示类型参数
public class GenericBox<T> {// 私有变量,存储内容private T content;// 设置内容public void setContent(T content) {this.content = content;}// 获取内容public T getContent() {return content;}public static void main(String[] args) {// 创建 GenericBox<String> 实例并设置内容GenericBox<String> stringBox = new GenericBox<>();stringBox.setContent("Hello");System.out.println(stringBox.getContent());// 创建 GenericBox<Integer> 实例并设置内容GenericBox<Integer> integerBox = new GenericBox<>();integerBox.setContent(123);System.out.println(integerBox.getContent());}
}
输出结果:
Hello
123
7. 泛型方法
泛型方法是在方法定义中引入类型参数。这些类型参数可以用于方法的参数列表和返回类型。
示例代码:
public class GenericMethodExample {// 泛型方法public static <T> void printArray(T[] array) {for (T element : array) {System.out.print(element + " ");}System.out.println();}public static void main(String[] args) {Integer[] intArray = {1, 2, 3, 4, 5};String[] stringArray = {"A", "B", "C", "D"};// 调用泛型方法printArray(intArray);printArray(stringArray);}
}
输出结果:
1 2 3 4 5
A B C D
8. 泛型边界
泛型边界允许我们限制类型参数的范围。我们可以使用 extends 关键字来指定上界,或者使用 super 关键字来指定下界。
8.1 上界通配符
上界通配符使用 extends 关键字,可以接受该类型及其子类型。
示例代码:
public static void printList(List<? extends Number> list) {for (Number n : list) {System.out.println(n);}
}
8.2 下界通配符
下界通配符使用 super 关键字,可以接受该类型及其父类型。
示例代码:
public static void addNumber(List<? super Integer> list) {list.add(1);list.add(2);
}
9. 泛型接口
泛型接口是带有类型参数的接口。我们可以在接口的声明中使用类型参数,然后在接口的定义中使用这些参数来代替具体的类型。
示例代码:
public interface GenericInterface<T> {void doSomething(T t);
}public class GenericInterfaceImpl implements GenericInterface<String> {@Overridepublic void doSomething(String s) {System.out.println(s);}public static void main(String[] args) {GenericInterface<String> generic = new GenericInterfaceImpl();generic.doSomething("Hello");}
}
输出结果:
Hello
10. 常用集合类
Java 集合框架中包含了许多常用的集合类,每个集合类都有其特定的用途和特点。
10.1 List 接口实现类
List 接口代表有序的集合,元素可以重复出现。
10.1.1 ArrayList
ArrayList 是一个动态数组,它的底层实现是一个可变大小的数组。ArrayList 提供了快速的随机访问能力,但在插入和删除元素时,可能需要移动大量元素。
适用场景: 适用于需要频繁读取元素,而插入和删除操作较少的场景。
示例代码:
List<String> arrayList = new ArrayList<>();
arrayList.add("Apple");
arrayList.add("Banana");
arrayList.add("Cherry");
System.out.println(arrayList);
输出结果:
[Apple, Banana, Cherry]
10.1.2 LinkedList
LinkedList 是一个双向链表,它的底层实现是一个链表结构。LinkedList 在插入和删除元素时性能较好,但随机访问元素的性能较差。
适用场景: 适用于需要频繁插入和删除元素,而读取操作较少的场景。
示例代码:
List<String> linkedList = new LinkedList<>();
linkedList.add("Apple");
linkedList.add("Banana");
linkedList.add("Cherry");
System.out.println(linkedList);
输出结果:
[Apple, Banana, Cherry]
10.2 Set 接口实现类
Set 接口代表无序的集合,元素不能重复出现。
10.2.1 HashSet
HashSet 是一个基于哈希表实现的集合,它不保证集合的迭代顺序,并且不允许重复元素。
适用场景: 适用于需要快速判断元素是否存在,且不关心元素顺序的场景。
示例代码:
Set<String> hashSet = new HashSet<>();
hashSet.add("Apple");
hashSet.add("Banana");
hashSet.add("Cherry");
System.out.println(hashSet);
输出结果:
[Banana, Apple, Cherry]
10.2.2 TreeSet
TreeSet 是一个基于红黑树实现的集合,它保证集合的迭代顺序,并且不允许重复元素。
适用场景: 适用于需要有序集合的场景。
示例代码:
Set<String> treeSet = new TreeSet<>();
treeSet.add("Apple");
treeSet.add("Banana");
treeSet.add("Cherry");
System.out.println(treeSet);
输出结果:
[Apple, Banana, Cherry]
10.3 Map 接口实现类
Map 接口代表键值对集合,每个键对应一个值。
10.3.1 HashMap
HashMap 是一个基于哈希表实现的映射关系,它不保证键值对的迭代顺序,并且允许键为 null。
适用场景: 适用于需要快速访问键值对,且不关心键值对顺序的场景。
示例代码:
Map<String, Integer> hashMap = new HashMap<>();
hashMap.put("Apple", 1);
hashMap.put("Banana", 2);
hashMap.put("Cherry", 3);
System.out.println(hashMap);
输出结果:
{Apple=1, Banana=2, Cherry=3}
10.3.2 TreeMap
TreeMap 是一个基于红黑树实现的映射关系,它保证键值对的迭代顺序,并且不允许键为 null。
适用场景: 适用于需要有序键值对的场景。
示例代码:
Map<String, Integer> treeMap = new TreeMap<>();
treeMap.put("Apple", 1);
treeMap.put("Banana", 2);
treeMap.put("Cherry", 3);
System.out.println(treeMap);
输出结果:
{Apple=1, Banana=2, Cherry=3}
相关文章:
Java 随笔记: 集合与泛型
文章目录 1. 集合框架概述2. 集合接口2.1 Collection 接口2.2 List 接口2.3 Set 接口2.4 Map 接口 3. 集合的常用操作3.1 添加元素3.2 删除元素3.3 遍历元素3.4 判断大小3.5 判断是否为空 4. 迭代器4.1 迭代器的作用4.2 迭代器的使用4.3 迭代器与增强 for 循环4.4 迭代器的注意…...
SurrealDB:高效构建实时Web应用的数据库
SurrealDB:数据驱动,实时协同。用SurrealDB简化你的开发流程- 精选真开源,释放新价值。 概览 SurrealDB,一款专为现代Web应用设计的云原生数据库,以其创新的架构和功能,为开发者提供了一个强大的工具。它整…...
SQL Server查询计划阅读及分析
6.4.5. 查询计划阅读及分析 SQL Server中,SQL语句的查询计划可能会包含多个节点,每个节点除了包含和对应一个操作符外,还包含节点及操作符相关的其他信息,其细节与具体的操作符相关。SQL Server查询计划与Oracle执行计划中,虽然每个节点所包含内容的具体称谓…...
SAP Fiori 实战课程(二):新建页面
课程回顾 上一课中,利用Visual studio Code 新建、并运行了一个Demo工程。可以实现对项目的启动,启动后进入一个List清单。 那么本次课程的目前就是在上一节Demo的基础上,从零开始新建一个完整的页面。实现从首页清单,选择行后,鼠标点击,进入下一个页面。 准备工作 在开…...
【Rust光年纪】超越ORM:探索Rust语言多款数据库客户端库的核心功能和使用场景
数据库操作新选择:从异步操作到连接管理,掌握Rust语言数据库客户端库的全貌 前言 在现代软件开发中,与数据库进行交互是一个常见的任务。Rust语言作为一种高性能、内存安全的编程语言,拥有丰富的生态系统来支持各种数据库操作。…...
解决:事件监听器 addEventListener 被多次调用
背景: 给一个元素添加了事件监听,click 会触发 然而在实际场景中,点击一次,事件会被触发两次 阻止冒泡也没有用 解决: 使用API:event.stopImmediatePropagation() stopImmediatePropagation() 方法可防止…...
配置RIPv2的认证
目录 一、配置IP地址、默认网关、启用端口 1. 路由器R1 2. 路由器R2 3. 路由器R3 4. Server1 5. Server2 二、搭建RIPv2网络 1. R1配置RIPv2 2. R2配置RIPv2 3. Server1 ping Server2 4. Server2 ping Server1 三、模拟网络攻击,为R3配置RIPv2 四、在R…...
前端调试技巧:动态高亮渲染区域
效果: 前端界面的渲染过程、次数,会通过高亮变化来显示,通过这种效果排除一些BUG 高亮 打开方式 F12进入后点击ESC,进入rendering,选择前三个即可(如果没有rendering,点击橘色部分勾选上&…...
深克隆与浅克隆的区别与实现
在软件开发中,克隆对象是一个常见需求。克隆的方式主要有两种:深克隆(Deep Clone)和浅克隆(Shallow Clone)。了解它们的区别及其实现方法,对于编写高效、安全的代码非常重要。 深克隆与浅克隆的…...
【学习笔记】无人机系统(UAS)的连接、识别和跟踪(六)-无人机直接C2通信
目录 引言 5.4 直接C2通信 5.4.1 概述 5.4.2 A2X直接C2通信服务的授权策略 5.4.3 USS使用A2X直接C2通信服务的C2授权程序 5.4.4 直接C2通信建立程序 引言 3GPP TS 23.256 技术规范,主要定义了3GPP系统对无人机(UAV)的连接性、身份识别…...
认识和安装R的扩展包,什么是模糊搜索安装,工作目录和空间的区别与设置
R语言以其强大的功能和灵活的扩展性,成为了无数数据分析师和研究者的首选工具。R的丰富功能和海量扩展包直接相关,但如何高效管理这些扩展包,进而充分发挥R的强大潜力?本文将为您揭示这些问题的答案。 一、R的扩展包 R的包(packages)是由R函数、数据和预编译代码组成的一…...
解决STM32开启定时器时立即进入一次中断程序问题
转自 解决STM32开启定时器时立即进入一次中断程序问题_stm32f407定时器初始化自动进入一次-CSDN博客 配置STM32定时器时,定时器中断使能、定时器使能、清除更新中断标志位,三者不同顺序程序执行时有不同效果,具体如下: TIM_Clea…...
Unity UGUI 之EventSystem
本文仅作学习笔记与交流,不作任何商业用途 本文包括但不限于unity官方手册,唐老狮,麦扣教程知识,引用会标记,如有不足还请斧正 1.EventSystem是什么? 有需要请查看手册:Unity - 手册࿱…...
USB转多路UART - USB 基础
一、 前言 断断续续做了不少USB相关开发,但是没有系统去了解过,遇到问题就很被动了。做这个USB转UART的项目就是,于是专门花了一天的时间学习USB及CDC相关,到写这文章时估计也忘得差不多了,趁项目收尾阶段记录一下&am…...
接近50个实用编程相关学习资源网站
Date: 2024.07.17 09:45:10 author: lijianzhan 编程语言以及编程相关工具等实用性官方文档网站 C语言文档:https://learn.microsoft.com/zh-cn/cpp/c-languageMicrosoft C、C和汇编程序文档:https://learn.microsoft.com/zh-cn/cppJAVA官方文档&#…...
在数据操作中使用SELECT子句
目录 一、INSERT 语句中使用 SELECT子句 二、UPDATE 语句中使用 SELECT子句 三、DELETE 语句中使用 SELECT子句 一、INSERT 语句中使用 SELECT子句 在 INSERT 语句中使用 SELECT子句,可以将一个或多个表或视图中的数据添加到另外一个表中。使用 SELECT 子句还可以…...
Golang | Leetcode Golang题解之第274题H指数
题目: 题解: func hIndex(citations []int) int {// 答案最多只能到数组长度left,right:0,len(citations)var mid intfor left<right{// 1 防止死循环mid(leftright1)>>1cnt:0for _,v:range citations{if v>mid{cnt}}if cnt>mid{// 要找…...
区块链技术在智能家居中的创新应用探索
随着物联网技术的发展和智能家居市场的蓬勃发展,区块链技术作为一种去中心化的数据管理和安全保障技术,正在逐渐引入智能家居领域,并为其带来了新的创新应用。本文将探讨区块链技术在智能家居中的具体应用场景、优势以及未来发展方向。 智能家…...
无需业务改造,一套数据库满足 OLTP 和 OLAP,GaiaDB 发布并行查询能力
在企业中通常存在两类数据处理场景,一类是在线事务处理场景(OLTP),例如交易系统,另一类是在线分析处理场景(OLAP),例如业务报表。 OLTP 数据库擅长处理数据的增、删、改,…...
PHP 表单验证:邮件和URL
PHP 表单验证:邮件和URL 在Web开发中,表单验证是一个至关重要的环节,它确保了用户输入的数据的有效性和安全性。特别是在处理邮件地址和URL时,准确的验证尤为重要。本文将详细介绍如何使用PHP来验证表单中的邮件地址和URL。 邮件…...
(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...
提升移动端网页调试效率:WebDebugX 与常见工具组合实践
在日常移动端开发中,网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时,开发者迫切需要一套高效、可靠且跨平台的调试方案。过去,我们或多或少使用过 Chrome DevTools、Remote Debug…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...
【UE5 C++】通过文件对话框获取选择文件的路径
目录 效果 步骤 源码 效果 步骤 1. 在“xxx.Build.cs”中添加需要使用的模块 ,这里主要使用“DesktopPlatform”模块 2. 添加后闭UE编辑器,右键点击 .uproject 文件,选择 "Generate Visual Studio project files",重…...
Linux 下 DMA 内存映射浅析
序 系统 I/O 设备驱动程序通常调用其特定子系统的接口为 DMA 分配内存,但最终会调到 DMA 子系统的dma_alloc_coherent()/dma_alloc_attrs() 等接口。 关于 dma_alloc_coherent 接口详细的代码讲解、调用流程,可以参考这篇文章,我觉得写的非常…...
