From: Dariusz Murakowski Date: Wed, 20 Aug 2014 20:26:56 +0000 (-0400) Subject: STL compliant sorted vector class (std::map replacement) from CodeProject. X-Git-Url: http://src.murakowski.org/?a=commitdiff_plain;h=6d15f6a14df7cc31db311e4c55c9cda2f8037701;p=VirEvoDyn.git STL compliant sorted vector class (std::map replacement) from CodeProject. Source: http://www.codeproject.com/Articles/3217/An-STL-compliant-sorted-vector?display=Print Introduction: sorted_vector adapts a std::vector to the interface required by std::set/std::multiset, thereby providing set/multiset and vector functionality (random access) in one container. Tests show that sorted_vector's element retrieval (find) speed outperforms that of std::set/std::multiset by a factor of 2 (on most machines). On the downward side is the poor performance of sorted_vector's insert member function, because it inserts an element somewhere in the middle of the sorted vector in order to preserve the sort order. By using sorted_vector's low level interface one can solve this performance bottleneck by inserting a bunch of objects using a series of push_back's followed by a call to the member function sort, which restores the sort order. sorted_vector should be preferred over std::set/std::multiset if many small elements must be stored. Most STL implementations use a variant of a balanced tree to implement set and multiset, which imposes a per element storage overhead of 3 pointers. The most important reason to use a std::set/std::multiset instead of sorted_vector is the need to keep iterators into a set/multiset while inserting more elements into the set. These iterators remain valid in the case of a set/multiset, but are invalidated in the case of a sorted_vector container. --- diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..6fb6acc --- /dev/null +++ b/main.cpp @@ -0,0 +1,387 @@ + +#pragma warning(disable:4786) + +#include +#include "sorted_vector.h" +#include +#include "assert.h" +#include +#include + +struct STest{ + std::string s; + int id; +}; + +STest MakeSTest() +{ + static int id= 0; + int nLen= rand()%10; + char* letters[]={"0","1"}; + STest stest; + + for(int i=0;i +bool is_unique(It beg,It beyond,Pred pred) +{ + return std::adjacent_find(beg,beyond,std::not2(pred))==beyond; +} +template +FwIt unsorted_find(FwIt beg,FwIt beyond,Pred pred) +{ + for(FwIt prev ;(prev=beg)!=beyond && ++beg!=beyond;){ + if( pred(*beg,*prev) ){ + return prev; + } + } + return beyond; +} +template +void TestSet(std::vector& v) +{ + std::set std_set; + {SimpTimer t("build std::set"); + for(unsigned i=0;i::iterator it= std_set.find(v[i]); + std::set::size_type nCount= std_set.count(v[i]); + assert( nCount==0 && it==std_set.end() + || nCount!=0 && it!=std_set.end()); + } + } +} +template +void TestSortedVector_AsSet(std::vector& v) +{ + codeproject::sorted_vector svec; + {SimpTimer t("build sortedvec_set (naiv)"); + for(unsigned i=0;i svec1; + {SimpTimer t("build sortedvec_set (opt.)"); + codeproject::sorted_vector::Cont& vec= svec1.get_container(); + for(unsigned i=0;i::iterator it= svec1.find(v[i]); + codeproject::sorted_vector::size_type nCount= svec1.count(v[i]); + assert( nCount==0 && it==svec1.end() + || nCount!=0 && it!=svec1.end()); + } + } + for(unsigned i=0;i +void TestMultiSet(std::vector& v) +{ + std::multiset svec; + + {SimpTimer t("build multiset"); + for(unsigned i=0;i::iterator it= svec.find(v[i]); + std::multiset::size_type nCount= svec.count(v[i]); + assert( nCount==0 && it==svec.end() + || nCount!=0 && it!=svec.end()); + } + } +} + +template +void TestSortedVector_AsMultiSet(std::vector& v) +{ + codeproject::sorted_vector svec; + {SimpTimer t("build sortedvec_multiset (naiv)"); + for(unsigned i=0;i svec1; + {SimpTimer t("build sortedvec_multiset (opt.)"); + codeproject::sorted_vector::Cont& vec= svec1.get_container(); + for(unsigned i=0;i::iterator it= svec1.find(v[i]); + codeproject::sorted_vector::size_type nCount= svec1.count(v[i]); + assert( nCount==0 && it==svec1.end() + || nCount!=0 && it!=svec1.end()); + } + } +/*test various functions*/ + const codeproject::sorted_vector svec2(svec); + assert(svec==svec2); + for(unsigned i=0;i svec3(v.begin(),v.end()); + assert(svec3==svec2); + codeproject::sorted_vector svec4(v.begin(),v.begin()+(v.end()-v.begin())/2); + svec4= svec3; + assert(svec4==svec3); + while(svec4.size()>0){ + svec4.pop_back(); + } +} + + + + +template +void ExecTests(std::vector& v) +{ + std::cout << "std::set versus 'sorted_vector as set'" << std::endl; + TestSet(v); + TestSortedVector_AsSet(v); + std::cout << "std::multiset versus 'sorted_vector as multiset'" << std::endl; + TestMultiSet(v); + TestSortedVector_AsMultiSet(v); +} + +template +void TestSetOperations(const std::vector& v0, + const std::vector& v1, + const std::vector& v2, + Pred pred) +{ +//A) compute the result of the set-operation: (v0-v1)+v2 - intersect(v1,v2) + codeproject::sorted_vector svec(v0.begin(),v0.end(),pred); + codeproject::sorted_vector svec_v1(pred); svec_v1= v1; + codeproject::sorted_vector svec_v2(pred); svec_v2= v2; + unsigned i,j; + for(i=0;i svec1(v0.begin(),v0.end(),pred); + for(unsigned k=0;k::size_type nSize= svec.size(); + codeproject::sorted_vector::size_type nSize1= svec1.size(); +//test whether results are the same + assert(svec==svec1); +} + + +codeproject::sorted_vector +BuildIntersection(std::vector& v0,std::vector& v1) +{ + codeproject::sorted_vector svec(v0.begin(),v0.end()); + codeproject::sorted_vector svecIntersection; + for(unsigned i=0;i +BuildIntersection1(std::vector& v0,std::vector& v1) +{ + codeproject::sorted_vector svec(v0.begin(),v0.end()); + codeproject::sorted_vector svecIntersection; + codeproject::sorted_vector::Cont& vInterSect= + svecIntersection.get_container(); + for(unsigned i=0;i A(a, a + N); + sorted_vector B(b, b + N); + sorted_vector C; + + cout << "Set A: "; + copy(A.begin(), A.end(), ostream_iterator(cout, " ")); + cout << endl; + cout << "Set B: "; + copy(B.begin(), B.end(), ostream_iterator(cout, " ")); + cout << endl; + + cout << "Union: "; + set_union(A.begin(), A.end(), B.begin(), B.end(), + ostream_iterator(cout, " "), + ltstr()); + cout << endl; + return 0; +} + +void TestAllSet() +{ + using namespace std; + using namespace codeproject; + typedef sorted_vector StrSet; + StrSet months; + months.insert("jan"); + months.insert("feb"); + months.insert("mar"); + months.insert("apr"); + months.insert("may"); + months.insert("jun"); + months.insert("jul"); + months.insert("aug"); + months.insert("sep"); + months.insert("oct"); + months.insert("nov"); + months.insert("dec"); + StrSet::iterator it= months.find("jul"); + assert(strcmp(*it,"jul")==0); + cout << "previous of jul (in alphabetical order) is " << (it[-1]) << endl; + cout << "next of jul (in alphabetical order) is " << (it[1]) << endl; + + cout << "months in alphabetical order: " << endl; + copy(months.begin(),months.end(),ostream_iterator(cout," ")); + cout << endl << "months in reverse alphabetical order: " << endl; + copy(months.rbegin(),months.rend(),ostream_iterator(cout," ")); + /*test count*/ + { + for(StrSet::iterator it= months.begin();it!=months.end();++it){ + assert(months.count(*it)==1); + } + } + /*test copy construction and comparison operators*/ + StrSet monthsCopy(months); + assert( months==monthsCopy + && months<=monthsCopy && months>=monthsCopy + && !(monthsmonthsCopy)); + + std::pair pairMismatch= + mismatch(months.begin(),months.end(),monthsCopy.begin()); + assert(pairMismatch.first==months.end() && pairMismatch.second==monthsCopy.end()); + + /*test insertion of already present element*/ + copy(months.begin(),months.end(),inserter(monthsCopy,monthsCopy.begin())); + assert(months.size()==monthsCopy.size()); + + /*test insert member functions*/ + months.insert(monthsCopy.begin(),monthsCopy.end()); + assert(months==monthsCopy); + StrSet months1(months.begin(),months.begin()+3); + months1.insert(months.begin()+1,months.end()); + assert(months1==months); + months1.insert("aug"); + months1.insert("xxx"); + months1.insert(months1.find("xxx"),"yyy"); + months1.insert("zzz"); + assert(months1>months && months1.size()==months.size()+3); + /*test erase member functions*/ + months1.erase(months1.find("xxx"),months1.end()); + assert(months1.size()==months.size()); + + /*test lower_bound,upper_bound,equal_range*/ + assert( strcmp(*months.lower_bound("jul"),"jul")==0); + + + cout << endl; +} + + +int main() +{ +//timed tests + std::vector v; + int i; + for(i=0;i<50000;i++){v.push_back(rand());} + std::cout << "--------------Tests with element type int-------------" << std::endl; + ExecTests(v); + + std::vector vt; + for(i=0;i<50000;i++){vt.push_back(MakeSTest());} + std::cout << "-Tests with element type 'STest' (string,int)--------" << std::endl; + ExecTests(v); + +//set operations-test + std::vector v1,v2; + for(i=0;i<10000;i++){v1.push_back(rand());} + for(i=0;i<10000;i++){v2.push_back(rand());} + TestSetOperations(v,v1,v2,std::greater()); + + assert(BuildIntersection(v1,v2)==BuildIntersection1(v1,v2)); + SGITest(); + TestAllSet(); + return 0; +} \ No newline at end of file diff --git a/sorted_vector.dsp b/sorted_vector.dsp new file mode 100644 index 0000000..fabd10c --- /dev/null +++ b/sorted_vector.dsp @@ -0,0 +1,104 @@ +# Microsoft Developer Studio Project File - Name="sorted_vector" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=sorted_vector - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "sorted_vector.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "sorted_vector.mak" CFG="sorted_vector - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "sorted_vector - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "sorted_vector - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "sorted_vector - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x807 /d "NDEBUG" +# ADD RSC /l 0x807 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "sorted_vector - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x807 /d "_DEBUG" +# ADD RSC /l 0x807 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "sorted_vector - Win32 Release" +# Name "sorted_vector - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\main.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\sorted_vector.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/sorted_vector.h b/sorted_vector.h new file mode 100644 index 0000000..b02c0dc --- /dev/null +++ b/sorted_vector.h @@ -0,0 +1,301 @@ +/* STL-conforming "sorted vector" container + * + * (C) 2002 Martin Holzherr (holzherr@infobrain.com). All rights reserved. + * + * Permission is granted to use, distribute and modify this code provided that: + * · this copyright notice appears, + * · + * The author welcomes any suggestions on the code or reportings of actual + * use of the code. Please send your comments to holzherr@infobrain.com. + * + * The author makes NO WARRANTY or representation, either express or implied, + * with respect to this code, its quality, accuracy, merchantability, or + * fitness for a particular purpose. This software is provided "AS IS", and + * you, its user, assume the entire risk as to its quality and accuracy. + * + * Created: November 19th, 2002 + * Last modified: November 27th, 2002 + (changed namespace from std to codeproject; + uses template member functions for MSCVER>=1300) + + */ + +#ifndef SORTED_VECTOR_ +#define SORTED_VECTOR_ +#define VERSION_SORTED_VECTOR_ 0x00010010 + + +#include +#include +#include +#include + +#pragma pack(push,8) +#pragma warning(push,3) + + +namespace codeproject{ + // TEMPLATE CLASS sorted_vector + + template, class A = std::allocator > + class sorted_vector { +public: + typedef sorted_vector Myt_; + typedef std::vector Cont; + typedef Cont::allocator_type allocator_type; + typedef Cont::size_type size_type; + typedef Cont::difference_type difference_type; + typedef Cont::reference reference; + typedef Cont::const_reference const_reference; + typedef Cont::value_type value_type; + typedef K key_type; + typedef Cont::iterator iterator; + typedef Cont::const_iterator const_iterator; + typedef Pr key_compare; + typedef Pr value_compare; + + typedef Cont::const_reverse_iterator + const_reverse_iterator; + typedef Cont::reverse_iterator reverse_iterator; + + typedef std::pair Pairii_; + typedef std::pair Paircc_; + typedef std::pair Pairib_; + explicit sorted_vector(const Pr& pred = Pr(),const A& al = A()) + :key_compare_(pred),vec_(al){} +#if (_MSC_VER >= 1300) + template + sorted_vector(It first, It beyond, + const Pr& pred = Pr(),const A& al = A()) + :key_compare_(pred),vec_(first,beyond,al) + {stable_sort();} +#else + sorted_vector(const_iterator first, const_iterator beyond, + const Pr& pred = Pr(),const A& al = A()) + :key_compare_(pred),vec_(first,beyond,al) + {stable_sort();} +#endif + sorted_vector(const Myt_& x) + : vec_(x.vec_),key_compare_(x.key_compare_) + {} + ~sorted_vector() {} + Myt_& operator=(const Myt_& x) {vec_.operator=(x.vec_); + key_compare_= x.key_compare_; + return *this;} + Myt_& operator=(const Cont& x){vec_.operator=(x); + sort();return *this;} + + void reserve(size_type n) {vec_.reserve(n);} + iterator begin() {return vec_.begin(); } + const_iterator begin() const {return vec_.begin(); } + iterator end() {return vec_.end();} + const_iterator end() const {return vec_.end();} + reverse_iterator rbegin() {return vec_.rbegin();} + const_reverse_iterator rbegin() const + {return vec_.rbegin();} + + reverse_iterator rend() {return vec_.rend();} + const_reverse_iterator rend() const + {return vec_.rend();} + + + size_type size() const {return vec_.size();} + size_type max_size() const {return vec_.max_size();} + bool empty() const {return vec_.empty();} + A get_allocator() const {return vec_.get_allocator();} + const_reference at(size_type p) const {return vec_.at(p);} + reference at(size_type p) {return vec_.at(p);} + const_reference operator[](size_type p) const + {return vec_.operator[](p);} + + reference operator[](size_type p) {return vec_.operator[](p);} + reference front() {return vec_.front();} + const_reference front() const {return vec_.front();} + reference back() {return vec_.back();} + const_reference back() const {return vec_.back();} + void pop_back() {vec_.pop_back();} + + void assign(const_iterator first, const_iterator beyond) + {vec_.assign(first,beyond);} + void assign(size_type n, const K& x = K()) + {vec_.assign(n,x);} +/*insert members*/ + Pairib_ insert(const value_type& x) + { + if(bNoDuplicates){ + iterator p= lower_bound(x); + if(p==end()||key_compare_(x,*p)){ + return Pairib_(InsertImpl_(p,x),true); + }else{ + return Pairib_(p,false); + } + }else{ + iterator p= upper_bound(x); + return Pairib_(InsertImpl_(p,x),true); + } + } + iterator insert(iterator it, const value_type& x)//it is the hint + { + if(it!=end() ){ + if(bNoDuplicates){ + if(key_compare_(*it,x)){ + if((it+1)==end()||KeyCompare_Gt_(*(it+1),x)){//use hint + return InsertImpl_(it+1,x); + }else if(KeyCompare_Geq_(*(it+1),x)){ + return end(); + } + } + }else{ + if( KeyCompare_Leq_(*it,x) + &&((it+1)==end()||KeyCompare_Geq_(*(it+1),x))){ + return InsertImpl_(it+1,x); + } + } + } + return insert(x).first; + } +#if (_MSC_VER >= 1300) + template + void insert(It first, It beyond) + { + size_type n= std::distance(first,beyond); + reserve(size()+n); + for( ;first!=beyond;++first){ + insert(*first); + } + } +#else + void insert(const_iterator first, const_iterator beyond) + { + size_type n= std::distance(first,beyond); + reserve(size()+n); + for( ;first!=beyond;++first){ + insert(*first); + } + } +#endif + iterator erase(iterator p) {return vec_.erase(p);} + iterator erase(iterator first, iterator beyond) + {return vec_.erase(first,beyond);} + size_type erase(const K& key) + { + Pairii_ begEnd= equal_range(key); + size_type n= std::distance(begEnd.first,begEnd.second); + erase(begEnd.first,begEnd.second); + return n; + } + void clear() {return vec_.clear();} + + bool Eq_(const Myt_& x) const + {return (size() == x.size() + && std::equal(begin(), end(), x.begin())); } + bool Lt_(const Myt_& x) const + {return (std::lexicographical_compare(begin(), end(), + x.begin(), x.end()));} + void swap(Myt_& x) + {vec_.swap(x.vec_);std::swap(key_compare_,x.key_compare_);} + + friend void swap(Myt_& x, Myt_& Y_) + {x.swap(Y_); } + + key_compare key_comp() const {return key_compare_; } + value_compare value_comp() const {return (key_comp()); } + iterator find(const K& k) + { iterator p = lower_bound(k); + return (p==end()||key_compare_(k, *p))? end():p; + } + const_iterator find(const K& k) const + {const_iterator p = lower_bound(k); + return (p==end()||key_compare_(k,*p))?end():p;} + size_type count(const K& k) const + {Paircc_ Ans_ = equal_range(k); + size_type n = std::distance(Ans_.first, Ans_.second); + return (n); } + iterator lower_bound(const K& k) + {return std::lower_bound(begin(), end(), k, key_compare_); } + const_iterator lower_bound(const K& k) const + {return std::lower_bound(begin(), end(), k, key_compare_); } + iterator upper_bound(const K& k) + {return std::upper_bound(begin(), end(), k, key_compare_); } + const_iterator upper_bound(const K& k) const + {return std::upper_bound(begin(), end(), k, key_compare_); } + Pairii_ equal_range(const K& k) + {return std::equal_range(begin(), end(), k, key_compare_); } + Paircc_ equal_range(const K& k) const + {return std::equal_range(begin(), end(), k, key_compare_); } + +/*functions for use with direct std::vector-access*/ + Cont& get_container() + {return vec_;} + void sort()//restore sorted order after low level access + { std::sort(vec_.begin(),vec_.end(),key_compare_); + if( bNoDuplicates ){ + vec_.erase(Unique_(),vec_.end()); + } + } + void stable_sort()//restore sorted order after low level access + { std::stable_sort(vec_.begin(),vec_.end(),key_compare_); + if( bNoDuplicates ){ + erase(Unique_(),end()); + } + } +protected: + iterator Unique_() + { iterator front_= vec_.begin(),out_= vec_.end(),end_=vec_.end(); + bool bCopy_= false; + for(iterator prev_; (prev_=front_)!=end_ && ++front_!=end_; ){ + if( key_compare_(*prev_,*front_)){ + if(bCopy_){ + *out_= *front_; + out_++; + } + }else{ + if(!bCopy_){out_=front_;bCopy_=true;} + } + } + return out_; + } + iterator InsertImpl_(iterator p,const value_type& x) + {return vec_.insert(p,x);} + bool KeyCompare_Leq_(const K& ty0,const K& ty1) + {return !key_compare_(ty1,ty0);} + bool KeyCompare_Geq_(const K& ty0,const K& ty1) + {return !key_compare_(ty0,ty1);} + bool KeyCompare_Gt_(const K& ty0,const K& ty1) + {return key_compare_(ty1,ty0);} + + key_compare key_compare_; + Cont vec_; +}; + + +template inline + bool operator==(const sorted_vector& x, + const sorted_vector& Y_) + {return x.Eq_(Y_); } +template inline + bool operator!=(const sorted_vector& x, + const sorted_vector& Y_) + {return !(x == Y_); } +template inline + bool operator<(const sorted_vector& x, + const sorted_vector& Y_) + {return x.Lt_(Y_);} +template inline + bool operator>(const sorted_vector& x, + const sorted_vector& Y_) + {return Y_ < x; } +template inline + bool operator<=(const sorted_vector& x, + const sorted_vector& Y_) + {return !(Y_ < x); } +template inline + bool operator>=(const sorted_vector& x, + const sorted_vector& Y_) + {return (!(x < Y_)); } +} +#pragma warning(pop) +#pragma pack(pop) +#elif VERSION_SORTED_VECTOR_ != 0x00010010 +#error You have included two sorted_vector.h with different version numbers +#endif diff --git a/sorted_vector_doc.html b/sorted_vector_doc.html new file mode 100644 index 0000000..a0d2302 --- /dev/null +++ b/sorted_vector_doc.html @@ -0,0 +1,875 @@ + + + + + + An STL compliant sorted vector - CodeProject + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Click here to Skip to main content + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + Click here to Skip to main content + + + + + +
+ +
+ +
+ + +
+ +
+ +
+ + +
+ +
+ +
+
+ + + + + + +
+ + + + + +
+
+ +
+ + + + + +
+ + + + +
+ +

An STL compliant sorted vector

+
+ +
+ +
+ +
+ + , + + 28 Nov 2002 + + +
+
+ + + + + + +
+ +
+ +
+ + +
A template container which implements set/multiset functionality using a vector
+ +
+ + + + + + + + + + + + + + + + + + +
+ + + + + + + +

Introduction

+ +

sorted_vector adapts a std::vector to the interface required by +std::set/std::multiset, thereby providing set/multiset and vector +functionality (random access) in one container.

+ +

Tests show that sorted_vector's element retrieval (find) speed outperforms +that of std::set/std::multiset by a factor of 2 (on most machines). On the downward side is +the poor performance of sorted_vector's insert member function, because it +inserts an element somewhere in the middle of the sorted vector in order +to preserve the sort order. By using sorted_vector's low level interface one can +solve this performance bottleneck by inserting a bunch of objects using a series of +push_back's followed by a call to the member function sort, which restores the sort +order.

+ +

sorted_vector should be preferred over std::set/std::multiset +if many small elements +must be stored. Most STL implementations use a variant of a balanced tree to +implement set and multiset, which imposes a per element storage overhead of 3 pointers. +The most important reason to use a std::set/std::multiset instead of +sorted_vector is +the need to keep iterators into a set/multiset while inserting more elements into +the set. These iterators remain valid in the case of a set/multiset, but are invalidated +in the case of a sorted_vector container.

+ +

Namespace

+

sorted_vector resides in the namespace codeproject.

+ +

Basic Usage

+ +

The following small table shows corresponding declarations of sorted_vector's +and set's/multiset's.

+ + + + + + + + + + + + + + + +
STL conceptstd librarysorted_vector
setset<Key,Pred>sorted_vector<Key,true,Pred>
multisetmutliset<Key,Pred>sorted_vector<Key,Pred>
+ +

(sorted_vector<Key,Pred,true> means sorted_vector<Key,Pred,bNoDuplicates=true>)

+ +

The various set/multiset insert and erase member functions as well as the +set/multiset functions count, lower_bound,upper_bound, equal_range are part of +sorted_vector's interface and behave the same as there set/multiset counterparts. +The following code snippet shows the use of a sorted_vector to hold the +set intersection of the content of two std::vector's.

+ +
#pragma warning(disable:4786)
+#include "sorted_vector.h"
+size_t             //build intersection using set interface of sorted_vector
+BuildIntersection(  const std::vector<int>& v0,const std::vector<int>& v1,
+                    codeproject::sorted_vector<int,true>& svecIntersection)
+{		
+    codeproject::sorted_vector<int,true> svec(v0.begin(),v0.end());
+    for(int i=0;i<v1.size();i++){
+        if( svec.find(v1[i])!=svec.end() ){
+            svecIntersection.insert(v1[i]);
+        }
+    }
+    return svecIntersection.size();
+}
+
+ +

The code example shows the use of the member functions find and insert. +If you replace codeproject::sorted_vector<int,true> by std::set<int> you get exactly +the same result. This piece of code can be optimized for speed by replacing the insert calls in the loop +by calls to push_back of the base container (a vector) followed by a call +to the member function sort at the end of the loop.

+ +
#pragma warning(disable:4786)
+#include "sorted_vector.h"
+size_t             //same as previous example, optimized insertions 
+BuildIntersection1(  const std::vector<int>& v0,const std::vector<int>& v1,
+                    codeproject::sorted_vector<int,true>& svecIntersection)
+{		
+    codeproject::sorted_vector<int,true> svec(v0.begin(),v0.end());
+    codeproject::sorted_vector<int,true>::Cont& vInterSect 
+        = svecIntersection.get_container();
+    for(int i=0;i<v1.size();i++){
+        if( svec.find(v1[i])!=svec.end() ){
+            vInterSect.push_back(v1[i]);
+        }
+    }
+    svecIntersection.sort();
+    return svecIntersection.size();
+}
+
+ +

Interface of sorted_vector

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Member + +Coming from + +Description +
Contsorted_vectorcontainer type, the type of the container used to store the controlled sequence.
value_typevectorThe type of object, T, stored in the set.
key_typeset/multisetThe key type associated with value_type.
key_compareset/multisetFunction object that compares two keys for ordering.
value_compareset/multisetFunction object that compares two values for ordering.
pointervectorPointer to T.
referencevectorReference to T
const_referencevectorConst reference to T
size_typevectorAn unsigned integral type.
difference_typevectorA signed integral type.
iteratorvectorRandom access iterator used to iterate through a vector.
const_iteratorvectorRandom access const iterator used to iterate through a vector
reverse_iteratorvectorRandom access iterator used to iterate backwards through a vector. +
const_reverse_iteratorvectorRandom access const iterator used to iterate backwards through a vector.
iterator begin() constvectorReturns an iterator pointing to the beginning of the sorted_vector.
iterator end() constvectorReturns an iterator pointing to the end of the sorted_vector.
reverse_iterator rbegin() constvectorReturns a reverse_iterator pointing to the beginning of the reversed sorted_vector
reverse_iterator rend() constvectorReturns a reverse_iterator pointing to the end of the reversed sorted_vector.
size_type size() constvectorReturns the size of the sorted_vector.
size_type max_size() constvectorReturns the largest possible size of the sorted_vector.
bool empty() constvectortrue if the sorted_vec's size is 0.
key_compare key_comp() constset/multisetReturns the key_compare object used by the sorted_vector.
value_compare value_comp() constset/multisetReturns the value_compare object used by the sorted_vector.
sorted_vector()set/multisetCreates an empty sorted_vector.
sorted_vector(const key_compare& comp)set/multisetCreates an empty sorted_vector, using comp as the key_compare object.
VC++7.0:
template <class InputIterator>
+sorted_vector(InputIterator f,
+ InputIterator l)
+
VC++6.0:
sorted_vector(const_iterator f, const_iterator l) +
set/multisetCreates a sorted_vector with a copy of a range.
VC++7.0:
template <class InputIterator>
+sorted_vector(InputIterator f,
+InputIterator l,const key_compare& comp)
+
VC++6.0:
sorted_vector(const_iterator f, const_iterator l,const key_compare& comp) +
set/multisetCreates a sorted_vector with a copy of a range, using comp as the key_compare object.
sorted_vector(const sorted_vector&)set/multisetThe copy constructor.
sorted_vector& operator=(const sorted_vector&)set/multisetThe assignment operator (assigns other sorted_vector )
sorted_vector& operator=(const Cont&)sorted_vectorThe assignment operator (assigns other vector<T> )
void swap(sorted_vector&)set/multisetSwaps the contents of two sorted_vector's
pair<iterator, bool>insert(const value_type& x)set/multisetInserts x into the sorted_vector.
iterator insert(iterator pos, const value_type& x)set/multisetInserts x into the sorted_vector, using pos as a hint to where it will be + inserted. +
VC++7.0:
template <class InputIterator>
+void insert(InputIterator f, InputIterator l)
+
VC++6.0:
void insert(const_iterator first f, const_iterator l) +
set/multisetInserts a range into the sorted_vector.
void erase(iterator pos)vectorErases the element pointed to by pos.
size_type erase(const key_type& k)set/multisetErases the element whose key is k.
void erase(iterator first, iterator last)vectorErases all elements in a range.
void clear()vectorErases all of the elements.
iterator
+ find(const key_type& k) const
set/multisetFinds an element whose key is k.
size_type
+ count(const key_type& k) const
set/multisetCounts the number of elements whose key is k.
iterator
+ lower_bound(const key_type& k) const
set/multisetFinds the first element whose key is not less than k.
iterator
+ upper_bound(const key_type& k) const
set/multisetFinds the first element whose key is greater than k.
pair<iterator, iterator>
+equal_range(const key_type& k) const
set/multisetFinds a range containing all elements whose key is k.
bool operator==(const sorted_vector&,const sorted_vector&)vectorTests two sorted_vector for equality. This is a global function, not a member function. +There is also an operator !=
bool operator<(const sorted_vector&, const sorted_vector&)vectorLexicographical comparison. This is a global function, not a member function. + There are also operators <= >= and >
Cont& get_container()sorted_vectorReturns a reference to the internal vector used to store the controlled sequence
void sort()sorted_vectorRestores the sort order using key_compare
reference at(size_type i)sorted_vectorReturns a reference to the element at *(begin()+i) with range check
const_reference at(size_type i) constsorted_vectorReturns a reference to the element at *(begin()+i) with range check
const_reference operator[](size_type i) constsorted_vectorReturns a reference to the element at *(begin()+i)
reference operator[](size_type i)sorted_vectorReturns a reference to the element at *(begin()+i)
const_reference front() constsorted_vectorReturns a reference to the first element
reference front()sorted_vectorReturns a reference to the first element
reference back()sorted_vectorReturns a reference to the last element
const_reference back() constsorted_vectorReturns a reference to the last element
void pop_back() sorted_vectorRemoves the last element from the sorted_vector
+ + + +

Points of Interest

+ +

An interview with Alexander Stepanov (inventor of the STL) at +http://www.stlport.org/resources/StepanovUSA.html +, in which he proposed to implement +a sorted container as an adapter of a std::container gave me the idea for this work. +The real surprise to me was the unexpected good performance of sorted_vector compared +to set/multiset. The outcome of my tests indicate, that the std::set/std::multiset +has only one (important) advantage over a sorted vector, namely that iterators remain +valid in a set/multiset, even when more elements are added. +

+ +

The implementation work itself was easy and did not require any advanced C++/STL feature. +It can be summarized as follows: +Most of the member functions of sorted_vector forward the call to the vec_ data member, +which is a std::vector. The set/multiset specific functions for inserting/erasing and +locating elements mostly use STL algorithms to deal with the sorted sequence present +in the vec_ data member. The low level interface consisting of the member functions + get_container() (which returns a reference to the vec_ data member) +and sort and stable_sort + (which restore the sort order) are necessary to improve insertion performance (by allowing + a temporary violation of the sorting order). +Only one function was unexpectedly difficult to implement: The function unique, which +must be called after sort and stable_sort in the case of a set. +This function is part of the STL and requires a predicate as third argument. This predicate must return true, +if the passed elements are equal. But the class sorted_vector only has access to a predicate +which evaluates the < relation. In theory, it should be possible, to transform a predicate +evaluating the < relation into another predicate evaluating ==, but in practice this is only +possible, when the < predicate is implemented as object derived from std::binary_function. +Ultimately, I had to implement unique myself because of that.

+ +

History

+
  • 1.0: November 19th, 2002; Initial release.
  • +
  • 1.1: November 28th, 2002; Documentation and code Update +
    • changed namespace from std to codeproject
    • +
    • supports member templates for constructing/inserting from iterator range in case of VC++7.0
    • +
    +
  • +
+ + +
+ + +
+ +
+ + + +

License

+

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

+ + + +

Share

+
+ +
+ + + + +

About the Author

+ + +
+
+ +
+
+ Martin Holzherr +
+ + +
Switzerland Switzerland +
+
+ +
+ No Biography provided + + + + +
+

+ + + +
+ +
+ +
+ + +
+ +
+ + + + +
+ +
+ + + + +

Comments and Discussions

+ +

Comment + 20 messages have been posted for this article + Visit http://www.codeproject.com/Articles/3217/An-STL-compliant-sorted-vector to post and view comments on + this article, or click here + to get a print view with messages.

+ + + +
+ +
+ +
+ + +
+ +
+
+
+ | + Advertise | + Privacy | + Mobile +
+ + + Web02 | + 2.8.140814.1 | + Last Updated 29 Nov 2002 +
+
+ Article Copyright 2002 by Martin Holzherr
Everything else + Copyright © CodeProject, 1999-2014
+ Terms of Service +
+ + + +
+
+ + +
+ + + +
+
+
+ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sorted_vector_src.zip b/sorted_vector_src.zip new file mode 100644 index 0000000..3354137 Binary files /dev/null and b/sorted_vector_src.zip differ