Предполагая карту, на которой вы хотите сохранить существующие записи. В 20% случаев вводимая вами запись - это новые данные. Есть ли преимущество в выполнении std :: map :: find, а затем std :: map :: insert с использованием этого возвращенного итератора? Или быстрее попытаться вставить, а затем действовать в зависимости от того, указывает ли итератор, что запись была или не была вставлена?





Между ними не будет почти никакой разницы в скорости, find вернет итератор, insert делает то же самое и в любом случае будет искать на карте, чтобы определить, существует ли уже запись.
Так что .. все зависит от личных предпочтений. Я всегда пытаюсь вставить, а затем обновить, если необходимо, но некоторым людям не нравится обрабатывать возвращаемую пару.
map [key] - пусть stl разберется. Это наиболее эффективная передача вашего намерения.
Да, достаточно честно.
Если вы выполняете поиск, а затем вставку, вы выполняете 2 x O (log N), когда вы получаете промах, поскольку поиск только сообщает вам, нужно ли вставлять, а не туда, куда вставка должна идти (lower_bound может помочь вам там) . Просто прямая вставка, а затем изучение результата - вот путь, которым я бы пошел.
Нет, если запись существует, она возвращает ссылку на существующую запись.
-1 за этот ответ. Как сказал Крис К., использование map [key] = value перезапишет существующую запись, а не «сохранит» ее, как требуется в вопросе. Вы не можете проверить наличие с помощью map [key], потому что он вернет созданный по умолчанию объект, если ключ не существует, и создаст его как запись для ключа
Дело в том, чтобы проверить, заполнена ли карта, и только добавить / перезаписать, если ее там нет. Использование map [key] предполагает, что значение уже существует всегда.
Любые ответы об эффективности будут зависеть от точной реализации вашего STL. Единственный способ узнать наверняка - это сравнить результаты в обоих направлениях. Я предполагаю, что разница вряд ли будет значительной, поэтому решайте, исходя из того, какой стиль вы предпочитаете.
Это не совсем так. STL отличается от большинства других библиотек тем, что предоставляет явные требования big-O для большинства своих операций. Существует гарантированная разница между 2 * O (log n) и 1 * O (log n), независимо от того, какую реализацию функции используют для достижения этого поведения O (log n). Другой вопрос, является ли эта разница существенный на вашей платформе. Но разница будет всегда.
@srm, определяющий требования big-O, по-прежнему не говорит вам, сколько времени займет операция в абсолютном выражении. Гарантированной разницы, о которой вы говорите, не существует.
Я бы подумал, что если вы выполните поиск и вставку, дополнительные расходы будут возникать, если вы не найдете ключ и не выполните вставку после. Это как если бы вы просматривали книги в алфавитном порядке и не находили книгу, а затем снова просматривали книги, чтобы увидеть, куда ее вставить. Все сводится к тому, как вы будете обращаться с ключами и будут ли они постоянно меняться. Теперь есть некоторая гибкость в том, что если вы ее не найдете, вы можете регистрировать, исключать, делать все, что хотите ...
Если вас беспокоит эффективность, вы можете проверить hash_map <>.
Обычно map <> реализуется как двоичное дерево. В зависимости от ваших потребностей hash_map может быть более эффективным.
Хотел бы. Но в стандартной библиотеке C++ нет hash_map, и PHB не допускают кода за пределами этого.
Ответ на этот вопрос также зависит от того, насколько дорого стоит создание типа значения, который вы храните на карте:
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, приведенное выше будет более эффективным, чем поиск с последующей вставкой (при отсутствии оптимизации компилятора). Как указано выше, это связано с тем, что поиск по карте выполняется только один раз.
Однако вызов вставки требует, чтобы у вас уже было сконструировано новое «значение»:
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% случаев. В приведенном выше случае, если изменение типа значения карты не является вариантом, более эффективно сначала выполнить «поиск», чтобы проверить, нужно ли нам создавать элемент.
В качестве альтернативы тип значения карты можно изменить, чтобы сохранить дескрипторы данных, используя ваш любимый тип интеллектуального указателя. Вызов вставки использует нулевой указатель (очень дешево для создания), и только в случае необходимости создается новый тип данных.
Ответ: вы тоже. Вместо этого вы хотите сделать что-то, что предложено в пункте 24 Эффективный STL от Скотт Мейерс:
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
}
Это действительно так, как работает find, уловка в том, что он объединяет поиск, необходимый для поиска и вставки. Конечно, то же самое и с использованием вставки с последующим просмотром второго возвращаемого значения.
Два вопроса: 1) Чем отличается использование lower_bound от поиска для карты? 2) Для «карты», не правда ли, что операция правой руки && всегда истинна, когда «lb! = Mymap.end ()»?
@Richard: find () возвращает end (), если ключ не существует, lower_bound возвращает позицию, в которой должен находиться элемент (что, в свою очередь, может использоваться как подсказка для вставки). @puetzek: Не будет ли "просто вставка" перезаписать референтное значение для существующих ключей? Не уверен, хочет ли ОП этого.
кто-нибудь знает, есть ли что-то подобное для unordered_map?
@Helltone: Реализации MS (msdn.microsoft.com/en-us/library/bb982322.aspx) и IBM (publib.boulder.ibm.com/infocenter/comphelp/v9v111/topic/…), похоже, имеют вставки с подсказками, но я не могу догадаться, как они будут работать с несортированными данными - как угадать подходящую точку вставки?
Они этого не делают. Подсказка не используется, только для совместимости. См. здесь
@puetzk «просто использовать вставку» - это нормально, если вставляемая вещь стоит недорого, см. ответ Ричарда Кордена. Решение Скотта Мейера обходит это.
@peterchen map :: insert не перезаписывает существующее значение, если оно существует, см. cplusplus.com/reference/map/map/insert.
Разве это не меняется на upper_bound в C++ 11? См. Раздел о сложности. en.cppreference.com/w/cpp/container/map/insert
@Tyler lower_bound возвращает первый ключ, который больше или равен (> =) искомому ключу, а upper_bound возвращает первый ключ, который больше (>), чем искомый ключ. Если искомого ключа нет на карте, lower_bound и upper_bound возвращают один и тот же итератор, поэтому ответ остается действительным. Имя «lower_bound» неточно для функции.
Кажется, у меня недостаточно очков, чтобы оставить комментарий, но отмеченный ответ мне кажется длинным - если учесть, что insert все равно возвращает итератор, зачем искать lower_bound, когда вы можете просто использовать возвращенный итератор. Странный.
Поскольку (конечно, до C++ 11) использование вставки означает, что вам все равно нужно создать объект std::map::value_type, принятый ответ позволяет избежать даже этого.
Я был исправлен и намеревался использовать std :: map :: lower_bound вместо std :: map :: find.