Maison  >  Article  >  développement back-end  >  N'utilisez pas array_merge() dans un corps de boucle

N'utilisez pas array_merge() dans un corps de boucle

藏色散人
藏色散人avant
2019-10-30 14:02:372747parcourir

Le titre est de ne pas utiliser array_merge() dans le corps de la boucle. En fait, ce n'est qu'une des conclusions de cet article

Étudions la fusion de tableaux dans le langage PHP (fusion récursive). n'est pas pris en compte ici)

Comparaison de quatre façons de fusionner des tableaux

Comparaison de quatre façons courantes de fusionner des tableaux

Écrire du code

Nous savons que array_merge() et Operator + peuvent épisser des tableaux

Créer une classe

ArrayMerge()
weight eachOne () corps de la boucle Utilisez array_merge() pour fusionner
● eachTwo() Après le corps de la boucle, utilisez array_merge() pour fusionner
● eachThree() Le corps de la boucle est imbriqué pour implémenter la fusion de tableaux
● eachFour( ) Le corps de la boucle utilise l'opérateur + l'épissage et la fusion
● getNiceFileSize() convertit l'utilisation de la mémoire dans un format lisible par l'homme

/**
 * Class ArrayMerge
 */
class ArrayMerge
{
    /**
     * @param int $times
     * @return array
     */
    public static function eachOne(int $times): array
    {
        $a = [];
        $b = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
        for ($i = 0; $i < $times; $i++) {
            $a = array_merge($a, $b);
        }
        return $a;
    }
    /**
     * @param int $times
     * @return array
     */
    public static function eachTwo(int $times): array
    {
        $a = [[]];
        $b = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
        for ($i = 0; $i < $times; $i++) {
            $a[] = $b;
        }
        return array_merge(...$a);
    }
    /**
     * @param int $times
     * @return array
     */
    public static function eachThree(int $times): array
    {
        $a = [];
        $b = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
        for ($i = 0; $i < $times; $i++) {
            foreach ($b as $item) {
                $a[] = $item;
            }
        }
        return $a;
    }
    /**
     * @param int $times
     * @return array
     */
    public static function eachFour(int $times): array
    {
        $a = [];
        $b = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
        for ($i = 0; $i < $times; $i++) {
            $a = $b + $a;
        }
        return $a;
    }
    /**
     * 转化内存信息
     * @param      $bytes
     * @param bool $binaryPrefix
     * @return string
     */
    public static function getNiceFileSize(int $bytes, $binaryPrefix = true): ?string
    {
        if ($binaryPrefix) {
            $unit = array(&#39;B&#39;, &#39;KiB&#39;, &#39;MiB&#39;, &#39;GiB&#39;, &#39;TiB&#39;, &#39;PiB&#39;);
            if ($bytes === 0) {
                return &#39;0 &#39; . $unit[0];
            }
            return @round($bytes / (1024 ** ($i = floor(log($bytes, 1024)))),
                    2) . &#39; &#39; . ($unit[(int)$i] ?? &#39;B&#39;);
        }
        $unit = array(&#39;B&#39;, &#39;KB&#39;, &#39;MB&#39;, &#39;GB&#39;, &#39;TB&#39;, &#39;PB&#39;);
        if ($bytes === 0) {
            return &#39;0 &#39; . $unit[0];
        }
        return @round($bytes / (1000 ** ($i = floor(log($bytes, 1000)))),
                2) . &#39; &#39; . ($unit[(int)$i] ?? &#39;B&#39;);
    }
}

Utilisez

pour allouer plus mémoire d'abord

mémoire de sortie Occuper et fusionner la longueur du tableau, et enregistrer le temps de chaque étape

ini_set(&#39;memory_limit&#39;, &#39;4000M&#39;);
$timeOne = microtime(true);
$a       = ArrayMerge::eachOne(10000);
echo &#39;count eachOne Result | &#39; . count($a) . PHP_EOL;
echo &#39;memory eachOne Result | &#39; . ArrayMerge::getNiceFileSize(memory_get_usage(true)) . PHP_EOL;
$timeTwo = microtime(true);
$b       = ArrayMerge::eachTwo(10000);
echo &#39;count eachTwo Result | &#39; . count($b) . PHP_EOL;
echo &#39;memory eachTwo Result | &#39; . ArrayMerge::getNiceFileSize(memory_get_usage(true)) . PHP_EOL;
$timeThree = microtime(true);
$c         = ArrayMerge::eachThree(10000);
echo &#39;count eachThree Result | &#39; . count($c) . PHP_EOL;
echo &#39;memory eachThree Result | &#39; . ArrayMerge::getNiceFileSize(memory_get_usage(true)) . PHP_EOL;
$timeFour = microtime(true);
$d        = ArrayMerge::eachFour(10000);
echo &#39;count eachFour Result | &#39; . count($d) . PHP_EOL;
echo &#39;memory eachFour Result | &#39; . ArrayMerge::getNiceFileSize(memory_get_usage(true)) . PHP_EOL;
$timeFive = microtime(true);
echo PHP_EOL;
echo &#39;eachOne | &#39; . ($timeTwo - $timeOne) . PHP_EOL;
echo &#39;eachTwo | &#39; . ($timeThree - $timeTwo) . PHP_EOL;
echo &#39;eachThree | &#39; . ($timeFour - $timeThree) . PHP_EOL;
echo &#39;eachFour | &#39; . ($timeFive - $timeFour) . PHP_EOL;
echo PHP_EOL;

Résultat

count eachOne Result | 100000
memory eachOne Result | 9 MiB
count eachTwo Result | 100000
memory eachTwo Result | 14 MiB
count eachThree Result | 100000
memory eachThree Result | 18 MiB
count eachFour Result | 10           #注意这里
memory eachFour Result | 18 MiB
eachOne | 5.21253490448                 # 循环体中使用array_merge()最慢,而且耗费内存
eachTwo | 0.0071840286254883            # 循环体结束后使用array_merge()最快
eachThree | 0.037622928619385           # 循环体嵌套比循环体结束后使用array_merge()慢三倍
eachFour | 0.0072360038757324           # 看似也很快,但是合并的结果有问题

● Utilisation de array_merge () dans le corps de la boucle est le plus lent et consomme de la mémoire

● L'utilisation de array_merge () après la fin du corps de la boucle est la plus rapide

● L'imbrication du corps de la boucle est trois fois plus lente que l'utilisation de array_merge () après la le corps de la boucle se termine

● Cela semble très rapide Rapide, mais il y a un problème avec le résultat fusionné

Les pièges de la fusion de tableaux

Nous avons remarqué que la longueur du résultat de eachFour à l'instant n'est que de 10

Explorons pourquoi il y aura un résultat comme celui-ci

Voici les fusions récursives à des fins de comparaison

Code

public static function test(): void
{
    $testA = [
        &#39;111&#39; => &#39;testA1&#39;,
        &#39;abc&#39; => &#39;testA1&#39;,
        &#39;222&#39; => &#39;testA2&#39;,
    ];
    $testB = [
        &#39;111&#39; => &#39;testB1&#39;,
        &#39;abc&#39; => &#39;testB1&#39;,
        &#39;222&#39; => &#39;testB2&#39;,
        &#39;www&#39; => &#39;testB1&#39;,
    ];
    echo &#39;array_merge($testA, $testB) | &#39; . PHP_EOL;
    print_r(array_merge($testA, $testB));
    echo &#39;$testA + $testB | &#39; . PHP_EOL;
    print_r($testA + $testB);
    echo &#39;$testB + $testA | &#39; . PHP_EOL;
    print_r($testB + $testA);
    echo &#39;array_merge_recursive($testA, $testB) | &#39; . PHP_EOL;
    print_r(array_merge_recursive($testA, $testB));
}

Résultat

+ le signe est utilisé pour épisser deux tableaux. Ce dernier ne fera que compléter les clés que le premier n'a pas, mais conservera les. index numérique.

array_merge() et array_merge_recursive() effaceront l'index numérique, et tous les index numériques seront dans l'ordre à partir de 0 démarré

array_merge($testA, $testB) |    #数字索引强制从0开始了 字符key相同的以后者为准
Array
(
    [0] => testA1
    [abc] => testB1
    [1] => testA2
    [2] => testB1
    [3] => testB2
    [www] => testB1
)
$testA + $testB |        #testA得到保留,testB补充了testA中没有的key,数字索引得到保留
Array
(
    [111] => testA1
    [abc] => testA1
    [222] => testA2
    [www] => testB1
)
$testB + $testA |        #testB得到保留,testA补充了testB中没有的key,数字索引得到保留
Array
(
    [111] => testB1
    [abc] => testB1
    [222] => testB2
    [www] => testB1
)

array_merge_recursive($testA, $testB) # L'index numérique commence à 0, mais l'ordre du tableau n'est pas détruit. La même chaîne `key` est fusionnée dans un tableau

Array
(
    [0] => testA1
    [abc] => Array
        (
            [0] => testA1
            [1] => testB1
        )
    [1] => testA2
    [2] => testB1
    [3] => testB2
    [www] => testB1
)

Analyse

Voyant cela. , vous devez être très confus. Je ne m'attendais pas à ce que array_merge() ait de tels pièges

Jetons d'abord un coup d'œil au manuel officiel

array_merge ( array $array1 [, array $... ] ) : array

array_merge () Fusionne les cellules d'un ou plusieurs tableaux, avec les valeurs d'un tableau ajoutées au tableau précédent. Renvoie le tableau résultant.

Si le tableau d'entrée a le même nom de clé de chaîne, la valeur après le nom de clé écrasera la valeur précédente. Cependant, si le tableau contient des clés numériques, les valeurs suivantes n'écraseront pas les valeurs d'origine mais leur seront ajoutées.

Si seul un tableau est donné et que le tableau est indexé numériquement, les noms de clés sont réindexés de manière contiguë.

Uniquement avec le même nom de clé de chaîne, la valeur suivante écrasera la valeur précédente. (Mais le manuel n'explique pas pourquoi l'index du nom de la touche numérique est réinitialisé)

Jetons ensuite un œil au code source

PHPAPI int php_array_merge(HashTable *dest, HashTable *src)
{
    zval *src_entry;
    zend_string *string_key;
    if ((dest->u.flags & HASH_FLAG_PACKED) && (src->u.flags & HASH_FLAG_PACKED)) {
        // 自然数组的合并,HASH_FLAG_PACKED表示数组是自然数组([0,1,2])   参考http://ju.outofmemory.cn/entry/197064
        zend_hash_extend(dest, zend_hash_num_elements(dest) + zend_hash_num_elements(src), 1);
        ZEND_HASH_FILL_PACKED(dest) {
            ZEND_HASH_FOREACH_VAL(src, src_entry) {
                if (UNEXPECTED(Z_ISREF_P(src_entry)) &&
                    UNEXPECTED(Z_REFCOUNT_P(src_entry) == 1)) {
                    ZVAL_UNREF(src_entry);
                }
                Z_TRY_ADDREF_P(src_entry);
                ZEND_HASH_FILL_ADD(src_entry);
            } ZEND_HASH_FOREACH_END();
        } ZEND_HASH_FILL_END();
    } else {
        //遍历获取key和vaule
        ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
            if (UNEXPECTED(Z_ISREF_P(src_entry) &&
                Z_REFCOUNT_P(src_entry) == 1)) {
                ZVAL_UNREF(src_entry);
            }
            Z_TRY_ADDREF_P(src_entry);
            //  参考https://github.com/pangudashu/php7-internal/blob/master/7/var.md
            if (string_key) {
                // 字符串key(zend_string)  插入或者更新元素,会增加key的计数
                zend_hash_update(dest, string_key, src_entry);
            } else {
                //插入新元素,使用自动的索引值(破案了,索引被重置的原因在此)
                zend_hash_next_index_insert_new(dest, src_entry);
            }
        } ZEND_HASH_FOREACH_END();
    }
    return 1;
}

Résumé

Résumé Comme mentionné ci-dessus, les différentes méthodes de fusion de tableaux présentent certains défauts, mais grâce à notre exploration ci-dessus, nous avons appris que

● Il n'est pas conseillé d'utiliser array_merge() dans le corps de la boucle pour fusionner des tableaux, et la différence de vitesse est des centaines de fois

● array_merge() doit être utilisé avec prudence lors de la fusion de tableaux. Si la clé est importante et que la clé peut être un nombre, array_merge() ne peut pas être utilisée pour fusionner. peut utiliser des corps de boucle imbriqués (notez que la boucle interne utilise la clé pour l'affectation) Opération)

● Si la clé est importante et que la clé peut être un nombre, l'opérateur + peut être utilisé pour simplement fusionner des tableaux, mais il doit ne pas être utilisé dans le corps de la boucle, car le résultat de chaque opération est de générer un nouveau tableau de

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer