- remove 用來移除容器對應迭代器區間[first, last)中,所有值與value相等的元素。相等通過operator== 來比較。
- remove_if 用來移除容器對應迭代器區間[first, last)中,滿足判別式p返回true的元素。
函數模板原型
#include <algorithm>
template< class ForwardIt, class T >
ForwardIt remove( ForwardIt first, ForwardIt last, const T& value );
template< class ForwardIt, class UnaryPredicate >
ForwardIt remove_if( ForwardIt first, ForwardIt last, UnaryPredicate p );
- 對於vector、deque、string等連續內存的順序容器
- remove 是通過迭代器的指針不斷向前移動來“刪除”元素的,最終會將值與value相等的元素移動到末尾,返回值就是這些與value相等的第一個元素位置對應的迭代器。remove 並不會改變這些容器的大小,如果要真正刪除元素、同時減小容器實際元素個數,應該結合容器的erase成員函數。
- remove_if 原理類似於remove,區別在於前者是通過判別p返回值來刪除元素的,后者是通過判斷是否與value相等。
remove示例:
“虛假刪除”vec中所有值與3相等的元素(容器尺寸不變):
vector<int> vec({ 1, 2, 3, 3, 9, 10, 3, 4, 5, 8});
auto it = remove(vec.begin(), vec.end(), 3); // vec為"1 2 9 10 4 5 8 4 5 8"
auto d = std::distance(vec.begin(), it); // it到vec.begin的元素個數d為7
cout << *it << endl; // 打印"4"
vec共有3個"3",所以remove調用后,3個"3"全部移動到vec末尾。也就是說,vec有效內容應該是"1 2 9 10 4 5 8 x x x" ,后面3個x是多余出來的空間,remove返回的迭代器指向第一個"x"。如果要真刪除元素,就應該搭配容器的成員函數erase,來刪除最后3個元素;或者調用resize重新調整容器大小。
下面用vector::erase 搭配remove,真正刪除值為3的元素值(容器尺寸變小):
vector<int> vec({ 1, 2, 3, 3, 9, 10, 3, 4, 5, 8});
// vector::erase + remove 刪去一個元素
cout << vec.size() << endl; // 打印"10"
vec.erase(remove(vec.begin(), vec.end(), 3)); // vec為"1 2 9 10 4 5 8 5 8",只會真正刪除1個元素
cout << vec.size() << endl; // 打印"9"
// vector::erase + remove 刪除一個區間
cout << vec.size() << endl; // 打印"10"
vec.erase(remove(vec.begin(), vec.end(), 3), vec.end()); // vec為"1 2 9 10 4 5 8",刪除一個區間元素
cout << vec.size() << endl; // 打印"7"
注意:前一個erase的參數只有一個迭代器,故只會刪除迭代器指向的那1個元素;后一個erase的參數是一個區間,故刪除若干個元素。
remove_if示例:
只舉運用remove_if刪除vector中元素值 > 4的例子:
#include <functional>
bool badValue(const int a, const int sz)
{
return a > sz;
}
using std::placeholders::_1;
vector<int> vec({ 1, 2, 3, 3, 9, 10, 3, 4, 5, 8}); // 10個元素
const int sz = 4;
auto it = remove_if(vec.begin(), vec.end(), std::bind(badValue, _1, sz)); // vec為"1 2 3 3 3 4 3 4 5 8"
// auto it = remove_if(vec.begin(), vec.end(), [sz](const int a) { return a > sz; }); 等價於 上面的語句
cout << *it << endl; // 打印"3"
cout << std::distance(vec.begin(), it) << endl; // 打印"6"
vec.erase(it, vec.end()); // vec為"1 2 3 3 3 4"
cout << vec.size() << endl; // 打印"6"
同樣的,調用remove_if之后,vec會通過移動的方式覆蓋掉待刪除元素,成為"1 2 3 3 3 4 x x x x"(有4個元素值 > 4)。調用erase后,會真正刪除末尾4個元素,減小容器尺寸。
- 對於list、forward_list等不連續內存的順序容器
- std::remove, std::remove_if也適用於list、forward_list
- 成員函數remove將移除元素
list::remove示例
list<int> lst = { 1,2,3,4,5,6,5,8,10 }; // 9個元素
cout << lst.size() << endl; // 打印9
for_each(lst.begin(), lst.end(), [](const int x) { cout << x << " "; }); // 1 2 3 4 5 6 5 8 10
cout << endl; // 打印7
lst.remove(5);
cout << lst.size() << endl;
for_each(lst.begin(), lst.end(), [](const int x) { cout << x << " "; }); // 1 2 3 4 6 8 10
cout << endl;
list::remove_if示例
list<int> lst = { 1,2,3,4,5,6,5,8,10 };
cout << lst.size() << endl; // 打印9
for_each(lst.begin(), lst.end(), [](const int x) { cout << x << " "; }); // 1 2 3 4 5 6 5 8 10
cout << endl;
int sz = 5;
lst.remove_if([sz](const int x) { return x >= sz; }); // 刪除list中 >= 5的元素
cout << lst.size() << endl; // 打印4
for_each(lst.begin(), lst.end(), [](const int x) { cout << x << " "; }); // 1 2 3 4
cout << endl;
- 對於std::set、std::map等關聯容器
- remove算法、remove_if算法不能應用,因為它們的迭代器類型沒有間接引用MoveAssignable(移動賦值)類型(容器中的key不能修改)。
- set、map也沒有remove、remove_if成員函數。
如果要刪除關聯容器中的若干元素,可以先將不打算刪除的元素拷貝到一個新容器中,然后再跟當前容器交換(swap)。
map刪除元素示例
使用remove_copy_if + std::swap,拷貝並交換容器
map<string, int> m;
m.insert(pair<string, int>("aa", 1));
m["a"] = 1;
m["b"] = 2;
m["c"] = 3;
m["d"] = 4;
map<string, int> tmpm;
cout << m.size() << endl; // 打印5
// (a,1) (aa,1) (b,2) (c,3) (d,4)
for_each(m.begin(), m.end(), [](const pair<string, int>& pr) { cout << "(" << pr.first << "," << pr.second << ") "; });
cout << endl;
int sz = 3;
remove_copy_if(m.begin(), m.end(), inserter(tmpm, tmpm.end()), [sz](const pair<string, int>& s) {
return s.second >= sz;
});
m.swap(tmpm);
cout << m.size() << endl; // 打印3
// (a,1) (aa,1) (b,2)
for_each(m.begin(), m.end(), [](const pair<string, int>& pr) { cout << "(" << pr.first << "," << pr.second << ") "; });
cout << endl;
