簡體   English   中英

將std :: vector轉換為另一個std :: vector的最快方法

[英]fastest way to convert a std::vector to another std::vector

將std :: vector從一種數據類型轉換為另一種數據類型(以節省空間的想法)的最快方法(如果有的話)是什么? 例如:

std::vector<unsigned short> ----> std::vector<bool> 

我們顯然假設第一個向量只包含0和1。 在矢量非常大的情況下,逐個元素復制是非常低效的。

條件問題:如果您認為沒有辦法更快地完成它,是否有一個復雜的數據類型實際上允許從一種數據類型快速轉換到另一種數據類型?

std::vector<bool> 

停止。

std::vector<bool>是......不是。 std::vector具有使用bool類型的特化,這會導致vector某些更改。 也就是說,它停止表現得像std::vector

標准保證您可以使用std::vector進行某些操作。 vector<bool>違反了這些保證。 所以你應該非常小心使用它們。

無論如何,我會假裝你說vector<int>而不是vector<bool> ,因為后者確實讓事情變得復雜。

在矢量非常大的情況下,逐個元素復制是非常低效的。

只有你做錯了。

您需要的矢量鑄造需要仔細完成才能高效。

如果源T類型可以轉換為目標T ,那么這可以正常工作:

vector<Tnew> vec_new(vec_old.begin(), vec_old.end());

體面的實現應該識別它們何時被賦予隨機訪問迭代器並優化內存分配並適當地循環。

對於簡單類型,不可轉換類型的最大問題不是這樣做:

std::vector<int> newVec(oldVec.size());

那很糟。 這將分配一個適當大小的緩沖區,但它將填充數據。 即,默認構造的intint() )。

相反,你應該這樣做:

std::vector<int> newVec;
newVec.reserve(oldVec.size());

這保留了與原始向量相等的容量,但它也確保不會發生默認構造。 你現在可以push_back回到你的內心,知道你永遠不會在你的新載體中重新分配。

從那里,您可以循環遍歷舊向量中的每個條目,根據需要進行轉換。

沒有辦法避免副本,因為std::vector<T>是與std::vector<U>不同的類型,並且它們無法共享內存。 除此之外,它取決於數據的映射方式。 如果映射對應於隱式轉換(例如unsigned short to bool ),那么使用舊的開頭和結束迭代器創建一個新向量就可以了:

std::vector<bool> newV( oldV.begin(), oldV.end() );

如果映射不僅僅是一個隱式轉換(這包括你要驗證的情況;例如, unsigned short只包含01 ),那么它會變得更復雜。 顯而易見的解決方案是使用std :: transform:

std::vector<TargetType> newV;
newV.reserve( oldV.size() );    //  avoids unnecessary reallocations
std::transform( oldV.begin(), oldV.end(),
                std::back_inserter( newV ),
                TranformationObject() );

,其中TranformationObject是一個進行轉換的功能對象,例如:

struct ToBool : public std::unary_function<unsigned short, bool>
{
    bool operator()( unsigned short original ) const
    {
        if ( original != 0 && original != 1 )
            throw Something();
        return original != 0;
    }
};

(請注意,我只是使用此轉換函數作為示例。如果區分轉換函數與隱式轉換的唯一區別是驗證,則首先使用std::for_each驗證oldV所有值可能會更快std::for_each ,然后使用上面的兩個迭代器構造函數。)

根據構造目標類型的默認成本,創建具有正確大小的新向量可能更快,然后覆蓋它:

std::vector<TargetType> newV( oldV.size() );
std::transform( oldV.begin(), oldV.end(),
                newV.begin(),
                TranformationObject() );

最后,另一種可能性是使用boost::transform_iterator 就像是:

std::vector<TargetType> newV(
    boost::make_transform_iterator( oldV.begin(), TranformationObject() ),
    boost::make_transform_iterator( oldV.end(), TranformationObject() ) );

在很多方面,這是我更喜歡的解決方案; 取決於boost::transform_iterator實現方式,它也可能是最快的。

您應該可以像這樣使用assign

vector<unsigned short> v;
//...
vector<bool> u;
//...
u.assign(v.begin(), v.end());
class A{... }
class B{....}
B convert_A_to_B(const A& a){.......}

void convertVector_A_to_B(const vector<A>& va, vector<B>& vb)
{
    vb.clear();
    vb.reserve(va.size());
    std::transform(va.begin(), va.end(), std::back_inserter(vb), convert_A_to_B);
}

最快的方法是要這樣做。 例如,如果您事先知道您的項目只需要一個字節進行存儲,那么請先使用字節大小的向量。 你會發現很難找到比這更快的方式:-)

如果那是不可能的,那么只需要承擔轉換成本。 即使它有點慢(而且這並不確定,請參閱Nicol對細節的出色答案 ),它仍然是必要的。 如果不是,你只需將它留在較大類型的向量中。

首先,警告:不要做我即將建議的事情。 這很危險,絕不可能。 也就是說,如果你只需要擠出更多的性能,那么無關緊要......

首先,有一些警告。 如果你不滿足這些,你不能這樣做:

  1. 向量必須包含普通舊數據。 如果你的類型有指針,或使用析構函數,或需要operator =正確復制...不要這樣做。

  2. sizeof()兩個向量包含的類型必須相同。 也就是說,僅當sizeof(A)== sizeof(B)時,vector <A>才能從vector <B>復制。

這是一個相當穩定的方法:

vector< A > a;
vector< B > b;
a.resize( b.size() );
assert( sizeof(vector< A >::value_type) == sizeof(vector< B >::value_type) );
if( b.size() == 0 )
   a.clear();
else
   memcpy( &(*a.begin()), &(*b.begin()), b.size() * sizeof(B) );

這會對向量b中包含的內存進行非常快速的塊復制,直接粉碎向量a中的任何數據。 它不調用構造函數,它不進行任何安全檢查,並且它比這里給出的任何其他方法快得多。 優化編譯器應該能夠在理論上匹配這個速度,但除非你使用的是非常好的編譯器,否則它不會(幾年前我用Visual C ++檢查過,它甚至沒有關閉)。

另外,考慮到這些約束,你可以強行(通過void *)將一個矢量類型轉換為另一個並交換它們 - 我有一個代碼示例,但它開始在我的屏幕上滲出外質,所以我刪除了它。

#ifdef VECTOR_H_TYPE1
#ifdef VECTOR_H_TYPE2
#ifdef VECTOR_H_CLASS
/* Other methods can be added as needed, provided they likewise carry out the same operations on both */

#include <vector>

using namespace std;

class VECTOR_H_CLASS {
public:
        vector<VECTOR_H_TYPE1> *firstVec;
        vector<VECTOR_H_TYPE2> *secondVec;

        VECTOR_H_CLASS(vector<VECTOR_H_TYPE1> &v1, vector<VECTOR_H_TYPE2> &v2) { firstVec = &v1; secondVec = &v2; }
        ~VECTOR_H_CLASS() {}

        void init() { // Use this to copy a full vector into an empty (or garbage) vector to equalize them
                secondVec->clear();
                for(vector<VECTOR_H_TYPE1>::iterator it = firstVec->begin(); it != firstVec->end(); it++) secondVec->push_back((VECTOR_H_TYPE2)*it);
        }

        void push_back(void *value) {
                firstVec->push_back((VECTOR_H_TYPE1)value);
                secondVec->push_back((VECTOR_H_TYPE2)value);
        }

        void pop_back() {
                firstVec->pop_back();
                secondVec->pop_back();
        }

        void clear() {
                firstVec->clear();
                secondVec->clear();
        }
};
#undef VECTOR_H_CLASS
#endif
#undef VECTOR_H_TYPE2
#endif
#undef VECTOR_H_TYPE1
#endif

逐個元素復制並不是非常低效。 std :: vector為其任何元素提供持續訪問時間,因此整個操作將為O(n)。 你不會注意到它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM