Строка карты Javascript по сравнению с целочисленной ключевой производительностью

#javascript #dictionary #key-value

Вопрос:

Мой случай заключается в том, что мне нужно сохранить уникальный ключ, чтобы отслеживать миллионы моих моделей в Javascript, и сохранить пару ключ-значение на карте.

Существует существующая библиотека uuid, которая сгенерирует для меня строковый ключ, но я боюсь, что мне понадобится целочисленный ключ для достижения оптимальной производительности. Однако, похоже, мне придется самому преобразовать строку uuid в уникальное целое число, что не является тривиальным и также имеет накладные расходы.

Есть ли существенная разница в использовании строки в качестве ключа для карты или целого числа?

Комментарии:

1. Вы измерили производительность? Как вы думаете, почему строковые ключи и целочисленные ключи имеют существенно различную производительность?

2. Единственный раз, когда я знаю, что целые числа быстрее, — это когда вы используете разреженный массив вместо карты. «Существует существующая библиотека uuid, которая сгенерирует для меня строковый ключ…» Если uuid в любом случае произвольны, есть ли какая-либо причина, по которой вы не можете начать с 0 и увеличить 1 для каждого?

3. @Крис Велтон, спасибо тебе. После рассмотрения на самом деле кажется, что нет причин, по которым я не должен начинать с 0 и увеличивать 1 каждый. В этом случае было бы быстрее, если бы я использовал обычный разреженный массив вместо карты?

4. Я приведу вам пример…

Ответ №1:

Вот решение, основанное на нашем разговоре.

Существует много подобных способов сделать это, но главное-сохранить обратный индекс для поиска в самой модели, чтобы каждая модель «знала», где она находится в индексе модели.

Правка: В моем первом примере была ошибка, которая появилась бы, если бы массив стал разреженным из-за сокращения array.length.

Это более продвинутый пример, который избавляется от ошибки и имеет класс dataIndex, отвечающий за индексацию, и в котором модели могут выполнять обратный поиск для нескольких индексов.

 class dataIndex {
    constructor(indexId) {
        this.vec = [];
        this.indexId = indexId;
        this.indexNext = 0;
    }
    indexModel(model) {
        this.vec[model.lookup[this.indexId] = this.indexNext  ] = model;
        return this;
    }
}

class dataModel {
    constructor(data) {
        this.data = data;
        this.lookup = new Map();
    }
    static compareData(a, b) {
        return (a.data === b.data) ? 0:
            (a.data > b.data) ? 1 : -1;
    }
}

const modelIndex = new dataIndex('primary');
const sortedIndex = new dataIndex('sorted');

for (let i = 0; i < 10; i  ) {
    let newModel = new dataModel(Math.random());
    modelIndex.indexModel(newModel);
}

const ordered = modelIndex.vec.sort((a, b) => dataModel.compareData(a, b))
ordered.forEach(model => sortedIndex.indexModel(model));

console.log(ordered);
 

Выход:

 [
  dataModel {
    data: 0.08420389624353097,
    lookup: Map(0) { primary: 9, sorted: 0 }
  },
  dataModel {
    data: 0.1528733550120258,
    lookup: Map(0) { primary: 7, sorted: 1 }
  },
  dataModel {
    data: 0.28483626134194595,
    lookup: Map(0) { primary: 8, sorted: 2 }
  },
  dataModel {
    data: 0.3257986769682104,
    lookup: Map(0) { primary: 5, sorted: 3 }
  },
  dataModel {
    data: 0.3409113857134396,
    lookup: Map(0) { primary: 3, sorted: 4 }
  },
  dataModel {
    data: 0.3841968173496322,
    lookup: Map(0) { primary: 1, sorted: 5 }
  },
  dataModel {
    data: 0.40414714769598237,
    lookup: Map(0) { primary: 4, sorted: 6 }
  },
  dataModel {
    data: 0.5817767975980099,
    lookup: Map(0) { primary: 0, sorted: 7 }
  },
  dataModel {
    data: 0.8091360598739015,
    lookup: Map(0) { primary: 2, sorted: 8 }
  },
  dataModel {
    data: 0.8217632650897493,
    lookup: Map(0) { primary: 6, sorted: 9 }
  }
]
 

Комментарии:

1. Спасибо вам за ответ. Мне интересно, это в среднем быстрее или медленнее, чем если бы indexArray был картой, учитывая, что модели также могут быть удалены. Т. Е. может произойти случай, когда revIndex начинается со 100000 и заканчивается 999999 с отверстиями посередине.

2. Когда начинают происходить «дыры», это называется переходом от «плотного» массива к «разреженному». Как правило, разреженные массивы довольно хорошо оптимизированы в большинстве реализаций javascript. (На самом деле он хранит их во многом как объекты с ключами и значениями, но внутри они могут сильно оптимизировать «хэширование» в основном непрерывных целых чисел.) news.qooxdoo.org/…

3. @cr001, чтобы дать вам представление о том, что для создания и индексирования 1000000 случайных чисел в моих ноутбуках требуется 175 мс node.js. Сколько миллионов вы хотите отслеживать сразу?

4. Проблема в том, что на данный момент я не совсем уверен, насколько велика она может быть. Он используется для отслеживания чего-то вроде соединений сокетов, поэтому он зависит от многих факторов, которые сейчас невозможно смоделировать. В Java я был бы почти уверен, что разреженные массивы быстрее, чем карты деревьев в случае целочисленных ключей, однако я не уверен, что это так в Javascript.

5. Ну, я не знаю ничего, что было бы быстрее, и второе преимущество использования массива заключается в том, что он обеспечивает легкую доступность собственных оптимизированных Array.filter и Array.sort, которые могут принимать пользовательские обратные вызовы для фильтрации и сортировки. Поэтому, если у вас 10 миллионов записей и вам нужно удалить самые старые 2 миллиона, у вас есть Array.filter, Array.sort, Array.splice и Array.slice, которые помогут вам.