indexOf в стиле ArrayList для std::vector в С++?

я перехожу на С++ с Java и имею общую ситуацию проектирования, в которой у меня есть элемент (не примитивный), который я хотел бы удалить из std::vector.

в Java я бы написал что-то вроде: arrayList.remove(arrayList.indexOf(myClassInstance));

в С++ с помощью std::vector, какой лучший/самый эффективный/самый чистый способ сделать это?

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

я на правильном пути? или есть лучший способ сделать это? (возможно, используя другой контейнер std, я пока использовал только std::vector.)


person ericsoco    schedule 22.11.2010    source источник
comment
Предполагая, что у вас есть коллекция указателей или shared_ptr, std::set может хорошо работать для вас, просто сравнивая адреса указателей. Если вы знаете адрес элемента, который вы ищете, просто mySet.erase(ptr);   -  person CashCow    schedule 22.11.2010
comment
@CashCow - есть ли большая разница в производительности при переборе всех членов std::set по сравнению с std:vector? в другом месте моего кода я вызываю метод для каждого элемента в наборе, каждый цикл.   -  person ericsoco    schedule 22.11.2010


Ответы (3)


Используйте std::find, чтобы найти элемент, и vector::erase, чтобы удалить его.

std::find по существу перебирает вектор, чтобы найти элемент, и вы не можете добиться большего успеха с простым вектором (то же самое в случае с ArrayList в Java). Следует ли вам использовать другой контейнер, зависит от ваших требований.

person casablanca    schedule 22.11.2010
comment
+1. Обратите внимание, что если вы удаляете несколько элементов, соответствующих предикату, вы также должны использовать std::remove_if :) - person Billy ONeal; 22.11.2010
comment
оо. не знал, что со стандартными контейнерами можно делать так много... -- cplusplus.com /ссылка/алгоритм - person ericsoco; 22.11.2010
comment
@eric: Добро пожаловать в чудесный мир универсального программирования! - person fredoverflow; 22.11.2010
comment
@ericsco: функциональность, предоставляемая <algorithm>, немного похожа на Collections в Java. - person casablanca; 22.11.2010

Если вы хотите искать линейно по вектору, тогда

seq.erase( std::find( seq.begin(), seq.end(), elt ));

Если у вас есть предикат и вы хотите удалить все элементы, соответствующие предикату, тогда:

seq.erase( std::remove_if( seq.begin(), seq.end(), Pred ), seq.end());

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

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

Если возможно хранить ваши элементы в ассоциативном контейнере, который использует поиск по ключу, тогда это будет более эффективно: поиск O (log N) и удаление за постоянное время.

Хэш-карта может быть даже лучше, близка к постоянному поиску и удалению времени.

Для того, что вы предлагаете, т.е. стирания указателем объекта, вы можете использовать std::set для своего типа T. Затем используйте mySet.erase( pt );, где pt - ваш указатель. Конечно, вам нужно управлять временем жизни ваших указателей, но тот факт, что вы знаете, какой из них стереть из своей коллекции, предполагает, что у вас есть его копия в другом месте.

Вы можете использовать std::set, SharedPtrLess >

где вы определяете SharedPtrLess следующим образом:

template< typename T >
struct SharedPtrLess
{
   bool operator()( boost::shared_ptr<T> left, boost::shared_ptr<T> right ) const
   {
     return std::less<T>()( left.get(), right.get());
   }
};
person CashCow    schedule 22.11.2010
comment
отличные советы на будущее. я начну с того, что возьму std::vector под свой пояс, а оттуда пойду дальше... спасибо! - person ericsoco; 22.11.2010

person    schedule
comment
Я думаю, что вы что-то упустили в конце этого std::find звонка :) - person Billy ONeal; 22.11.2010
comment
разве это не плохая идея стереть во время итерации? или, я думаю, это неплохая идея, потому что, как только мы вызываем vector.erase, мы закончили с итератором, и больше не имеет значения, признан ли он недействительным. - person ericsoco; 22.11.2010
comment
@Billy: Спасибо, что заметили это :) @eric: Если бы мы действительно делали итерацию вручную, нам пришлось бы быть очень осторожными (но мы этого не делаем). Инвалидация итератора — очень интересная тема. Я чую очередной FAQ? ;-) - person fredoverflow; 22.11.2010