新闻详情

新闻详情

首页 / 资讯中心 / 详情

数组常用操作跨语言实战指南:初始化、排序、去重与切片

发布时间:2026/9/30 3:49:01来源:尧图网络
数组常用操作跨语言实战指南:初始化、排序、去重与切片
数组的相关常用操作可以说是所有写代码的人都绕不开的一门基础功课。不管你是前端处理接口返回的列表、后端做批量数据统计还是数据分析时切分一张二维表格每天都要跟数组打交道。这篇内容我不想再重复教科书里那种数组是一种线性数据结构的定义而是把我在日常开发里真正高频使用、真正踩过坑的数组操作一次性铺开来讲尽量覆盖JavaScript、Java、Python/NumPy、C/C、VBA这些常用语言里的通用场景和差异点。适合刚入门但想看懂项目代码的初学者也适合每天被业务代码里各种数组问题折腾的开发者直接参考。这个主题看起来简单真深入进去会发现水很深。比如数组初始化在不同语言里有完全不同的规则稍不注意就踩了未定义内存的坑再比如数组去重常规写法能写出七八种每种适用场景还不一样更别说Python切片、numpy取多列、VBA里两个数组做对比这种看起来基础、实际一堆细节的操作。我把自己这些年积累的经验整理成了一套可以直接照抄的操作手册下面逐步展开。1. 先理清数组的本质连续内存、动态数组、对象数组1.1 数组的内存布局与底层原理不管是C语言里老老实实的原生数组还是JavaScript里那种啥都能塞的数组底层都离不开一块连续的内存区域。连续内存是数组一切性能优势的来源——因为地址是挨着的CPU访问第i个元素时可以靠起始地址 偏移量直接算出来时间复杂度是O(1)。这就带来了一个非常直观的结论数组适合查得多、增删少的场景。如果主要操作是按下标读取元素数组几乎是无敌的。但如果频繁在中间插入、删除元素那每次都要把后面的元素整体搬家数据量一大就会明显卡顿。我在实际项目里见过不少人用数组做队列反复shift、unshift结果数据到几千条的时候页面直接假死这就是没理解连续内存的特性。另外一个容易被忽略的点C/C里数组名在很多场合会退化成指向首元素的指针。这一点是新手最容易懵的地方。比如你写了一个函数void func(int arr[])在函数内部用sizeof(arr)想去算数组长度得到的其实是指针的大小64位机器上是8字节而不是数组本身的字节数。这也是后面要专门讲数组长度计算的原因。1.2 动态数组与扩容逻辑原生数组的长度在定义时就要确定但实际业务里我们更多是用动态数组。Java的ArrayList、C的vector、JavaScript的数组本质都是动态数组它们的扩容策略大致相同当元素个数达到容量上限时新分配一块更大的内存通常是原来的1.5倍或2倍再把旧数据拷贝过去。这里有个性能经验如果能预估大概的数据量最好在创建动态数组时就指定初始容量。Java里可以new ArrayList(1000)C里可以vector.reserve(1000)。不要小看这个动作避免多次扩容拷贝在处理几万条数据时差距非常明显。我做过一个批量导入功能一开始没设初始容量导入50000条记录耗时一度到了800多毫秒加了一个初始容量参数后直接掉到200毫秒以内。至于JavaScript的数组它的动态扩容是引擎层面自动处理的开发者没法直接干预。但我们可以尽量避免在循环里一个一个push如果数据源本身是一个可迭代对象用Array.from()或者剩余参数一次性展开比循环push要高效。1.3 对象数组、JSON数组与PHP接口返回的数组数组里的元素不一定是基本类型更多时候是对象。前端拿到接口返回的[{id: 1, name: 张三}, {id: 2, name: 李四}]Java里对应ListMapString, Object或者ListUserC里可能是vectorstruct。这本身不复杂复杂的是对对象数组做筛选、排序、去重时得知道怎么取字段、怎么比较。这里重点说下PHP接口返回的坑。在PHP里json_decode($json, true)第二个参数决定返回的是关联数组还是对象。如果不传true返回的是stdClass对象前端拿到后用法完全不同。我遇到过不止一次联调时接口文档没说清楚前端按数组处理拿data[name]实际返回的对象要>int a[5] {1, 2, 3, 4, 5}; // 完全初始化 int b[5] {1, 2}; // 剩余元素自动补0 int c[5] {}; // 全部初始化为0 int d[5]; // 这行要特别注意只声明不初始化最后一行int d[5];里元素的值是不确定的取决于栈上的遗留数据。如果后续逻辑直接拿d[i]来做加法或者判断结果完全不可预期。所以我的习惯是只要不是对性能极度敏感的场景定义时就顺手初始化别给bug留机会。字符串数组的初始化更是经典坑位。char s[10] hello;看起来很直观但这个字符串实际占据11个字节末尾还有\0如果声明成char s[5]就会溢出。而const char* arr[] {apple, banana, cherry};是指针数组每个元素指向一个字符串字面量这种声明可以用来做配置文件式的静态数据特点是只读、不可修改。还有一个高频操作把std::string转成char数组。用strcpy(dest, str.c_str())之前一定要先确认dest的空间足够否则越界写是CV上查都查不出来的隐蔽bug。稳妥的做法是用snprintf限制最大长度或者直接用std::vectorchar来动态管理大小。2.2 JavaScript数组初始化的几种方式JS数组初始化的复杂度在于稀疏数组和密集数组的区分。先看这段代码const arr1 new Array(5); // 创建一个长度为5但没有任何元素的稀疏数组 const arr2 [1, 2, 3, 4, 5]; // 常规方式 const arr3 Array.from({length: 5}, (_, i) i); // 创建一个密集数组new Array(5)创建的数组长度是5但没有任何实际元素。你调用arr1.map(x x * 2)得到的结果还是一个全是空位的数组因为map根本不会遍历空位。这个坑在刷算法题时经常遇到我见过有人想创建一个长度为n的数组并全部初始化为0写了new Array(n).map(() 0)结果完全没效果后面才意识到要先fill。正确的密集数组初始化方式是new Array(n).fill(0)或者用Array.from({length: n}, (_, i) i)同时把索引也填好。另外如果要做二维数组比如3行4列全0的矩阵直接new Array(3).fill(new Array(4).fill(0))是有大坑的——每一行其实都是同一个数组的引用改一个元素其它行也跟着变。正确做法是Array.from({length: 3}, () new Array(4).fill(0))确保每行都是独立数组。2.3 Python/NumPy创建数组、元素为汉字与切片Python里创建数组最常用的是list和numpy.array。普通列表可以混合类型[1, a, 3.14]完全合法。但如果数据量大、要做数值计算一定要用NumPy因为numpy.array是存储在连续内存里的同构数据计算速度比纯Python list快几个数量级。创建NumPy数组也很简单import numpy as np arr np.array([1, 2, 3, 4]) # 从列表创建 zeros np.zeros((3, 4)) # 3行4列全0 ones np.ones(5) # 5个1 range_arr np.arange(10) # 0到9如果元素是汉字Python 3默认字符串就是Unicodenp.array([张三, 李四])完全没问题。但要注意如果你用dtypeS去强制转字节串中文会被截断或者乱码因为UTF-8编码下一个汉字要占3个字节。实际处理中文数据时不要轻易指定dtype留下让NumPy自动判断就行。切片是Python数组最强大的能力之一arr[1:4]、arr[::-1]、arr[::2]这类操作很多人在用但容易忽略的是Python列表切片返回的是新列表而NumPy数组切片返回的是原数组的视图不复制数据。这意味着修改切片会影响到原数组。如果你想得到独立的副本必须显式.copy()。3. 增删改查与遍历最熟悉的日常操作3.1 JS数组的常用方法大盘点JavaScript的数组方法可能是所有语言里最丰富的这里按功能梳理一份简洁版。增删类push()在尾部添加pop()在尾部删除unshift()在头部添加shift()在头部删除。高阶一点splice(start, count, ...items)可以在任意位置删除和插入。注意splice是直接修改原数组而slice是返回新数组这两个名字长得像作用完全不同也是面试里最喜欢考的点之一。查找遍历类forEach()只遍历不改动map()遍历并返回新数组filter()筛选符合条件的元素find()返回第一个符合条件的元素本身findIndex()返回第一个符合条件的下标includes()判断是否包含某个值some()和every()分别对应有一个满足和全部满足常用于条件判断。我在实际写代码时有一个明确的分工习惯需要改每一条数据就map要删掉一批数据就filter只想拿第一条匹配的记录就find需要同时拿到下标和值做逻辑判断就findIndex。这样写出来语义清晰团队里其他同事看代码时也不需要逐行猜。还有一个很容易被忽视的细节map、filter这些方法都要求回调函数显式返回内容一多就容易忘记return方法本身不报错但结果变成全是undefined排查时挺费劲。3.2 Java中数组方法与分组操作Java语言本身给我印象最深的一点是java.util.Arrays工具类几乎覆盖了所有日常操作但很多人不知道去用它。int[] arr {5, 3, 8, 1}; Arrays.sort(arr); // 排序 int pos Arrays.binarySearch(arr, 3); // 二分查找前提是先排好序 Arrays.fill(arr, 0); // 填充 int[] copy Arrays.copyOf(arr, arr.length); // 复制 String s Arrays.toString(arr); // 转字符串另一个非常常用的操作是对象数组分组。比如有一个用户列表想按城市分组Java 8之后的Stream可以这么写MapString, ListUser groupByCity userList.stream() .collect(Collectors.groupingBy(User::getCity));只要一行的功夫传统写法可能要十几行循环加判断。这个功能在很多报表场景里极其好用按天、按分类、按状态分组都能套同一个模板。关于分组后得到的是MapString, List...还是MapString, Long取决于你用的是groupingBy还是groupingBy加counting()这两个变体在不同业务诉求里都有用武之地。3.3 C/C中增加元素、数组扩充与指针数组C/C原生数组长度固定想增加元素本质上只有一条路分配一个更大的新数组把旧数据拷过去再释放旧数组。这就是std::vector内部干的事。如果已经在用C建议直接拥抱vector不要自己手动管理这个流程手动扩容容易漏掉边界检查内存泄漏、越界这些问题全都会找上门。std::vectorint v {1, 2, 3}; v.push_back(4); // 尾部增加 v.insert(v.begin() 1, 9); // 在第二个位置插入9指针数组在C/C里也非常常见。const char* names[3] {tom, jack, lucy};这种写法数组里每个元素是一个指向字符的指针特点是可以存放不同长度的字符串而不会浪费空间。遍历方式有两种一种是用下标names[i]另一种是用指针偏移*(names i)效果一样但很多老代码风格会混用看的时候要有这个意识。需要注意指针数组和二维字符数组char names[3][20]是有本质区别的。二维字符数组每一行都是固定20字节的连续空间适合存储需要后续修改的字符串指针数组每一行指向的是字符串字面量通常是只读的试图往里写内容就是未定义行为。这个差别决定了你的代码能不能安全运行选型时要想清楚。3.4 用宏定义数组到底图什么C/C里用宏定义数组本质上是为了一处修改、全局生效。比如下面的写法#define MAX_SIZE 128 #define TABLE_SIZE 64 int buffer[MAX_SIZE];有同学可能会问为什么不直接用const int MAX_SIZE 128;区别在于宏是在编译预处理阶段做文本替换不占运行时的内存空间而且在数组声明这种需要编译期常量值的地方宏是最简单可靠的方式。C里如果想更规范一点可以用constexpr int MAX_SIZE 128;效果类似但类型更安全。这里我踩过的坑是宏名没取好、值被意外覆盖。比如多个头文件里都定义了MAX_SIZE值还不一样就会产生编译期冲突。所以宏定义数组的配套规范是宏名要有项目前缀比如PROJECT_NAME_MAX_BUFFER_SIZE尽量避免那种通用短名字。4. 排序、去重、切片、匹配这些筛选类操作最考验功底4.1 JS数组排序的几种写法与陷阱JavaScript的sort()方法有一点必须刻进记忆默认情况下它把元素转成字符串再按字典序排序。所以[10, 9, 100].sort()的结果是[10, 100, 9]因为字符串10比9排在前面。这意味着手动传比较函数几乎是每次使用sort的必备动作。// 升序 arr.sort((a, b) a - b); // 降序 arr.sort((a, b) b - a); // 对象数组按某字段升序 users.sort((x, y) x.age - y.age); // 多条件排序先按age升序同年龄按name的localeCompare users.sort((a, b) a.age - b.age || a.name.localeCompare(b.name));如果数组元素结构复杂排序算法本身不是瓶颈瓶颈往往是比较逻辑。我就遇到过按金额排序金额是字符串的场景直接用a.amount - b.amount得到的是0因为字符串减法会隐式转成NaN。最稳妥的做法是先把字符串转成数字Number(a.amount) - Number(b.amount)或者干脆在数据源层面统一转好类型。还有一点sort是修改原数组的如果你还需要保留原来的顺序提前[...arr]拷贝一份再排序。4.2 数组去重的五条路径数组去重大概是前端面试里出镜率最高的问题但很多人只会背一种写法。我按使用场景把它们分了个类1. Set去重最推荐const unique [...new Set(arr)];简单、快、可读性好几乎能覆盖所有基本类型去重场景。2. filter indexOfconst unique arr.filter((item, index) arr.indexOf(item) index);相比Set这个写法在性能上略有劣势但它可以作为一个通用模板在需要同时做其它判断的地方可以灵活改造。3. reduce 构建对象const unique arr.reduce((acc, cur) { if (!acc.includes(cur)) acc.push(cur); return acc; }, []);如果你需要兼容不支持Set的老环境这是个备选方案。4. 对象数组按字段去重const seen new Set(); const unique users.filter(item { if (seen.has(item.id)) return false; seen.add(item.id); return true; });这是实际项目里最常遇到的场景接口返回重复数据按id去重。直接对对象本身做Set去重是无效的因为两个结构相同的对象也不是同一个引用。5. 特殊类型去重如果数组里有NaN用indexOf方案会漏掉因为indexOf(NaN)永远返回-1。但Set方案能正确去重NaN。所以遇到包含NaN的数组选Set方案最稳妥。4.3 Python/NumPy切片和多列提取Python列表切片的基本语法是list[start:stop:step]start包含、stop不包含step默认1。如果写成负数则表示从右往左取。例如arr[-1]是最后一个元素arr[-3:]是最后三个元素arr[::-1]是整个数组反转。这个语法极其强大但新手容易在stop不包含这个细节上犯错想取前5个写了arr[1:5]结果只拿到4个。NumPy里处理二维数组时切片和取列是最常用的操作。假设有一个矩阵m形状是3行4列m[:, 0] # 取第0列 m[:, [0, 2]] # 取第0列和第2列 m[1:, :] # 取从第1行开始的所有行 m[[0, 2], :] # 取第0行和第2行有人会问m[:, 0]和m[:, [0]]有什么区别前者返回的是一维数组后者返回的是二维数组形状是(3,1)。这个差异在后续做矩阵运算时非常重要无意中变了维度会导致广播错误或者形状不匹配的报错。建议每次取完列之后顺手print(arr.shape)确认一下形状。另外NumPy的布尔索引也是处理筛选的高频手段m[m[:, 0] 10]可以一次性筛选出第一列大于10的所有行。这种写法比起一段一段循环筛选表达上直观得多。4.4 数组分割、包含匹配与Excel动态数组数组分割和包含匹配几乎总是一起出现。最典型的场景是有一个字符串数组需要把包含某个关键词的元素挑出来。这个用JavaScript的filter加includes非常自然const result arr.filter(s s.includes(关键字));如果要忽略大小写就统一转小写再判断const result arr.filter(s s.toLowerCase().includes(关键字.toLowerCase()));如果不仅要筛选还要按分隔符把字符串切分成数组就用splitstr.split(,)返回一个数组。这两个操作配合起来正好能应对日志处理、CSV解析、批量关键词匹配一类业务。Excel场景里动态数组是近几年Excel 365非常强大的能力。比如你想从原始表中提取满足某条件的前两列数据成为一个新数组可以用FILTER函数一下子完成FILTER(原始表范围, 条件区域, 未找到时的返回值)如果还要去掉重复项就用UNIQUE函数包一层。我用这个组合做数据清洗比用VBA循环快多了而且改一个条件结果自动刷新真正的函数式数据处理。如果更习惯老一点的写法也可以用INDEX加MATCH组合但公式会冗长不少。动态数组的核心优势在于自动溢出只在一个单元格输入公式整片区域自动填充结果不需要再手动下拉复制。4.5 一组数据和等于固定值的子集问题这是一个看起来简单、实际非常有价值的经典问题。比如有一个数组[3, 8, 9, 4, 1, 6, 2]要找出哪些数字加起来等于固定值比如10。这个场景在实际业务里能遇到——比如订单金额拆分、凑单匹配、账单核对。最简单直观的解法是回溯法遍历数组每个元素有选和不选两种状态把路径总和记录起来。代码如下function findCombinations(arr, target) { const res []; const dfs (start, path, sum) { if (sum target) { res.push([...path]); return; } if (sum target) return; for (let i start; i arr.length; i) { path.push(arr[i]); dfs(i 1, path, sum arr[i]); path.pop(); } }; dfs(0, [], 0); return res; }如果要处理的数据量大比如数组有好几百个元素回溯就太慢了。这时可以用动态规划的思路dp[j]表示凑成和为j的方案情况。不过要注意动态规划求是否可行很容易求所有方案在数量巨大时内存会爆炸。实际工程里我建议先确认数据规模几百以内的直接回溯几十个以内元素求组合也够用如果只需要是否存在就用dp布尔数组复杂度O(n * target)。这类问题还有一个侧面三个数组里取一个数求最大乘积。这是经典贪心题核心思路是考虑最大值和最小值。最大值可能是三个最大正数的乘积也可能是两个最小负数乘以一个最大正数。写代码的时候不能只取三个最大数比如数组是[-10, -10, 5, 6]三个最大数是5、6相乘是30但真正的最大乘积是(-10) * (-10) * 6 600。这就是实战中容易错的地方。5. 进阶与模板树状数组、乘积最大、类型转换与性能对比5.1 树状数组模板维护前缀和与单点修改树状数组算是在线算法里很经典的结构适合处理单点修改 前缀和查询这类场景。比如维护一个长度为n16的序列查询前缀和sum(11)、单点修改add(3, x)两种操作都要在O(log n)内完成树状数组是最合适的工具。树状数组的核心就一句话下标从1开始i的二进制最低位1对应的值就是lowbit(i)代码实现是i -i。模板非常固定const int MAXN 100005; int tree[MAXN], n; int lowbit(int x) { return x -x; } void add(int idx, int delta) { while (idx n) { tree[idx] delta; idx lowbit(idx); } } int prefixSum(int idx) { int res 0; while (idx 0) { res tree[idx]; idx - lowbit(idx); } return res; }查询前缀和sum(11)时就是从11开始一路减lowbit11 - 10 - 8把三个位置的tree值累加起来。修改add(3, x)时从3开始一路加lowbit3 - 4 - 8 - 16把所有受影响区间更新。整个流程熟练之后写起来就是肌肉记忆。关于2的幂数组本质上跟lowbit是同一个思想。树状数组的层数恰好最多是log2(n)所以n16时最多维护4层。理解这个结构有助于你调试时手动模拟数据变化。5.2 数组长度计算C/C和JavaScript的差异数组长度这个话题看起来太基础了但恰恰是bug高发区。C/C里求原生数组长度常见的写法是int len sizeof(arr) / sizeof(arr[0]);这在定义数组的同一个作用域内是有效的但一旦数组名被作为参数传递sizeof(arr)算出来的就是指针大小这个公式立刻失效。所以官方推荐、也是我长期使用的方案是在C里用宏封装在C里直接用std::size(arr)或arr.size()。JavaScript里arr.length是数组的属性看起来很简单但有一个反直觉的点给length赋更小的值可以直接裁切数组赋更大的值会制造稀疏数组。比如arr.length 0可以清空数组这是个技巧但很少有人知道为什么要这样做——因为它比反复splice快。Python里len(list)是内置函数NumPy里是arr.shape。很多初学者会把arr.shape当成函数调用来写arr.shape()结果报错ndarray object is not callable。区别在于shape是元组属性不需要加括号。这两个语言细节虽然小但跨语言切换写得多了经常会在这种地方卡壳。5.3 数组类型转换与QT中常量引用数组的接收数组类型转换在不同语言里的坑位置各不相同。C语言里数组类型转换最常遇到的场景是把char数组转成数字、或者把字符串转成char数组再修改。C里则容易遇到std::string和char*互相转换的问题。这里特别说一下QT面板里出现过的const (double [10])这种写法。这是一个常量的数组引用意思是函数参数接收的是一个长度为10的double数组的引用而不是指针。这种写法的好处是如果用数组引用作参数编译器会强制检查数组长度传一个长度不对的数组会直接编译报错避免越界风险。接收这种参数时可以直接给一个同长度的double数组变量传进去using DoubleArrayRef const double ()[10]; void processArray(DoubleArrayRef arr) { for (int i 0; i 10; i) { qDebug() arr[i]; } } double data[10] {0.0, 1.1, 2.2, /* ... */}; processArray(data);如果你想接收赋值到另一个数组里直接double temp[10]; memcpy(temp, data, sizeof(data));就行。不过其实最省事的办法还是用std::arraydouble, 10语义更清晰也避免了原生数组退化成指针的各种坑。5.4 VBA数组对比的最快思路字典优先VBA里最容易被写慢的操作就是数组对比。比如有两个数组想找出A有B没有的元素很多初学者会写两层循环遍历数据量小没事数据量一旦上千代码就跑得让人怀疑人生。实测下来最快的方案是先转字典再比对。因为字典的查找是O(1)的而数组循环查找是O(n)的。具体思路是把其中一个数组的元素放到Scripting.Dictionary中作为key然后遍历另一个数组去查字典存不存在。Sub CompareArrays() Dim dict As Object Set dict CreateObject(Scripting.Dictionary) Dim arr1 As Variant, arr2 As Variant arr1 Range(A1:A1000).Value arr2 Range(B1:B1000).Value Dim i As Long For i 1 To UBound(arr1) If Not dict.Exists(arr1(i, 1)) Then dict.Add arr1(i, 1), 1 End If Next i For i 1 To UBound(arr2) If dict.Exists(arr2(i, 1)) Then 匹配到公共元素 Else 只存在arr2中 End If Next i End Sub用字典对比10000条数据也是瞬间完成而两层循环大概要几秒钟。这个性能差距在Excel VBA的日常处理里非常明显。另外提醒一点VBA里从Range.Value取数组默认是二维数组就算只有一列它的维度也是(行, 列)直接UBound(arr1(1))的写法会报错或者取错值这个细节常被忽视。5.5 数组转字符串最容易出错的拼接数组转字符串在不同语言里有不同的首选方案。JavaScript里arr.join(,)是最快的它会忽略空位但如果你用的是arr.toString()效果等同于join(,)但可读性差一些。Java里用String.join(,, arr)注意它接收的是CharSequence数组如果要处理的是int[]得先用Arrays.stream转成String数组。C/C里把数组转字符串就没有这么高级的玩法了常规方案是循环拼接或者用sprintf格式化到一个char缓冲区。这里容易踩的坑是缓冲区长度不够导致溢出。稳妥做法是根据数组元素数量和最大格式长度提前算好或者用std::ostringstream来自动管理内存。Python里最简单的做法是,.join(map(str, arr))因为数组元素可能是数字必须先用str()转换。不转换会直接报错sequence item 0: expected str instance, int found。这个类型错误的提示对初学者来说很容易一头雾水本质上是join只能拼接字符串。6. 常见问题速查表与几条独家心得6.1 高频问题速查我把上面提到的问题浓缩成一张速查表方便遇到问题直接对号入座问题场景正确做法错误示范JS创建长度为n的全0数组new Array(n).fill(0)new Array(n).map(() 0)JS对象数组按字段去重用Set存字段值再filter直接new Set(arr)JS默认排序传比较函数(a,b)a-b直接arr.sort()C/C在函数内求数组长度用数组引用参数或std::size对指针做sizeofC/C数组初始化定义时就写{}只声明不初始化直接使用Python NumPy切片确认返回的是视图还是副本修改切片后误以为不影响原数组NumPy取一列用m[:, 0]误写成m[0]VBA数组对比先转Dictionary两层循环遍历PHP json_decode明确第二个参数true与否拿到不确定类型直接取字段Excel动态数组用FILTER加UNIQUE手动下拉填充数组中Office字符串转数字统一Number()转换直接字符串相减这十几条是我在项目代码评审里反复提醒别人的高频问题也是真实踩坑记录。很多东西不是不懂原理而是跨语言写久了容易出现惯性误用。6.2 数组相关操作的几条独家经验我做了这么多年开发对数组操作有几点体会特别深刻。第一数组的API和方法只是工具真正重要的是理解每种操作的副作用。一个sort是原地修改还是返回新数组、一个splice要从哪个下标删几个元素这些细节决定接口会不会被意外污染。我在团队里定了条规矩凡是方法会修改原数组的调用前都要有意确认确实想改原数组否则一律先复制再操作。第二数据量是划分用哪种写法的裁判。5个元素去重随便怎么写都行50000个元素去重就必须用覆盖度和性能都好的方案。我一般习惯在写代码前先估算数据规模范围小用可读性最好的写法范围大才引入更复杂的算法结构避免为不必要的性能优化牺牲代码清晰度。第三跨语言开发时逻辑要统一语法要隔离。同一个从第2行开始取数据的需求Python写arr[1:]JavaScript写arr.slice(1)Java写Arrays.copyOfRange(arr, 1, arr.length)。语法差异是必然的但只要脑子里想的都是同一件事——从下标1开始取到末尾——就不容易乱。反过来如果思维也跟着语言切来切去反而容易在边界条件上出错。最后我自己的一个小习惯凡是跟数组下标、长度、边界有关的代码写完之后立刻用边界值测一遍。空数组、全同元素数组、最大长度数组这三种极端情况一测绝大多数潜在bug都会立刻暴露。这个方法成本很低但回报很高几乎适合所有语言的数组操作场景。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

基于SSM的牙科诊所预约管理系统设计与实现要点 2026/9/30 4:50:52

基于SSM的牙科诊所预约管理系统设计与实现要点

做Java后端这些年,接手过不少管理系统,但牙科诊所预约这块给我留下的印象最深。不是技术有多难,而是业务细节特别琐碎:患者要预约、医生要排班、前台要改约、老板要看报表,稍微有个环节没想清楚,后面全是坑…

阅读更多 →
Octop 1.0:开箱即用的生产级AI团队协作框架 2026/9/30 4:50:52

Octop 1.0:开箱即用的生产级AI团队协作框架

1. 这不是又一个“玩具级”AI框架:Octop 1.0 的生产定位从第一条命令就写在了基因里你有没有试过,在凌晨两点,盯着终端里一行行滚动的docker-compose up -d日志,心里却没底——这玩意儿真能扛住明天上线后用户涌进来的第一个请求吗…

阅读更多 →
AI渗透测试真实能力评估:从基准设计到落地实践 2026/9/30 4:50:52

AI渗透测试真实能力评估:从基准设计到落地实践

上周在群里看到一条链接,标题就是“AI渗透测试被高估了?”,后面还挂着一篇论文的解读。当时我正在陪客户做红队复盘,手边是一堆AI生成但基本没法直接用的利用脚本,看到这个标题我愣了一下——终于有人把这块遮羞布扯下…

阅读更多 →
JSP家政系统实战:从零搭建可交付的在线家政网 2026/9/30 4:50:52

JSP家政系统实战:从零搭建可交付的在线家政网

简介:本资源是一份面向Java Web初学者与课程设计学生的完整毕业设计文档,聚焦基于JSP技术构建B/S架构在线家政服务平台的全流程实现。文档系统阐述了家政信息预约、家教服务预约、用户在线求职及个人后台管理等核心功能模块的设计逻辑与技术落地路径&…

阅读更多 →
大模型服务器部署完全指南:框架选型、云服务对比与生产级流程 2026/9/30 4:50:52

大模型服务器部署完全指南:框架选型、云服务对比与生产级流程

很多团队走到“模型跑起来了”这一步,往往只用了推理框架的默认参数。可一旦把服务推到生产环境,压测一上来,各种各样的毛病就全暴露出来了——显存不够、请求排队、首字延迟飙升、并发一高直接 OOM。这篇文章不聊算法,只聊部署。…

阅读更多 →
SuperMap iServer 信创环境部署适配与故障排查指南 2026/9/30 4:50:45

SuperMap iServer 信创环境部署适配与故障排查指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉