7

For removing an iterator from std::vector I can do these two things:

std::vector<int>& vec = myNumbers; // use shorter name
vec.erase(std::remove(vec.begin(), vec.end(), number_in), vec.end());

Or I can do this:

auto it = find(vec.begin(), vec.end(), number_in);
vec.erase(it);

The second is more intuitive, I guess, but which one is faster?

EDIT: Elements in vector are unique and we don't have to worry to delete several elements at once.

11
  • 2
    These are definitely different ones. The first removes all of elements which are number_in, and the second removes one of elements which are number_in. Commented Feb 5, 2015 at 10:58
  • What if there is more than one element in the vector that satisfies the removal condition? You use the first one if you're not sure how many items may be removed. Commented Feb 5, 2015 at 10:58
  • Sorry, I forgot to mention that the elements are unique. Editing. Commented Feb 5, 2015 at 10:58
  • 1
    @Narek - The second one is faster, but it is not safer. What if the element can't be found? Commented Feb 5, 2015 at 11:04
  • 2
    FWIW, if you happen not to care about element ordering, it's faster to find then overwrite the found element with vec.back(), then vec.pop_back(). That makes the post-find erase part of the operation O(1), instead of linear in the number of elements thereafter in the vector (as it shuffles them front-ward). Commented Feb 5, 2015 at 11:13

4 Answers 4

2

The first one is guaranteed to work correctly, while your second version may be faster (due to std::find stopping at the first item that matches), but it certainly is not safer.

auto it = find(vec.begin(), vec.end(), number_in);
if (it != vec.end())
   vec.erase(it);

That would ensure you are not erasing an invalid iterator.

So it depends on your needs. If you want a program that is correct, the first one works without further intervention, however the second requires a bug ticket from your customer and then you have to go fix it (as above).

Sign up to request clarification or add additional context in comments.

4 Comments

@Walter I am completely confused between your comment, PaulMcKenzie and ks1322 answers. :)
"but it certainly is not safer" - I disagree with the emphasis here... the "find / if !end erase" approach is idiomatic when you know there's a single-element, and any vaguely experienced C++ programmer knows when/if a check against end is needed and the implications either way when reading code; that contrasts with the remove approach which firmly suggests potential for multiple values - breaking down the readers mental model of the data and logic, which can lead to errors during program maintenance and evolution.
@TonyD You're right, but maybe I'm a stickler for correct code. I remember answering a question from a poster a while ago, where the poster had sworn that the items were unique, but had issues with the program. Guess what -- the items were not unique.
@PaulMcKenzie: well, maybe it's lucky they tried erase not remove so they discovered the problem earlier...! :-) I would say that either of a check against end() and an assertion that it is not end() are as correct/robust as using remove, without the confusing implication that there might be 0, 1, 2 or even more copies of the value. Anyway - my point's here for readers - not pushing you to also change your answer if you don't heartily agree. Cheers.
1

The second is faster - the first will try to find all of elements which are equal to number_in, although it has already found one. However, the second will stop when it find one.

5 Comments

Good observation, but could be misunderstood - "However, the second will stop when it find one." - it'll stop bothering with the comparisons but still compact the remaining elements towards the front of the vector.
@TonyD "but still compact the remaining elements...". What you mean?
@Narek erase will keep the order of all other elements, which will all be moved or copied to a new position to fill the gap left by the erased element(s).
Yes, but after erasing. @ikh was talking about searching only, I think.
@Narek: let's say vec has 5 elements and the 2nd is to be erased, the 3rd will be copied over the 2nd, then the 4th will be copied over the 3rd, then the 5th over the fourth, then the destructor for the fifth element will run. So - there's "compaction" happening. For simple POD types (basically, types without complicated constructors/destructors/pointers/references etc), the implementation may be able to use a memmove style operation to do compaction much faster, but the amount of work is still related to the remaining vector size.
1

std::vector::erase

  • Removes the element at pos.

  • Removes the elements in the range [first; last).

    Invalidates iterators and references at or after the point of the erase, including the end() iterator. The iterator pos must be valid and dereferenceable. Thus the end() iterator (which is valid, but is not dereferencable) cannot be used as a value for pos. The iterator first does not need to be dereferenceable if first==last: erasing an empty range is a no-op.

http://en.cppreference.com/w/cpp/container/vector/erase

The first approach would be slower since the entire vector will be searched for the number. But it is also the safer way. Consider number_in is not an element of your vector. The first approach would try to erase an empty range which is defined and safe. And the second approach would try to erase the end iterator of your vector which is unsafe and UB.

Comments

1

It appears that you're interested in performance. Finding the (first) matching element cannot be done faster than O(n). So, the part where you can try to improve performance is the removal, which can be O(1), if you allow the order of the vector elements to change. For example

// find requires up to O(n)
auto it = std::find(v.begin(),v.end(),value);
// remove in O(1) but don't preserve order
if(it!=v.end()) {
  std::iter_swap(it,--(v.end()));
  v.pop_back();
}

Note that solutions using std::remove() and/or vector::erase() will preserve the order of the remaining elements and hence inevitable still compact the remaining elements (quoted from comment by Tony D), which is almost always more expensive than finding the matching element and hence dominates the computational costs.

Just try which solution is faster – the proof of the pudding is in the eating!

5 Comments

vec.erase(std::remove will not operate in o(n) too? Like std::remove swaps with the last elem, and then erase removes with o(1), right? But we don't preserve the order?
Does std::remove() swap with the last element? I don't think so, since it (according to cppreference) preserves the order of the remaining elements and has complexity O(n).
Agreed! So it is basically o(n) + o(n) = o(n)
Are you interesting in performance or in arguing? If you want to make your code faster, then you should consider the fastest method, regardless of its O() scaling: suppose moving costs twice as much as comparison, then your solutions cost n+2n=3n but my solution only n+1<3n, even though both are O(n).
To be honest I wanted to have a full discussion about these two methods. Your suggestion is the best, in terms of performance, I don't argue.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.