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

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

我感谢自己平凡,敢爱敢恨没负担。
我感谢自己不凡,可爱可恨都包揽。

本帖由系统于 3个月前 自动加精
讨论数量: 9
lmaster

赞。

4个月前 评论
Wi1dcard (楼主) 4个月前
lmaster

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

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

4个月前 评论

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

4个月前 评论
lmaster 4个月前

优化后的确定是无限极?

3个月前 评论
Wi1dcard (楼主) 3个月前
$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']; // 根节点只有中华人民共和国,所以直接返回中国的所有子节点
3个月前 评论
Wi1dcard (楼主) 3个月前
yzh52521 (作者) 3个月前
Wi1dcard (楼主) 3个月前

file
也许我的姿势不对吧

3个月前 评论
circle

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

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

是不是该改成:

return $catList[1]['list'];
3个月前 评论
Wi1dcard (楼主) 3个月前
circle (作者) 3个月前
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;
    }
1周前 评论

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!