千家信息网

PHP排序稳定性实例分析

发表于:2025-11-14 作者:千家信息网编辑
千家信息网最后更新 2025年11月14日,今天小编给大家分享一下PHP排序稳定性实例分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解
千家信息网最后更新 2025年11月14日PHP排序稳定性实例分析

今天小编给大家分享一下PHP排序稳定性实例分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

PHP排序稳定性问题

最近在工作中碰到一个挺有意思的问题,线上输入是一串排好序的关联数组,经过一系列处理后输出的数组却是乱序,且本地运行无法复现。查看相关代码后,最让人在意的是这一段:

$categories = Arr::sort($categories, function ($node) {    return $node['default'];}, true);

作用是按default优先级将元素提到前面,首先确认了下线上的illuminate/support版本和本地一致,查看Arr::sort()源码:

...$descending ? arsort($results, $options)            : asort($results, $options);

最终还是调用 php 的asort,线上是 php5 而本地和测试因为最近考虑升级都换成了 php7,最后在 php5 环境下成功复现,确定出是sort问题。

在排序前后相等的元素相对位置不变即说这个算法是稳定的。

对快速排序有一定了解的话可以知道,快速排序是不稳定的所以这段代码在元素default优先级都相同的情况下输出将不会是之前的顺序,但在 php7 环境下测试结果为什么会保留原来的顺序呢。难道关于我之前理解的天底下所有默认排序都是快速排序这一点是错的吗?

好吧,让我们来快速看看 php 源码是如何解决这个问题的。到 github 官方的 php-src 直接搜索asort in this repository,找c文件,找到这个函数定义在 arr.c:814

PHP_FUNCTION(asort){        zval *array;        zend_long sort_type = PHP_SORT_REGULAR;        bucket_compare_func_t cmp;        ZEND_PARSE_PARAMETERS_START(1, 2)                Z_PARAM_ARRAY_EX(array, 0, 1)                Z_PARAM_OPTIONAL                Z_PARAM_LONG(sort_type)        ZEND_PARSE_PARAMETERS_END();        // 设置比较函数        cmp = php_get_data_compare_func(sort_type, 0);        zend_hash_sort(Z_ARRVAL_P(array), cmp, 0);        RETURN_TRUE;}

可以看到最终调用的是zend_hash_sort(),我们继续搜索:

发现这个函数是zend_hash_sort_ex()的套娃,最后找到zend_hash.c:2497

ZEND_API void ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort, bucket_compare_func_t compar, zend_bool renumber){        Bucket *p;        uint32_t i, j;        IS_CONSISTENT(ht);        HT_ASSERT_RC1(ht);        if (!(ht->nNumOfElements>1) && !(renumber && ht->nNumOfElements>0)) {                /* Doesn't require sorting */                return;        }        // 这里的hole指数组元素被unset掉产生的洞        if (HT_IS_WITHOUT_HOLES(ht)) {                /* Store original order of elements in extra space to allow stable sorting. */                for (i = 0; i < ht->nNumUsed; i++) {                        Z_EXTRA(ht->arData[i].val) = i;                }        } else {                /* Remove holes and store original order. */                for (j = 0, i = 0; j < ht->nNumUsed; j++) {                        p = ht->arData + j;                        if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;                        if (i != j) {                                ht->arData[i] = *p;                        }                        Z_EXTRA(ht->arData[i].val) = i;                        i++;                }                ht->nNumUsed = i;        }        sort((void *)ht->arData, ht->nNumUsed, sizeof(Bucket), (compare_func_t) compar,                        (swap_func_t)(renumber? zend_hash_bucket_renum_swap :                                ((HT_FLAGS(ht) & HASH_FLAG_PACKED) ? zend_hash_bucket_packed_swap : zend_hash_bucket_swap)));        ...

好耶!,官方注释里就有说明是怎么实现排序的稳定性,在排序之前用这个Z_EXTRA保留了原数组元素到下标的映射。

但当我搜索这块代码提交信息时发现了一个问题:稳定排序相关的 rfc 在https://wiki.php.net/rfc/stable_sorting,可以看到是发生在今年五月份且针对 php8.0 的。

?? 那之前的 php7 排序稳定是怎么回事。

马上构造个例子:

$count = 10;$cc = [];for ($i=0; $i<$count; $i++) {    $cc[] = [        'id' => $i,        'default' => rand(0, 10),    ];}$cc = Arr::sort($cc, function ($i) {   return $i['default'];});dd($cc);

经过多次在 php7 下的测试发现:当$count比较小的时候,排序才是稳定的,但$count较大情况下的排序又变成不稳定。也就是线上排序不稳定而本地无法复现其实是用例的数组长度太短所致。

看到这里可以确定是快速排序长度阈值优化的问题,最后查了下相关资料。php7 中的sort是基于LLVMlibc++std::sort实现。当元素数量小于等于16时候有特殊优化,大于16才走快速排序,而 php5 是直接就走快速排序的。

 $i,        'default' => rand(0, 10),    ];}usort($cc, function($a, $b){    if ($a['default'] == $b['default']) return 0;    return ($a['default'] < $b['default']) ? 1 : -1;});print_r($cc);

最后在 php8 环境下试了试,排序绝对稳定

以上就是"PHP排序稳定性实例分析"这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注行业资讯频道。

排序 元素 问题 数组 稳定性 知识 篇文章 代码 函数 环境 搜索 测试 实例 实例分析 分析 优先级 内容 官方 情况 时候 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 武汉大学国家网络安全学院食堂 汉枫wifi模块与服务器 shell服务器 榆林高中生5g软件开发培训 力控7创建SQL数据库 我国物流信息化现状数据库 与逻辑服务器断开连接 网络安全在我心中短视频 王者荣耀里面没有服务器怎么办 南充领跑网络技术有限公司 我的世界联机服务器模组 材料科学有关的数据库有哪些 网络安全宣传活动周的主题是 中国电信网络安全研究所 路北区信息网络技术质量保证 如何给一个软件开发插件 三星服务器管理口地址 京轩汇锦互联网科技是做什么的 淘宝erp什么软件开发 数据库操作使用包括 电子邮件连接服务器出错 通州区信息化软件开发 云服务器云主机 数据库实施阶段所做的工作是 校园网络安全宣传东莞 网络安全与舆情导控 nor叔我的世界服务器擂台赛 合肥市蜀山区芃夯网络技术工作室 数据库的核心也是最常用的 朝阳区智能网络技术服务有哪些
0