Предполагая карту, на которой вы хотите сохранить существующие записи. В 20% случаев вводимая вами запись - это новые данные. Есть ли преимущество в выполнении std :: map :: find, а затем std :: map :: insert с использованием этого возвращенного итератора? Или быстрее попытаться вставить, а затем действовать в зависимости от того, указывает ли итератор, что запись была вставлена или нет?
c++
optimization
stl
stdmap
Суперполок
источник
источник
Ответы:
Ответ: вы тоже. Вместо этого вы хотите сделать что - то предложенное пунктом 24 эффективного СТЛ по Скотт Мейерс :
typedef map<int, int> MapType; // Your map type may vary, just change the typedef MapType mymap; // Add elements to map here int k = 4; // assume we're searching for keys equal to 4 int v = 0; // assume we want the value 0 associated with the key of 4 MapType::iterator lb = mymap.lower_bound(k); if(lb != mymap.end() && !(mymap.key_comp()(k, lb->first))) { // key already exists // update lb->second if you care to } else { // the key does not exist in the map // add it to the map mymap.insert(lb, MapType::value_type(k, v)); // Use lb as a hint to insert, // so it can avoid another lookup }
источник
Ответ на этот вопрос также зависит от того, насколько дорого стоит создать тип значения, который вы храните на карте:
typedef std::map <int, int> MapOfInts; typedef std::pair <MapOfInts::iterator, bool> IResult; void foo (MapOfInts & m, int k, int v) { IResult ir = m.insert (std::make_pair (k, v)); if (ir.second) { // insertion took place (ie. new entry) } else if ( replaceEntry ( ir.first->first ) ) { ir.first->second = v; } }
Для типа значения, такого как int, приведенное выше будет более эффективным, чем поиск с последующей вставкой (при отсутствии оптимизации компилятора). Как указано выше, это связано с тем, что поиск по карте выполняется только один раз.
Однако для вызова insert требуется, чтобы у вас уже было сконструировано новое «значение»:
class LargeDataType { /* ... */ }; typedef std::map <int, LargeDataType> MapOfLargeDataType; typedef std::pair <MapOfLargeDataType::iterator, bool> IResult; void foo (MapOfLargeDataType & m, int k) { // This call is more expensive than a find through the map: LargeDataType const & v = VeryExpensiveCall ( /* ... */ ); IResult ir = m.insert (std::make_pair (k, v)); if (ir.second) { // insertion took place (ie. new entry) } else if ( replaceEntry ( ir.first->first ) ) { ir.first->second = v; } }
Чтобы вызвать 'insert', мы платим за дорогостоящий вызов для создания нашего типа значения - и, судя по тому, что вы сказали в вопросе, вы не будете использовать это новое значение в 20% случаев. В приведенном выше случае, если изменение типа значения карты не является вариантом, более эффективно сначала выполнить «поиск», чтобы проверить, нужно ли нам создавать элемент.
В качестве альтернативы тип значения карты можно изменить, чтобы сохранить дескрипторы данных, используя ваш любимый тип интеллектуального указателя. Вызов insert использует нулевой указатель (очень дешево для создания), и только в случае необходимости создается новый тип данных.
источник
Между ними не будет почти никакой разницы в скорости, find вернет итератор, insert сделает то же самое и все равно будет искать на карте, чтобы определить, существует ли уже запись.
Так что .. все зависит от личных предпочтений. Я всегда пытаюсь вставить, а затем обновить, если необходимо, но некоторым людям не нравится обрабатывать возвращаемую пару.
источник
Я бы подумал, что если вы выполните поиск, а затем вставку, дополнительные расходы будут, когда вы не найдете ключ и не выполните вставку после. Это похоже на то, как если бы вы просматривали книги в алфавитном порядке и не находили книгу, а затем снова просматриваете книги, чтобы увидеть, куда ее вставить. Все сводится к тому, как вы будете обращаться с ключами и будут ли они постоянно меняться. Теперь есть некоторая гибкость в том, что если вы ее не найдете, вы можете регистрировать, исключать, делать все, что хотите ...
источник
Если вас беспокоит эффективность, вы можете проверить hash_map <> .
Обычно map <> реализуется как двоичное дерево. В зависимости от ваших потребностей hash_map может быть более эффективным.
источник
Кажется, у меня недостаточно очков, чтобы оставить комментарий, но помеченный ответ мне кажется длинным - если учесть, что insert все равно возвращает итератор, зачем искать lower_bound, когда вы можете просто использовать возвращенный итератор. Странный.
источник
std::map::value_type
объект, принятый ответ позволяет избежать даже этого.Любые ответы об эффективности будут зависеть от точной реализации вашего STL. Единственный способ узнать наверняка - это провести оба теста. Я предполагаю, что разница вряд ли будет значительной, поэтому решайте, исходя из того, какой стиль вы предпочитаете.
источник
map [key] - пусть stl разберется. Это наиболее эффективная передача вашего намерения.
Да, достаточно честно.
Если вы выполняете поиск, а затем вставку, вы выполняете 2 x O (log N), когда вы получаете промах, поскольку поиск только сообщает вам, нужно ли вам вставлять, а не туда, куда вставка должна идти (lower_bound может помочь вам там) . Просто прямая вставка, а затем изучение результата - вот путь, которым я бы пошел.
источник