PHP 快速扫描列表创建无限极分类树

书接上回。上文结尾,讲解了引用的妙用。刚好,在我现在所处公司的业务里有一处用递归实现的「省市区」分级列表;本文将这一用途搬进生产环境,通过优化此省市区列表,试试真正的效果如何。

废话不多说,上代码。

省市区列表结构

array(
    1 => array(
            'id' => 1,
            'name' => '中华人民共和国',
            'parent_id' => 0,
            'level' => 'country',
        ),
    2 => array(
            'id' => 2,
            'name' => '北京市',
            'parent_id' => 1,
            'level' => 'province',
        ),
    20 => array(
            'id' => 20,
            'name' => '天津市',
            'parent_id' => 1,
            'level' => 'province',
        ),
    38 => array(
            'id' => 38,
            'name' => '河北省',
            'parent_id' => 1,
            'level' => 'province',
        ),
    218 => array(
            'id' => 218,
            'name' => '山西省',
            'parent_id' => 1,
            'level' => 'province',
        ),
    349 => array(
            'id' => 349,
            'name' => '内蒙古自治区',
            'parent_id' => 1,
            'level' => 'province',
        ),
    465 => array(
            'id' => 465,
            'name' => '辽宁省',
            'parent_id' => 1,
            'level' => 'province',
        ),
    ...
);

优化前

/**
* 获取以父级 ID 为 $parent_id 为根节点的树型结构数组
*
* @param array $arr
* @param int $level 树型当前层
* @param int $parent_id 父级id
* @param int $floor 树型总层数
* @return array
*/
public static function getList(&$arr, $level = 0, $parent_id = 1, $floor = 3)
{
    if ($level != 0) {
        $empty = $arr[$parent_id];
        $empty['list'] = [];
        $emptyPointer = &$empty['list'];
    } else {
        $empty = [];
        $emptyPointer = &$empty;
    }
    if ($level < $floor) {
        $ok = false;
        foreach ($arr as $index => &$item) {
            if ($item['parent_id'] == $parent_id) {
                $data = self::getList($arr, $level + 1, $index);
                array_push($emptyPointer, $data);
                $ok = true;
            }
            if ($ok && $item['parent_id'] != $parent_id) {
                break;
            }
        }
    }

    return $empty;
}

优化后

function getStructuredTree($list)
{
    $tree = [];

    foreach ($list as &$item) {
        $parent_id = $item['parent_id'];

        if (isset($list[$parent_id]) && !empty($list[$parent_id])) {
            $list[$parent_id]['list'][] = &$item;
        } else {
            $tree[] = &$item;
        }
    }
    // unset($item);

    return $tree[0]['list']; // 根节点只有中华人民共和国,所以直接返回中国的所有子节点
}

效果

以下为此函数执行 1000 次取平均值。

函数运行时间 (ms) memory_get_peak_usage() 峰值内存 (MB)
优化前 157.65 2516192 2.39
优化后 2.01 987872 0.94

仅供参考,不同环境生成的具体数值可能差异较大,只关注优化前后的对比就好。

本作品采用《CC 协议》,转载必须注明作者和本文链接
Former WinForm and PHP engineer. Now prefer Golang and Rust, and mainly working on DevSecOps and Kubernetes.
本帖由系统于 4年前 自动加精
讨论数量: 9
lmaster

赞。

4年前 评论
Wi1dcard (楼主) 4年前
lmaster

@Wi1dcard$catList体量小时,无所谓;在体量大时,首次循环要栈空间开辟地址存放变量,之后每次还要到变量表中去查找。虽然php不适合密集型程序,但是能提高一点算一点。

其实这个是个人习惯 :blush:

4年前 评论

@lmaster 是的,个人习惯吧。所以还是采纳了你的建议~

4年前 评论
lmaster 4年前

优化后的确定是无限极?

4年前 评论
Wi1dcard (楼主) 4年前
$treeData = [];
    foreach ($catList as $item) {
        if (isset($catList[$item['parent_id']]) && !empty($catList[$item['parent_id']])) {
            $catList[$item['parent_id']]['list'][] = &$catList[$item['id']];
        } else {
            $treeData[] = &$catList[$item['id']];
        }
    }
    return $treeData[0]['list']; // 根节点只有中华人民共和国,所以直接返回中国的所有子节点
4年前 评论
Wi1dcard (楼主) 4年前
yzh52521 (作者) 4年前
Wi1dcard (楼主) 4年前

file
也许我的姿势不对吧

4年前 评论
circle

你好,复制粘贴文中的所有代码执行确实返回空,如果要返回所有子节点,这个地方

return $treeData[0]['list']; // 根节点只有中华人民共和国,所以直接返回中国的所有子节点

是不是该改成:

return $catList[1]['list'];
4年前 评论
Wi1dcard (楼主) 4年前
circle (作者) 4年前
private static function buildTree($data)
    {
        if (empty($data)) {
            return [];
        }

        $tree = [];

        foreach ($data as $item) {
            if (isset($data[$item['parentId']])) {
                $data[$item['parentId']]['children'][] = &$data[$item['id']];
            } else {
                $tree[] = &$data[$item['id']];
            }
        }

        return $tree;
    }
4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
未填写
文章
67
粉丝
590
喜欢
1236
收藏
1133
排名:13
访问:32.4 万
私信
所有博文
社区赞助商