Почему array.push иногда быстрее, чем массив [n] = значение?

Одним из способов было бы разбить строку на "/" и взять последовательные срезы.

in_string = "/house/dogs/ralph/bone"
s = in_string.split("/")
out_strings = list(filter(None, ("/".join(s[:i+1]) for i in range(len(s)))))
print(out_strings)
#['/house', '/house/dogs', '/house/dogs/ralph', '/house/dogs/ralph/bone']

filter(None, ...) используется для удаления пустых строк.

Или измените диапазон, если вы хотите выводить в порядке, указанном в вашем посте:

out_strings = list(filter(None, ("/".join(s[:i]) for i in range(len(s), 0, -1))))
print(out_strings)
#['/house/dogs/ralph/bone',
# '/house/dogs/ralph',
# '/house/dogs',
# '/house']
69
задан holographic-principle 15 July 2013 в 12:51
поделиться

4 ответа

Все виды факторов играют роль, большинство реализаций JS использует плоскую антенную решетку, которая преобразовывает в редкое устройство хранения данных, если это становится необходимым позже.

В основном решение стать редким является эвристикой на основе того, какие элементы устанавливаются, и сколько пространства было бы потрачено впустую, чтобы остаться плоским.

В Вашем случае Вы устанавливаете последний элемент сначала, что означает, что механизм JS будет видеть массив, который должен иметь длину n, но только единственный элемент. Если n будет достаточно большим, то это сразу сделает массив разреженным массивом - в большинстве механизмов, это означает, что все последующие вставки возьмут медленный случай разреженного массива.

необходимо добавить дополнительный тест, в котором Вы заполняете массив от индекса 0 до индекса n-1 - это должно быть очень, намного быстрее.

В ответ на @Christoph и из требования отложить, вот описание того, как массивы (обычно) реализуются в JS - специфические особенности варьируются с механизма JS на механизм JS, но общий принцип является тем же.

Весь JS Object с (так не строки, числа, верные, ложные, undefined, или null), наследовались типу базового объекта - точная реализация варьируется, это могло быть наследование C++, или вручную в C (существуют преимущества для выполнения в нем так или иначе) - тип базового объекта определяет методы доступа свойства по умолчанию, например,

interface Object {
    put(propertyName, value)
    get(propertyName)
private:
    map properties; // a map (tree, hash table, whatever) from propertyName to value
}

, Этот Тип объекта обрабатывает всю стандартную логику доступа свойства, опытную цепочку, и т.д. Тогда реализация Массива становится

interface Array : Object {
    override put(propertyName, value)
    override get(propertyName)
private:
    map sparseStorage; // a map between integer indices and values
    value[] flatStorage; // basically a native array of values with a 1:1
                         // correspondance between JS index and storage index
    value length; // The `length` of the js array
}

Теперь при создании Массива в JS, механизм создает что-то сродни вышеупомянутой структуре данных. Когда Вы вставляете объект в экземпляр Массива помещенные проверки метода Массива, чтобы видеть, является ли имя свойства целым числом (или может быть преобразован в целое число, например, "121", "2341", и т.д.) между 0 и 2^32-1 (или возможно 2^31-1, я забываю точно). Если это не, то помещенный метод передается реализации базового объекта, и стандартная [[Помещенная]] логика сделана. Иначе значение помещается в собственное устройство хранения данных Массива, если данные будут достаточно компактны тогда, то механизм будет использовать устройство хранения данных плоской антенной решетки, в этом случае вставка (и извлечение) является просто стандартной операцией индексации массива, иначе механизм преобразует массив в редкое устройство хранения данных и поместит/получит, используют карту для получения от propertyName для оценки местоположения.

я честно не уверен, преобразовывает ли какой-либо механизм JS в настоящее время с редкого на плоское устройство хранения данных после того, как то преобразование происходит.

Anyhoo, это - обзор довольно высокого уровня того, что происходит и не учитывает много более неприглядных деталей, но это - общий шаблон реализации. Специфические особенности того, как дополнительное устройство хранения данных, и как помещал/получал, диспетчеризируется, отличаются с механизма на механизм - но это является самым ясным, я могу действительно описать дизайн/реализацию.

А незначительная дополнительная точка, в то время как спецификация ES относится к propertyName как строка механизмы JS, имеет тенденцию специализироваться на целочисленных поисках также, таким образом someObject[someInteger] не преобразует целое число в строку, если Вы посмотрите на объект, который имеет целочисленные свойства, например, Массив, Строку и типы DOM (NodeList с, и т.д.).

84
ответ дан Bharat Khatri 24 November 2019 в 13:53
поделиться

push() особый случай [[Помещенного]] более общего и поэтому может быть далее оптимизирован:

, Когда вызов [[Помещенный]] на объект массива, аргумент должен быть преобразован в целое число без знака сначала, потому что все свойство называет - включая индексы массива - строки. Тогда это должно сравниться со свойством длины массива, чтобы определить, должна ли длина быть увеличена. Когда продвижение, никакое такое преобразование или сравнение должно произойти: Просто используйте текущую длину в качестве индекса массива и увеличьте его.

, Конечно, существуют другие вещи, которые будут влиять на время выполнения, например, вызов push() должен быть медленнее, чем вызов [[Помещенный]] через [], потому что опытная цепочка должна быть проверена на первого.

<час>

, Поскольку olliej указал: фактические реализации ECMAScript оптимизируют преобразование далеко, т.е. для числовых имен свойства, никакое преобразование от строки до uint не сделано, но просто простая проверка типа. Основное предположение должно все еще содержать, хотя его влияние будет меньше, чем я первоначально принял.

6
ответ дан Christoph 24 November 2019 в 13:53
поделиться

Нажатие добавляет его до конца, в то время как массив [n] должен пройти массив для нахождения правильного пятна. Вероятно, зависит от браузера и его способа обработать массивы.

-5
ответ дан Stiropor 24 November 2019 в 13:53
поделиться

Эти результат, который я получил с вашим тестом

в Safari:

  • Array.push (n) 1 000 000 значений: 0,124 сек
  • Array [n .. 0] = значение {{ 1}} (по убыванию) 1 000 000 значений: 3,697 сек
  • Массив [0 .. n] = значение (по возрастанию) 1 000 000 значений: 0,073 секунды

в FireFox:

  • Array.push (n) 1 000 000 значений: 0,075 с
  • Массив [n .. 0] = значение (по убыванию) 1 000 000 значений: 1,193 с
  • Массив [0 .. n] = значение (по возрастанию) 1 000 000 значений: 0,055 сек

в IE7:

  • Array.push (n) 1 000 000 значений: 2,828 сек
  • Массив [n .. 0] = значение (по убыванию) 1 000 000 значений: 1,141 сек
  • Массив [0 .. n ] = значение (по возрастанию) 1 000 000 значений: 7,984 сек

Согласно ваш тест метод push кажется лучше в IE7 (огромная разница), а с другой стороны браузеры разница невелика, кажется, метод push действительно лучший способ добавить элемент в массив.

Но я создал еще один простой тестовый скрипт , чтобы проверить, какой метод быстрого добавления значений в массив, результаты меня очень удивили , использование Array.length кажется намного быстрее по сравнению с использованием Array.push , поэтому я действительно не знаю, что сказать или подумать, я не знаю.

Кстати: в моем IE7 ваш скрипт останавливается, и браузеры спрашивают меня, хочу ли я его продолжить (вы знаете типичное сообщение IE, в котором говорится: «Прекратить запускать этот скрипт? ...») Я рекомендовал бы немного уменьшить петли.

10
ответ дан 24 November 2019 в 13:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: