JAVA 集合框架进阶:List 与 Set 的深度解析与实战

JAVA 集合框架进阶:List 与 Set 的深度解析与实战

JAVA 集合框架进阶:List 与 Set 的深度解析与实战

在这里插入图片描述

1.1 本章学习目标与重点

💡 掌握 List 和 Set 接口的核心特性,理解不同实现类的底层原理与适用场景。
💡 熟练运用集合的常用方法,解决数据存储、查找、去重等实际开发问题。
💡 理解集合的线程安全问题,掌握线程安全集合的使用方式。
⚠️ 本章重点是 不同集合的底层数据结构性能对比,这是面试和开发中的核心考点。

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

1.2.1 List 接口的核心特性

💡 List 是有序集合,元素的存储顺序和插入顺序一致,支持通过索引访问元素。
List 允许存储重复元素,也可以存储 null 值。
List 接口的常用实现类有 ArrayListLinkedListVector,它们的底层结构和性能各有差异。

✅ 核心结论:List 适合需要按索引操作、元素有序且允许重复的场景。

1.2.2 ArrayList:基于动态数组的实现

底层原理

💡 ArrayList 的底层是动态扩容的数组,默认初始容量为 10。
当数组容量不足时,会自动扩容为原来的 1.5 倍。
扩容过程需要新建数组并复制原数组元素,频繁扩容会影响性能。

代码实操:ArrayList 的常用操作

① 📝 创建 ArrayList 对象,添加不同类型的元素
② 📝 通过索引获取、修改元素
③ 📝 遍历集合,删除指定元素
④ 📝 判断集合是否包含某个元素

importjava.util.ArrayList;importjava.util.Iterator;importjava.util.List;publicclassArrayListDemo{publicstaticvoidmain(String[] args){// 1. 创建 ArrayList 集合List<String> list =newArrayList<>();// 2. 添加元素 list.add("Java"); list.add("Python"); list.add("C++"); list.add("Java");// 允许重复元素 list.add(null);// 允许存储null// 3. 通过索引获取元素String firstElement = list.get(0);System.out.println("第一个元素:"+ firstElement);// 4. 修改元素 list.set(1,"Go");System.out.println("修改后的集合:"+ list);// 5. 遍历集合 - 方式1:普通for循环System.out.println("普通for循环遍历:");for(int i =0; i < list.size(); i++){System.out.println(list.get(i));}// 6. 遍历集合 - 方式2:增强for循环System.out.println("增强for循环遍历:");for(String s : list){System.out.println(s);}// 7. 遍历集合 - 方式3:迭代器System.out.println("迭代器遍历:");Iterator<String> iterator = list.iterator();while(iterator.hasNext()){String s = iterator.next();// 迭代器遍历中删除元素,避免并发修改异常if(s ==null){ iterator.remove();}}System.out.println("删除null后的集合:"+ list);// 8. 判断集合是否包含指定元素boolean containsJava = list.contains("Java");System.out.println("集合包含Java:"+ containsJava);// 9. 清空集合 list.clear();System.out.println("清空后的集合是否为空:"+ list.isEmpty());}}
性能分析
  • 查询操作:基于索引的查询效率高,时间复杂度为 O(1)
  • 增删操作:在集合尾部增删元素效率高;在中间增删元素需要移动数组元素,时间复杂度为 O(n)
    ⚠️ 注意事项:ArrayList线程不安全的集合,多线程环境下使用会出现并发修改异常。

1.2.3 LinkedList:基于双向链表的实现

底层原理

💡 LinkedList 的底层是双向链表,每个节点包含前驱节点、后继节点和元素值。
链表结构无需连续的内存空间,增删元素时只需要修改节点的引用关系,不需要扩容。

代码实操:LinkedList 的特有方法

LinkedList 除了继承 List 接口的方法,还提供了操作首尾元素的特有方法,适合作为栈或队列使用。

importjava.util.LinkedList;publicclassLinkedListDemo{publicstaticvoidmain(String[] args){LinkedList<String> linkedList =newLinkedList<>();// 1. 添加元素 linkedList.addFirst("头元素"); linkedList.addLast("尾元素"); linkedList.add("中间元素");// 2. 获取首尾元素String first = linkedList.getFirst();String last = linkedList.getLast();System.out.println("头元素:"+ first +",尾元素:"+ last);// 3. 删除首尾元素 linkedList.removeFirst(); linkedList.removeLast();System.out.println("删除首尾后的集合:"+ linkedList);// 4. 作为栈使用:先进后出 linkedList.push("元素1"); linkedList.push("元素2"); linkedList.push("元素3");System.out.println("栈结构:"+ linkedList);String popElement = linkedList.pop();System.out.println("弹出的元素:"+ popElement);System.out.println("弹出后的栈:"+ linkedList);// 5. 作为队列使用:先进先出 linkedList.offer("队列元素1"); linkedList.offer("队列元素2");System.out.println("队列结构:"+ linkedList);String pollElement = linkedList.poll();System.out.println("出队的元素:"+ pollElement);System.out.println("出队后的队列:"+ linkedList);}}
性能分析
  • 查询操作:查询元素需要遍历链表,时间复杂度为 O(n)
  • 增删操作:增删元素只需修改节点引用,时间复杂度为 O(1)
    ⚠️ 注意事项:LinkedList 同样是线程不安全的集合,不适合多线程环境。

1.2.4 Vector:线程安全的动态数组

💡 Vector 的底层和 ArrayList 类似,都是动态数组。
Vector 的方法都加了 synchronized 关键字,是线程安全的集合。
它的扩容机制默认是原来的 2 倍,扩容效率低于 ArrayList

✅ 核心结论:Vector 性能较低,现代开发中更推荐使用 Collections.synchronizedList()CopyOnWriteArrayList 实现线程安全。

1.2.5 ArrayList 与 LinkedList 性能对比测试

我们通过代码测试两种集合在查询和增删操作中的耗时差异,测试数据为 10 万条元素。

importjava.util.ArrayList;importjava.util.LinkedList;importjava.util.List;publicclassListPerformanceTest{publicstaticfinalintSIZE=100000;publicstaticvoidmain(String[] args){List<Integer> arrayList =newArrayList<>();List<Integer> linkedList =newLinkedList<>();// 初始化集合for(int i =0; i <SIZE; i++){ arrayList.add(i); linkedList.add(i);}// 测试查询性能long arrayListQueryTime =testQuery(arrayList);long linkedListQueryTime =testQuery(linkedList);System.out.println("ArrayList查询耗时:"+ arrayListQueryTime +"ms");System.out.println("LinkedList查询耗时:"+ linkedListQueryTime +"ms");// 测试中间增删性能long arrayListAddRemoveTime =testAddRemove(arrayList);long linkedListAddRemoveTime =testAddRemove(linkedList);System.out.println("ArrayList中间增删耗时:"+ arrayListAddRemoveTime +"ms");System.out.println("LinkedList中间增删耗时:"+ linkedListAddRemoveTime +"ms");}// 测试查询性能:随机访问1000次privatestaticlongtestQuery(List<Integer> list){long start =System.currentTimeMillis();for(int i =0; i <1000; i++){int index =(int)(Math.random()*SIZE); list.get(index);}returnSystem.currentTimeMillis()- start;}// 测试中间增删性能:在中间位置增删1000次privatestaticlongtestAddRemove(List<Integer> list){long start =System.currentTimeMillis();int middleIndex = list.size()/2;for(int i =0; i <1000; i++){ list.add(middleIndex, i); list.remove(middleIndex);}returnSystem.currentTimeMillis()- start;}}

测试结果(仅供参考)

ArrayList查询耗时:1ms LinkedList查询耗时:15ms ArrayList中间增删耗时:8ms LinkedList中间增删耗时:2ms 

✅ 核心结论:查询多用 ArrayList,增删多用 LinkedList

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

1.3.1 Set 接口的核心特性

💡 Set 是无序集合,元素没有索引,存储顺序由底层数据结构决定。
Set 不允许存储重复元素,最多只能存储一个 null 值。
Set 接口的常用实现类有 HashSetLinkedHashSetTreeSet

✅ 核心结论:Set 适合需要元素去重、不关注存储顺序的场景。

1.3.2 HashSet:基于哈希表的实现

底层原理

💡 HashSet 的底层是 HashMap,它是通过 HashMap 的 key 来存储元素的,value 是一个固定的 PRESENT 对象。
HashSet 保证元素唯一的原理是:先通过 hashCode() 方法计算哈希值,再通过 equals() 方法比较元素是否相同。
如果两个元素的 hashCode() 值相同且 equals() 方法返回 true,则认为是重复元素,无法添加。

代码实操:HashSet 的常用操作
importjava.util.HashSet;importjava.util.Iterator;importjava.util.Set;publicclassHashSetDemo{publicstaticvoidmain(String[] args){Set<String> hashSet =newHashSet<>();// 1. 添加元素 hashSet.add("Apple"); hashSet.add("Banana"); hashSet.add("Orange"); hashSet.add("Apple");// 重复元素,无法添加 hashSet.add(null);// 可以存储一个nullSystem.out.println("HashSet集合:"+ hashSet);// 输出顺序与插入顺序无关// 2. 遍历集合System.out.println("增强for循环遍历:");for(String s : hashSet){System.out.println(s);}System.out.println("迭代器遍历:");Iterator<String> iterator = hashSet.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}// 3. 判断元素是否存在boolean containsBanana = hashSet.contains("Banana");System.out.println("包含Banana:"+ containsBanana);// 4. 删除元素 hashSet.remove("Orange");System.out.println("删除Orange后的集合:"+ hashSet);// 5. 清空集合 hashSet.clear();System.out.println("集合是否为空:"+ hashSet.isEmpty());}}
性能分析
  • 增删查操作:效率高,时间复杂度为 O(1)
  • 当哈希冲突严重时,性能会下降到 O(n)
    ⚠️ 注意事项:
  1. HashSet线程不安全的集合。
  2. 存储自定义对象时,必须重写 hashCode()equals() 方法,否则无法保证元素唯一。
自定义对象去重案例

我们定义一个 Student 类,重写 hashCode()equals() 方法,实现基于学号的去重。

importjava.util.HashSet;importjava.util.Objects;importjava.util.Set;classStudent{privateString id;privateString name;publicStudent(String id,String name){this.id = id;this.name = name;}// 重写equals方法:根据学号判断是否相同@Overridepublicbooleanequals(Object o){if(this== o)returntrue;if(o ==null||getClass()!= o.getClass())returnfalse;Student student =(Student) o;returnObjects.equals(id, student.id);}// 重写hashCode方法:根据学号计算哈希值@OverridepublicinthashCode(){returnObjects.hash(id);}@OverridepublicStringtoString(){return"Student{id='"+ id +"',+ name +"'}";}}publicclassHashSetCustomObjectDemo{publicstaticvoidmain(String[] args){Set<Student> studentSet =newHashSet<>(); studentSet.add(newStudent("001","张三")); studentSet.add(newStudent("002","李四")); studentSet.add(newStudent("001","张三"));// 重复元素,无法添加for(Student student : studentSet){System.out.println(student);}}}

输出结果

Student{id='001', name='张三'} Student{id='002', name='李四'} 

1.3.3 LinkedHashSet:有序的哈希集合

💡 LinkedHashSetHashSet 的子类,底层是 HashMap + 双向链表
它通过双向链表维护元素的插入顺序,保证遍历顺序和插入顺序一致。
它的元素唯一性原理和 HashSet 相同,性能略低于 HashSet

代码实操:LinkedHashSet 的有序性
importjava.util.LinkedHashSet;importjava.util.Set;publicclassLinkedHashSetDemo{publicstaticvoidmain(String[] args){Set<String> linkedHashSet =newLinkedHashSet<>(); linkedHashSet.add("B"); linkedHashSet.add("A"); linkedHashSet.add("C"); linkedHashSet.add("A");// 重复元素,无法添加// 遍历顺序与插入顺序一致System.out.println("LinkedHashSet集合:"+ linkedHashSet);}}

输出结果

LinkedHashSet集合:[B, A, C] 

✅ 核心结论:需要去重且保留插入顺序时,使用 LinkedHashSet

1.3.4 TreeSet:基于红黑树的排序集合

底层原理

💡 TreeSet 的底层是 TreeMap,基于红黑树实现。
TreeSet 会自动对元素进行排序,默认是升序排列。
它保证元素唯一的原理是:通过比较元素的大小,相同元素无法添加。

排序方式
  1. 自然排序:元素实现 Comparable 接口,重写 compareTo() 方法。
  2. 定制排序:创建 TreeSet 时传入 Comparator 比较器,自定义排序规则。
代码实操1:自然排序
importjava.util.Set;importjava.util.TreeSet;publicclassTreeSetNaturalSortDemo{publicstaticvoidmain(String[] args){Set<Integer> treeSet =newTreeSet<>(); treeSet.add(3); treeSet.add(1); treeSet.add(2); treeSet.add(3);// 重复元素,无法添加// 自动升序排列System.out.println("TreeSet自然排序:"+ treeSet);}}

输出结果

TreeSet自然排序:[1, 2, 3] 
代码实操2:定制排序

我们对字符串进行降序排列,通过 Comparator 实现定制排序。

importjava.util.Comparator;importjava.util.Set;importjava.util.TreeSet;publicclassTreeSetCustomSortDemo{publicstaticvoidmain(String[] args){// 传入比较器,实现字符串降序排列Set<String> treeSet =newTreeSet<>(newComparator<String>(){@Overridepublicintcompare(String o1,String o2){return o2.compareTo(o1);// 降序排列}}); treeSet.add("Java"); treeSet.add("Python"); treeSet.add("Go");System.out.println("TreeSet定制排序:"+ treeSet);}}

输出结果

TreeSet定制排序:[Python, Java, Go] 
性能分析
  • 增删查操作:时间复杂度为 O(log n),效率低于 HashSet。
    ⚠️ 注意事项:
  1. TreeSet线程不安全的集合。
  2. 存储自定义对象时,必须指定排序规则,否则会抛出 ClassCastException

1.4 集合的线程安全问题与解决方案

1.4.1 线程不安全的表现

当多个线程同时操作一个非线程安全的集合时,会出现 ConcurrentModificationException 并发修改异常。
例如,一个线程遍历集合,另一个线程修改集合,就会触发该异常。

1.4.2 解决方案1:使用 Collections 工具类

java.util.Collections 提供了 synchronizedXxx() 方法,可以将非线程安全的集合包装成线程安全的集合。

importjava.util.ArrayList;importjava.util.Collections;importjava.util.List;publicclassCollectionsSynchronizedDemo{publicstaticvoidmain(String[] args){// 将ArrayList包装成线程安全的集合List<String> safeList =Collections.synchronizedList(newArrayList<>()); safeList.add("线程安全元素1"); safeList.add("线程安全元素2");System.out.println("线程安全集合:"+ safeList);}}

1.4.3 解决方案2:使用 JUC 包下的线程安全集合

JUC 包(java.util.concurrent)提供了性能更高的线程安全集合,常用的有:

  • CopyOnWriteArrayList:线程安全的 ArrayList,适合读多写少的场景。
  • CopyOnWriteArraySet:线程安全的 Set,底层是 CopyOnWriteArrayList。
importjava.util.Iterator;importjava.util.concurrent.CopyOnWriteArrayList;publicclassCopyOnWriteArrayListDemo{publicstaticvoidmain(String[] args){CopyOnWriteArrayList<String> cowList =newCopyOnWriteArrayList<>(); cowList.add("元素1"); cowList.add("元素2"); cowList.add("元素3");// 遍历过程中可以修改集合,不会抛出并发修改异常Iterator<String> iterator = cowList.iterator();while(iterator.hasNext()){String s = iterator.next();if(s.equals("元素2")){ cowList.remove(s);}}System.out.println("修改后的集合:"+ cowList);}}

✅ 核心结论:JUC 包下的线程安全集合性能高于 Collections 包装的集合,优先推荐使用。

1.5 实战案例:集合工具类封装

1.5.1 需求分析

💡 封装一个集合工具类 CollectionUtil,提供以下实用功能:

  1. 集合去重:去除 List 中的重复元素,保留插入顺序。
  2. 集合交集:获取两个 List 的共同元素。
  3. 集合差集:获取 List1 中有但 List2 中没有的元素。
  4. 集合排序:对 List 中的自定义对象进行排序。

1.5.2 代码实现

importjava.util.*;importjava.util.stream.Collectors;/** * 集合工具类 */publicclassCollectionUtil{/** * List去重,保留插入顺序 */publicstatic<T>List<T>distinctList(List<T> list){if(list ==null|| list.isEmpty()){returnnewArrayList<>();}returnnewLinkedHashSet<>(list).stream().collect(Collectors.toList());}/** * 获取两个List的交集 */publicstatic<T>List<T>intersection(List<T> list1,List<T> list2){if(list1 ==null|| list1.isEmpty()|| list2 ==null|| list2.isEmpty()){returnnewArrayList<>();}Set<T> set =newHashSet<>(list2);return list1.stream().filter(set::contains).collect(Collectors.toList());}/** * 获取两个List的差集(list1 - list2) */publicstatic<T>List<T>difference(List<T> list1,List<T> list2){if(list1 ==null|| list1.isEmpty()){returnnewArrayList<>();}if(list2 ==null|| list2.isEmpty()){returnnewArrayList<>(list1);}Set<T> set =newHashSet<>(list2);return list1.stream().filter(t ->!set.contains(t)).collect(Collectors.toList());}/** * 对List中的自定义对象进行排序 * @param list 待排序集合 * @param comparator 比较器 */publicstatic<T>voidsortList(List<T> list,Comparator<T> comparator){if(list ==null|| list.isEmpty()|| comparator ==null){return;}Collections.sort(list, comparator);}// 测试方法publicstaticvoidmain(String[] args){// 测试去重List<Integer> list =Arrays.asList(1,2,3,2,1,4);List<Integer> distinctList =distinctList(list);System.out.println("去重后的集合:"+ distinctList);// 测试交集List<Integer> list1 =Arrays.asList(1,2,3,4);List<Integer> list2 =Arrays.asList(3,4,5,6);List<Integer> intersection =intersection(list1, list2);System.out.println("交集:"+ intersection);// 测试差集List<Integer> difference =difference(list1, list2);System.out.println("差集:"+ difference);// 测试自定义对象排序List<Student> studentList =newArrayList<>(); studentList.add(newStudent("003","王五",20)); studentList.add(newStudent("001","张三",18)); studentList.add(newStudent("002","李四",19));// 按年龄升序排序sortList(studentList,Comparator.comparingInt(Student::getAge));System.out.println("按年龄排序后的学生集合:"); studentList.forEach(System.out::println);}}// 学生类classStudent{privateString id;privateString name;privateint age;publicStudent(String id,String name,int age){this.id = id;this.name = name;this.age = age;}publicintgetAge(){return age;}@OverridepublicStringtoString(){return"Student{id='"+ id +"',+ name +"', age="+ age +"}";}}

输出结果

去重后的集合:[1, 2, 3, 4] 交集:[3, 4] 差集:[1, 2] 按年龄排序后的学生集合: Student{id='001', name='张三', age=18} Student{id='002', name='李四', age=19} Student{id='003', name='王五', age=20} 

1.5.3 案例总结

✅ 这个工具类综合运用了 List 和 Set 的核心知识,解决了开发中常见的集合处理问题。
通过 LinkedHashSet 实现去重并保留顺序,通过 HashSet 提升交集和差集的计算效率,通过 Comparator 实现自定义排序。

1.6 本章总结

  1. List 是有序可重复集合,ArrayList 适合查询,LinkedList 适合增删。
  2. Set 是无序不可重复集合,HashSet 效率最高,LinkedHashSet 保留插入顺序,TreeSet 支持排序。
  3. 存储自定义对象时,HashSet 需要重写 hashCode()equals(),TreeSet 需要指定排序规则。
  4. 多线程环境下,优先使用 JUC 包下的 CopyOnWriteArrayListCopyOnWriteArraySet 保证线程安全。
  5. 集合工具类可以封装常用功能,提升开发效率。

Read more

通义灵码 AI 程序员 实操全指南:从 IDE 安装到全栈需求落地(多文件批量修改 + 报错自动修复 + 跨语言开发)

通义灵码 AI 程序员 实操全指南:从 IDE 安装到全栈需求落地(多文件批量修改 + 报错自动修复 + 跨语言开发)

1. 背景与趋势 随着软件系统复杂度提升,传统开发模式面临代码重复率高、调试周期长、跨语言协作难等挑战。AI辅助编程已从单文件代码补全,演进为项目级代码理解、全流程开发辅助的核心生产力工具。通义灵码作为AI程序员,整合代码生成、重构、调试、多语言协作等能力,可覆盖从需求分析到部署上线的完整开发链路。 2. 核心技术原理 2.1 代码预训练与多语言理解 基于大规模代码语料(覆盖100+编程语言、10TB+开源代码),采用Transformer架构的代码大模型,学习语法规则、语义逻辑、设计模式及最佳实践,支持Java、Python、Go、Rust、TypeScript等主流语言的深度理解。 2.2 上下文感知与长序列处理 支持100K+ Token上下文窗口,可解析项目级代码结构(包括多文件依赖、类继承关系、API调用链),实现跨文件的逻辑一致性校验与修改。 2.3 多模态交互与工具链集成 支持自然语言、代码片段、错误日志、

By Ne0inhk
人工智能:自然语言处理在法律领域的应用与实战

人工智能:自然语言处理在法律领域的应用与实战

自然语言处理在法律领域的应用与实战 学习目标 💡 理解自然语言处理(NLP)在法律领域的应用场景和重要性 💡 掌握法律领域NLP应用的核心技术(如法律文本分类、实体识别、合同分析) 💡 学会使用前沿模型(如LegalBERT、LexGLUE)进行法律文本分析 💡 理解法律领域的特殊挑战(如专业术语、法律规范、数据稀缺) 💡 通过实战项目,开发一个合同分析应用 重点内容 * 法律领域NLP应用的主要场景 * 核心技术(法律文本分类、实体识别、合同分析) * 前沿模型(LegalBERT、LexGLUE)在法律领域的使用 * 法律领域的特殊挑战 * 实战项目:合同分析应用开发 一、法律领域NLP应用的主要场景 1.1 法律文本分类 1.1.1 法律文本分类的基本概念 法律文本分类是将法律文本划分到预定义类别的过程。在法律领域,法律文本分类的主要应用场景包括: * 判例分类:将判例分为不同的类别(如民事、刑事、行政) * 法律文件分类:

By Ne0inhk
Flutter for OpenHarmony:graphql_codegen 让 GraphQL 开发如丝顺滑,自动化生成类型安全的 Dart 代码(Schema 到 Model) 深度解析与鸿蒙适

Flutter for OpenHarmony:graphql_codegen 让 GraphQL 开发如丝顺滑,自动化生成类型安全的 Dart 代码(Schema 到 Model) 深度解析与鸿蒙适

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 在 GraphQL 开发中,手动解析 JSON 是极其低效且易出错的。graphql_codegen 通过自动生成的强类型 Dart 代码,让你的开发体验从“黑盒解析”进化到“全量代码提示”。 本指南将结合 OpenHarmony 环境,详细介绍如何配置、编写以及解决常见的版本与构建报错。 一、 核心原理解析 graphql_codegen 的工作流程可以概括为:输入(Schema + Query) -> 编译 -> 输出(Type Safe Dart Code)。 * Schema (lib/schema.graphql): 它是服务端的“说明书”

By Ne0inhk
移动端也能玩转!OpenClaw iOS/Android 端部署教程,语音唤醒 + 全场景随身 AI 助手

移动端也能玩转!OpenClaw iOS/Android 端部署教程,语音唤醒 + 全场景随身 AI 助手

一、背景与价值:随身AI助手的刚需场景 随着大语言模型技术的普及,全场景AI助手的需求日益增长——无论是通勤途中的语音笔记、户外场景的实时翻译,还是离线环境下的知识查询,移动端随身AI都能解决传统桌面AI的场景局限。OpenClaw作为一款轻量级、可离线运行的开源AI框架,支持语音唤醒、多模态交互等核心功能,完美适配iOS/Android双平台部署,为用户打造真正的随身AI助手。 二、核心原理:OpenClaw移动端部署的技术逻辑 OpenClaw的移动端部署核心是将轻量化大语言模型(如Qwen-2-0.5B-Instruct)、语音唤醒模型(如PicoVoice Porcupine)与移动端推理引擎(如MLKit、TensorFlow Lite)进行整合,实现三大核心流程: 1. 低功耗语音唤醒:通过本地运行的轻量唤醒模型监听关键词,避免持续调用麦克风导致的高功耗; 2. 本地推理加速:利用移动端硬件加速(NNAPI、Core ML)运行量化后的大语言模型,实现离线交互; 3. 跨平台适配:通过Flutter或React Native统一代码底座,同时适配iOS的沙箱

By Ne0inhk