ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

网易Java校招笔试复盘:从集合框架到JVM的考点地图

网易Java校招笔试复盘:从集合框架到JVM的考点地图 秋招季有人把网易2020校招笔试的Java开发工程师正式批试题翻出来复盘我顺手把当年的笔记也翻了一遍。说实话这套卷子在过去几年里不断被当成“Java八股文”素材流传面试题、基础题、容器题、算法题都被拆得七零八落。但我重新过了一遍之后发现与其去背散装的面经不如把它当一张考纲地图来用——网易这套题真正反映的是企业筛选Java工程师时的底层逻辑语言基础扎不扎实集合框架会不会用JVM有没有概念算法能不能手写工程环境能不能跑通。这篇文章我就按这套逻辑把考点拆开讲结合这几年踩过的坑给准备校招笔试的人一份能直接照着复习的路线。1. 网易这套笔试卷的考点地图与复习优先级1.1 试卷结构与高频模块我自己复盘了2020年正式批的笔试题型分布网易笔试Java方向的题目结构大概是选择题语言基础、集合框架、并发、JVM 编程题算法与数据结构 少量问答题或设计题。和前几年批次相比2020正式批明显加强了对Java基础的考察密度尤其是面向对象编程、枚举类型、异常处理和容器这几个方向的单选题数量不少。这类分布其实不是网易独有的它代表了主流互联网公司笔试的通用思路先筛掉基础不过关的人再在编程题里筛掉代码能力弱的人。而所谓“基础过关”并不只是知道关键字和语法而是能识别出题目里埋的坑。比如“java中数组越界异常”这种热搜词看着像送分题实际考察的是索引边界、循环边界和集合与数组转化时的隐蔽问题。1.2 热搜词背后的考察信号我把与这套题相关的高频热搜词梳理了一遍能明显看出当下Java求职者关注什么也能反推笔试出题方向Java基础、Java面试题、Java面试八股文说明绝大多数人都在背面试题但笔试考察的是能不能活学活用不是背诵复述。HashMap、集合容器、Comparator.comparing集合框架一直是笔试选择题和编程题的交汇点尤其是排序比较器的变形题。OutOfMemoryError、JVM这部分直接对应笔试中的JVM选择题和面试中的深入追问。冒泡排序Java、快速排序Java实现编程题的热门考点也是手写算法的基础题。Java环境变量配置、源发行版17错误、Lombok编译报错虽然笔试在线OJ不会考环境配置但面试前的本地练习阶段这些问题会浪费大量时间。这些热搜词叠加起来我的结论是笔试考的是“听得懂、想得到、写得对”不只是“背得熟”。1.3 复习优先级怎么排如果给这套卷子排一个复习优先级我的建议是集合框架与容器最高优先单选题常考编程题也会用到是笔试的基本盘。Java语法细节标识符规则、运算符与表达式、枚举、异常、数组边界性价比很高短期就能提分。算法手写以排序、链表、栈、队列、字符串处理为主不需要死磕难题但经典题目必须能15分钟内写完并跑通。JVM基础概念重点在内存分区、OOM场景、垃圾回收基础不需要深抠调优参数。框架与工程化笔试直接考察的相对少但对于面试和后续实习/试用期属于拉开差距的部分。这套优先级照顾了“短期应试”和“长期成长”两方面下面按这个顺序逐个模块拆解。2. Java语言基础模块送分与送命只隔一层窗户纸2.1 标识符命名、运算符与表达式规则题为什么总有人错“java标识符命名规则”是搜索热词但它通常不出现在笔试大题里而是藏在选择题里当干扰项。常见的命题方式有下面哪个是合法标识符下面哪个表达式的结果为true标识符规则本身不复杂标识符由字母、数字、下划线_、美元符号$组成不能以数字开头不能是Java关键字理论上汉字和$都是合法的但极不建议使用。容易踩的坑有两类一是忘了中文可以当标识符某些网题会把“Int”“class1”“_name”“$value”和“1value”混在一起你得能从选项里准确挑出“1value”是非法项二是把关键字记混比如true、false、null虽然不是关键字但是字面量不能用作变量名很多选择题在这上面设坑。运算符与表达式的选择题则是另一类套路重点看这几个i和i的返回值区别。和、|和||的区别位运算 vs 短路逻辑。整数除法与浮点数除法的差异尤其是3/2的结果是1而不是1.5。三元运算符? :的类型提升问题。我记得网易这套试卷里有一道关于表达式求值的题表面在考优先级实际坑在自增运算和赋值的先后顺序。这种题没太多技巧平时多写几段代码、用IDEA的Evaluate表达式验证一下就记住了。2.2 面向对象三大特性在笔试里怎么“变形”面向对象编程几乎是每套Java笔试题的必考板块但它的考法很少直接问“封装是什么”而是用代码片段让你判断结果或者找设计问题。封装的考察点集中在访问修饰符private、default、protected、public各自的作用范围以及同包、同包子类、异包子类的可见性差异。这类题要注意protected在异包子类中只能通过子类对象的引用访问从父类继承来的成员不能通过父类引用访问子类对象的父类受保护成员。这种话读起来拗口但选择题就爱这么出。继承的考察点集中在初始化顺序。经典题型是父类静态块、父类实例块、父类构造器、子类静态块、子类实例块、子类构造器一起出现问你输出顺序。标准答案是父类静态块 - 子类静态块 - 父类实例块 - 父类构造器 - 子类实例块 - 子类构造器。这个顺序我已经替无数同学在笔试现场验证过了必须背到形成条件反射。多态的考察点集中在重写与重载的区别、动态绑定。相关热词里“java聚合”也是这里经常出现的一个概念题目可能给出“汽车和发动机”让你判断是聚合关系还是组合关系。记住一个简单的判别法聚合是“整体-部分”但部分可独立存在组合是“整体-部分”且部分生命周期随整体。汽车没了发动机就不能开但发动机本身可以单独存在所以是聚合人没了心脏不能活心脏也不能脱离人体所以是组合。2.3 枚举类型最容易被忽视的细节“java枚举类型的使用”这个热搜词说明很多人复习时跳过枚举但网易这类大厂笔试特别喜欢考enum。原因很简单枚举能考察你对类机制的理解程度。枚举的核心考点有三个1. 枚举本质是一个类而且默认继承java.lang.Enum。所以枚举不能显式继承其他类但可以实现接口。题目可能会问“枚举可以被反射创建实例吗”标准答案是通过反射的newInstance是禁止的因为Enum类没有公开的构造器逻辑支持实际上Constructor.newInstance()对枚举类型会直接抛异常。2. 枚举的构造器是私有的。枚举常量在类加载时创建构造器只能私有这是保证单例的一种方式。3. 枚举的switch支持。Java 8以后可以在switch中直接使用枚举常量做题时要注意case分支不能带枚举类型前缀直接写常量名即可。另外枚举实现单例是《Effective Java》推荐的写法因为它天然线程安全且能防止反序列化破坏单例这知识点在面试环节是加分项笔试选择题偶有出现。2.4 异常体系与数组越界笔试常客“java中数组越界异常”能成为热搜词是因为它不仅选择题考编程题也会因为边界处理不当导致运行时崩溃直接影响判分。先看基础Java异常体系结构要能画出来Throwable是顶层下面分Error和Exception。Exception分为受检异常Checked和运行时异常RuntimeException。常见的ArrayIndexOutOfBoundsException、NullPointerException、ClassCastException都是RuntimeException子类编译期不强制处理运行时才可能抛出。选择题常问“下面哪个异常是受检异常”答案往往就是IOException、SQLException、ClassNotFoundException这几个。数组越界的高频场景在笔试编程题中通常是二重循环操作二维数组时比如遍历矩阵是i rows还是i rows差一个等号就数组越界。还有一个隐蔽场景把List转成数组时用toArray(new String[0])如果用toArray()无参版本在特定场景下可能得到Object[]再强转报错这也是选择题爱埋的坑。注意在线笔试平台对运行时异常的判题是零容忍的即使核心逻辑写对了只要有一个测试用例触发数组越界整题得分的可能性就非常低。所以我强烈建议在笔试前养成一个习惯所有数组循环都写成“上限不包含边界”的形式比如for (int i 0; i arr.length; i)避免i arr.length - 1这种写法。3. 集合容器与Lambda笔试里最被高估的“简单题”3.1 集合容器考点地图“java容器”这个热搜词覆盖面很广它对应的是Collection和Map两大体系。笔试选择题最常考的容器有以下几类基本按优先级排HashMap数据结构数组链表红黑树、put流程、扩容机制、为什么线程不安全、与Hashtable的区别。ConcurrentHashMapJDK 7分段锁和JDK 8 CASsynchronized的实现差异。ArrayList vs LinkedList底层结构、增删改查复杂度、扩容机制。HashSet vs TreeSet底层依赖HashMap和TreeMap以及有序性来源。LinkedHashMap有序性的两种模式插入序和访问序可以用来实现LRU缓存。网易笔试题对HashMap的考察密度很高而且经常把并发场景结合起来。有一道印象深的题是这样的多个线程同时对一个HashMap执行put操作会发生什么选项包括覆盖丢数据、死循环、抛异常、以上都有可能。JDK 7及以前在并发扩容时可能形成环形链表导致死循环JDK 8以后修复了这个问题但覆盖丢数据和size不准的问题仍然存在。所以标准答案应该是“以上都有可能”这也是大多数人容易选错的点。3.2 Comparator.comparing 实战把指定元素排到第一位热搜词里有一个非常具体的需求“java comparator.comparing 将某元素值放第一个”。这个需求虽然不是网易原题考法但它背后的comparing用法绝对是笔试编程题的高频工具型知识点。比如有一道题要求给一个学生列表按成绩降序排列如果成绩相同则按学号升序。用Java 8的写法就是students.sort(Comparator.comparing(Student::getScore, Comparator.reverseOrder()) .thenComparing(Student::getId));如果想把某个特定状态比如“待审核”排在最前面可以使用自定义比较器MapString, Integer order Map.of(待审核, 0, 已通过, 1, 已拒绝, 2); list.sort(Comparator.comparing(item - order.getOrDefault(item.getStatus(), 99)));这道题背后真正考的是Comparator的链式调用、方法引用、以及自定义排序键。很多人在笔试里明明思路对了却栽在comparing的泛型推断上导致编译失败。比如直接用Comparator.comparing(item - item.getStatus())时因为类型推断不出来而报错解法就是显式指定类型Comparator.comparing((Item item) - item.getStatus())。经验在线笔试的代码编辑器通常没有IDEA那么智能的类型推断提示写Comparator.comparing这类泛型方法时优先用方法引用而不是Lambda表达式比如Comparator.comparing(Item::getStatus)能避开一大部分编译报错。3.3 Lambda与函数式接口的笔试陷阱“lambda函数 java” 能上热搜说明很多人对这个语法认识停留在“能写出来”的层面但笔试考的是“知道为什么”。Lambda表达式本质是函数式接口的实例而函数式接口是“只包含一个抽象方法的接口”。常见的函数式接口有FunctionT,R、PredicateT、ConsumerT、SupplierT。选择题常考的是下面这种下面哪段代码能编译通过A.Runnable r () - System.out.println(hi);B.Object o () - System.out.println(hi);C.new Thread(() - System.out.println(hi)).start();D.ListString list new ArrayList(); list.forEach(System.out::println);B不能编译因为Object不是函数式接口Lambda必须赋值给函数式接口类型或作为函数式接口参数的实参。这类题理论上是送分题但每年都有人选错根源是光背语法没理解“函数式接口”这个底层约束。3.4 equals 与 hashCode笔试与面试的连体婴网易笔试有一种组合拳先让你判断两个对象相等用什么方法再追问HashSet去重的原理。这里面的核心点就两个1. equals相等hashCode必须相等。这是约定也是规范违反它会导致HashMap中放进去的对象get不到。2. hashCode相等equals不一定相等。这是哈希碰撞。笔试编程题经常让实现一个类的equals和hashCode通常写法是Override public boolean equals(Object o) { if (this o) return true; if (o null || getClass() ! o.getClass()) return false; User user (User) o; return age user.age Objects.equals(name, user.name); } Override public int hashCode() { return Objects.hash(name, age); }注意这里用getClass() ! o.getClass()而不是instanceof因为instanceof会对子类也返回true破坏equals的对称性。这个细节是选择题高发考点。4. JVM与OutOfMemoryError拉开差距的分水岭题目4.1 内存区域分区与堆栈差异Java类的笔试选择题里JVM相关内容是拉开差距的关键因为它需要理解而不是背诵。首先要把运行时数据区五个部分记牢程序计数器、虚拟机栈、本地方法栈、堆、方法区JDK 8以后是元空间。高频选择题考点如下线程私有程序计数器、虚拟机栈、本地方法栈。线程共享堆、方法区。对象实例存在堆里局部变量和引用存在虚拟机栈里。字符串常量池JDK 7以前在方法区永久代JDK 7以后移到堆里JDK 8把“永久代”彻底改成元空间存放类元信息。网易笔试有一道很典型的题问“Java中所有对象实例以及数组都要在哪个区域分配”很多人会答堆但严格来说JIT编译器优化的逃逸分析如果判断对象不会逃逸出方法可能分配到栈上这就是栈上分配。问到这个层面的题就是在考察你对JVM进阶知识的储备而不仅仅是背诵内存分区。4.2 一个OutOfMemoryError: insufficient memory的实战排查热搜词里有一条“java: outofmemoryerror: insufficient memory”这不是标准JVM异常信息标准的是“Java heap space”、“GC overhead limit exceeded”等但这恰恰反映了现实中很多人遇到的其实是操作系统层面的内存不足或者是容器内存限制导致的无法分配内存。笔试如果出OOM相关题通常有三个角度1. 堆内存溢出Java heap space不断创建对象且无法回收。常见原因是集合类静态持有对象、缓存无上限、大对象过多。排查用jmap -dump导出堆转储然后用MAT分析Leak Suspects。2. 栈溢出StackOverflowError递归调用太深、无限递归。不同于OOM它是Error但是是java.lang包下的异常选择题会问它属于Error还是Exception。3. 元空间/方法区溢出Metaspace动态生成大量类CGLIB代理、反射、热部署类加载器没有卸载时出现。我在实际工作中遇到过一种很容易误判的OOM应用部署在容器里容器限定了内存但JVM启动参数没有感知容器限制导致JVM按宿主机内存来设置堆大小结果物理内存不够直接报“insufficient memory”。排查方式是通过docker stats查看容器内存再对比JVM的-Xmx参数。所以如果你在笔试里看到“OOM可能由外部限制导致”这个选项它是对的虽然不在主流JVM教程里。提示笔试复习JVM不用一上来就背调优参数先把“堆、栈、方法区各存什么”“什么情况会发生OOM”“OOM有哪几种类型”这三个问题弄清再去看GC算法和收集器这样面试被追问也不慌。4.3 垃圾回收与调优问答的答题姿势垃圾回收方向笔试高频考点是垃圾标记算法引用计数法难以解决循环引用、可达性分析GC Roots。GC Roots有哪些虚拟机栈中引用的对象、静态属性引用的对象、常量引用的对象、JNI引用的对象。分代收集理论新生代Eden、Survivor From、Survivor To、老年代。常见收集器Serial、Parallel、CMS、G1各自的特点和适用场景。如果笔试有简答题问“CMS和G1有什么区别”可以答三个层面适用区域不同CMS针对老年代、G1是整堆收集、停顿时间可控性不同CMS基于标记清除有碎片、G1基于Region可以预测停顿、内存布局不同CMS分区物理连续、G1逻辑分代但物理不连续。这三条答上去基本就是面试官想听到的框架。5. 手写算法真题复盘从冒泡排序到列车调度类应用题5.1 冒泡排序简单但有手写翻车点“冒泡排序java”热度高是因为面试官真正让你手写时很多人写不对。笔试题通常不会直接让写冒泡而是让你在选择题里判断某种排序的时间复杂度和稳定性或者给一串初态让你写出第一趟排序后的结果。冒泡排序的标准写法public static void bubbleSort(int[] arr) { int n arr.length; for (int i 0; i n - 1; i) { boolean swapped false; for (int j 0; j n - 1 - i; j) { if (arr[j] arr[j 1]) { int temp arr[j]; arr[j] arr[j 1]; arr[j 1] temp; swapped true; } } if (!swapped) { break; } } }关键点有三个外层循环次数是 n-1内层循环终止条件是 n-1-i因为每趟都能把当前最大值放到末尾无需再比较尾部已排序区域加入swapped标志位提前结束。选择题经常问“最好情况时间复杂度”加入标志位后是O(n)没加的写法即使传入有序数组也要走完两层循环还是O(n²)。这就是“知道概念但不写优化”和“真正会写”的区别。5.2 快速排序的两种实现与基准值选择快速排序是网易笔试编程题的高频底子“快速排序java实现”这个热搜词的背后是因为快排既考分治思想又考边界处理能力。快速排序的核心是partition。常见实现有两种Lomuto分区单向扫描代码短但常数大public static void quickSort(int[] arr, int left, int right) { if (left right) return; int pivotIndex partition(arr, left, right); quickSort(arr, left, pivotIndex - 1); quickSort(arr, pivotIndex 1, right); } private static int partition(int[] arr, int left, int right) { int pivot arr[right]; int i left; for (int j left; j right; j) { if (arr[j] pivot) { swap(arr, i, j); i; } } swap(arr, i, right); return i; }Hoare分区双向扫描交换次数少但实现细节多。笔试建议背Lomuto版本因为不容易写错但要知道时间复杂度是O(n log n)最坏情况O(n²)发生在基准值选择不当导致极端不平衡时。网易的算法题很少直接“让你写快排”但很多排序相关题求第K大、最小K个数、按照频率排序等都用到partition思想。求第K大就利用了分区后pivotIndex的位置来缩小范围这个变形思路必须掌握。5.3 列车调度类应用题怎么转化为栈/队列/贪心热搜词里有“列车调度java”这类题是经典的数据结构应用题在互联网笔试里经常以“火车进站出站”“车厢重排”等变体出现。比如题目描述一列火车有编号1到n的车厢按给定顺序进站问能否通过栈中转站按顺序出站。这种题本质是模拟栈。思路如下维护一个待入栈的序列in一个目标出栈序列out。遍历出栈序列如果当前要出的车厢不在栈顶就不断从“待入栈”序列中压栈直到栈顶匹配然后弹出。如果所有车厢都压完栈了栈顶还不匹配说明序列不可能。伪代码StackInteger stack new Stack(); int index 0; boolean possible true; for (int target : out) { while (index n (stack.isEmpty() || stack.peek() ! target)) { stack.push(in[index]); index; } if (stack.peek() target) { stack.pop(); } else { possible false; break; } }这种题的价值在于它考的不是某个难题而是你能不能把一个现实场景抽离成数据结构问题。刷题时遇到类似的“括号匹配”“表达式求值”“最小栈”都建议整理到一个“栈的应用”专题里这类题型的解法套路是高度统一的。5.4 编程题的刷题策略与时间分配网易笔试的编程题通常2到4道难度从简单到中等偏上。我的建议是刷题和现场时间分配并重刷题阶段LeetCode 前200题中的简单中等题足够覆盖大多数笔试。重点专题数组、字符串、链表、二叉树、栈与队列、哈希、双指针、二分、排序、贪心。现场时间分配先把所有编程题都看一遍从最容易拿分的题开始写。即使某道题没有完全AC也要把自己能想到的暴力解法写上去很多OJ是按部分用例给分的。我自己之前吃过一个亏最后一道题最难但分值高我先死磕它结果前两道简单题没写完总分反而不如先做简单题的同学。校招笔试本质上是在有限时间内尽可能多拿分不是做学术研究。6. 环境配置与框架工程化让阅卷人和面试官看到你“能干活”6.1 JDK环境变量与“源发行版17需要目标发行版17”报错笔试本身是在线OJ不考环境配置但准备笔试需要在本地IDE里练题。这时候“java环境变量配置”这个热搜词的意义就体现出来了。本地练习最常见的问题就是编译报错“java: 警告: 源发行版 17 需要目标发行版 17”或者相反。这个问题的本质是IDEA里Project SDK设置了Java 17但Java Compiler的target bytecode version或者Maven的maven.compiler.source/target还是旧版本比如1.8导致编译选项不一致。排查顺序建议打开File - Project Structure - Project确认SDK是17Language Level是17。打开Settings - Build, Execution, Deployment - Compiler - Java Compiler确认target bytecode version是17。如果是Maven工程检查pom.xml里是否配了maven.compiler.source和maven.compiler.target或者properties里的java.version。这个报错的坑在于有时候改了Project StructureIDEA不会自动同步编译器的target选项必须手动去Compiler设置里改或者重装project。我在帮学弟学妹看代码时遇到最多的环境问题就是这一个。6.2 Spring Boot接口安全对接与接口自动化测试虽然试卷是2020年的但这几年面试里“项目经验”的考察比例越来越高。网易这类公司不会只看算法题简历上有Spring Boot项目经验的候选人面试时会被追问接口安全、接口测试这些工程化问题。“java springboot apikey 安全对接”这个热搜词反映的是很实际的场景如何给第三方提供接口又不让接口裸奔。最简单的方案是API Key 签名验证分配一个AppId和AppSecret给调用方。请求参数按字典序拼接加时间戳和nonce用AppSecret做HMAC-SHA256签名。服务端用同样的规则重新签名对比是否一致同时校验时间戳防重放。对应的“java接口自动化测试框架”最轻量的路径是RestAssured TestNG/JUnit 5 Maven。RestAssured的优势是能用链式语法直接校验状态码、响应体和JsonPath比手动写HttpClient快得多。笔试后的面试如果被问到测试能说出“用RestAssured做接口自动化断言从状态码覆盖到响应体字段”就足够了。6.3 Lombok与MapStruct编译报错的坑“java: you arent using a compiler supported by lombok”以及“java: internal error in the mapping processor: java.lang.nullpointerexception”都在热搜里这两个报错都出现在本地开发环境也是笔试准备阶段特别磨人的问题。Lombok那个报错通常发生在IDEA版本和Lombok版本不兼容时或者项目用了高版本JDK但Lombok版本太旧比如JDK 17配Lombok 1.18.20以下通常需要升级到1.18.22。解决方式有两个升级Lombok依赖或在IDEA里安装/更新Lombok插件并开启Annotation Processing。MapStruct那类NullPointerException报错多半是因为编译时没有配置annotationProcessorPaths导致注解处理器没有正确执行。Maven工程需要在pom.xml里显式声明plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-compiler-plugin/artifactId configuration annotationProcessorPaths path groupIdorg.mapstruct/groupId artifactIdmapstruct-processor/artifactId version1.5.5.Final/version /path /annotationProcessorPaths /configuration /plugin这些报错虽然不在笔试里考但如果你在准备笔试期间自己搭过Spring Boot项目把这些坑踩一遍面试时讲到项目反而能说得更真实。面试官听过的项目都差不多能讲清“我在环境搭建阶段遇到什么问题、怎么解决的”比单纯说“我做过某某管理系统”更有说服力。6.4 从笔试到面试卷面之外还能准备什么网易校招的完整流程通常是笔试 - 面试技术面HR面 - Offer。笔试只是筛选门槛真正的定级在面试。所以刷这套题不能只刷题本身还要顺着题目延伸准备笔试试卷里出现过的JVM题面试大概率会追问GC回收算法和线上OOM排查。笔试里让你手写排序面试可能让你继续分析时间复杂度和稳定性再让你写一个“稳定的快排”实际通常用归并。简历里写了Spring Boot面试官很可能问“如何设计一个防止接口重复提交的方案”这属于必考题。近两年的面试还会问一些偏实践的内容比如“ES异步写入Java”的实现思路批量提交、失败重试、缓冲区设计或者数据库到向量数据库的集成比如LangChain4j调用Embedding模型并存储Milvus。这些虽然不在2020年的卷子里但如果你准备得足够深面试时能在这里给面试官留下“这个候选人持续在学”的印象。我的经验是每次笔试题不管有没有通过都值得花时间把错题整理成一份自己的八股文笔记。不要直接复制别人的“Java面试题大全”因为每个人的知识盲区不一样只有自己做错的题才是真正需要补的知识点。我当年那张网易笔试的卷子最后变成了一个Excel表格记录了大概三十多个错题覆盖基础语法、集合、JVM、算法四类。后续再参加其他公司的笔试很多选择题的考点都是重复的那份笔记直接帮我提高了后面几场笔试的通过率。最后再分享一个我后来带新人时反复强调的做法笔试前三天不要再看新知识只做三件事——把HashMap的put流程口头讲一遍把快速排序在纸上无参考写一遍把所有集合类的线程安全性过一遍。这三件事看着简单但每年都有大量同学在其中一个环节卡壳。卡壳不是坏事早发现早补总比笔试当场卡壳强。
返回列表