it-swarm.com.de

Verketten von zwei std :: Vektoren

Wie verkette ich zwei std::vectors?

544
yigal
vector1.insert( vector1.end(), vector2.begin(), vector2.end() );
608
Robert Gamble

Wenn Sie C++ 11 verwenden und die Elemente verschieben möchten, anstatt sie nur zu kopieren, können Sie std::move_iterator zusammen mit dem Einfügen (oder Kopieren) verwenden:

#include <vector>
#include <iostream>
#include <iterator>

int main(int argc, char** argv) {
  std::vector<int> dest{1,2,3,4,5};
  std::vector<int> src{6,7,8,9,10};

  // Move elements from src to dest.
  // src is left in undefined but safe-to-destruct state.
  dest.insert(
      dest.end(),
      std::make_move_iterator(src.begin()),
      std::make_move_iterator(src.end())
    );

  // Print out concatenated vector.
  std::copy(
      dest.begin(),
      dest.end(),
      std::ostream_iterator<int>(std::cout, "\n")
    );

  return 0;
}

Dies ist für das Beispiel mit ints nicht effizienter, da das Verschieben nicht effizienter ist als das Kopieren. Bei einer Datenstruktur mit optimierten Verschiebungen kann das Kopieren unnötiger Zustände vermieden werden:

#include <vector>
#include <iostream>
#include <iterator>

int main(int argc, char** argv) {
  std::vector<std::vector<int>> dest{{1,2,3,4,5}, {3,4}};
  std::vector<std::vector<int>> src{{6,7,8,9,10}};

  // Move elements from src to dest.
  // src is left in undefined but safe-to-destruct state.
  dest.insert(
      dest.end(),
      std::make_move_iterator(src.begin()),
      std::make_move_iterator(src.end())
    );

  return 0;
}

Nach der Verschiebung verbleibt das Element von src in einem undefinierten, aber sicheren Destruktionszustand, und seine früheren Elemente wurden am Ende direkt in das neue Element von dest übertragen.

139
Alex

Ich würde die insert Funktion verwenden , etwas wie:

vector<int> a, b;
//fill with data
b.insert(b.end(), a.begin(), a.end());
128
Tom Ritter

Oder du könntest verwenden:

std::copy(source.begin(), source.end(), std::back_inserter(destination));

Dieses Muster ist nützlich, wenn die beiden Vektoren nicht genau dieselbe Art von Dingen enthalten, da Sie etwas anstelle von std :: back_inserter verwenden können, um von einem Typ zum anderen zu konvertieren.

74
Roger Lipscombe

Bei C++ 11 würde ich Folgendes vorziehen, um Vektor b an a anzuhängen:

std::move(b.begin(), b.end(), std::back_inserter(a));

wenn a und b nicht überlappen und b nicht mehr verwendet wird.


Dies ist std::move von <algorithm>, nicht das gewöhnlichestd::move von <utility>.

42
Deqing
std::vector<int> first;
std::vector<int> second;

first.insert(first.end(), second.begin(), second.end());
31
James Curran

Ich bevorzuge eine, die bereits erwähnt wurde: 

a.insert(a.end(), b.begin(), b.end());

Wenn Sie jedoch C++ 11 verwenden, gibt es einen generischen Weg:

a.insert(std::end(a), std::begin(b), std::end(b));

Auch nicht Teil einer Frage, aber es ist ratsam, vor dem Anhängen reserve zu verwenden, um eine bessere Leistung zu erzielen. Und wenn Sie vector mit sich selbst verketten, schlägt die Reservierung fehl, und Sie sollten immer reserve verwenden.


Also im Grunde was Sie brauchen:

template <typename T>
void Append(std::vector<T>& a, const std::vector<T>& b)
{
    a.reserve(a.size() + b.size());
    a.insert(a.end(), b.begin(), b.end());
}
22
ST3

Verwenden Sie vector :: insert

v1.insert(v1.end(), v2.begin(), v2.end());
12
Boris

Mit range v3 haben Sie möglicherweise eine lazy -Kettenfolge:

ranges::view::concat(v1, v2)

Demo .

8
Jarod42

Wenn Sie an einer starken Ausnahmegarantie interessiert sind (wenn der Copy-Konstruktor eine Ausnahme auslösen kann):

template<typename T>
inline void append_copy(std::vector<T>& v1, const std::vector<T>& v2)
{
    const auto orig_v1_size = v1.size();
    v1.reserve(orig_v1_size + v2.size());
    try
    {
        v1.insert(v1.end(), v2.begin(), v2.end());
    }
    catch(...)
    {
        v1.erase(v1.begin() + orig_v1_size, v1.end());
        throw;
    }
}

Ein ähnlicher append_move mit starker Garantie kann nicht generell implementiert werden, wenn der Bewegungskonstruktor des Vektorelements auslösen kann (was jedoch unwahrscheinlich ist).

4
AlexT

Ein general performance boost für Verkettung ist die Überprüfung der Größe der Vektoren. Und füge die kleinere mit der größeren zusammen. 

//vector<int> v1,v2;
if(v1.size()>v2.size()){
    v1.insert(v1.end(),v2.begin(),v2.end());
}else{
    v1.insert(v2.end(),v1.begin(),v1.end());
}
3
Vikramjit Roy
vector<int> v1 = {1, 2, 3, 4, 5};
vector<int> v2 = {11, 12, 13, 14, 15};
copy(v2.begin(), v2.end(), back_inserter(v1));
3
instance

Hier eine allgemeine Lösung mit C++ 11-Verschiebesemantik:

template <typename T>
std::vector<T> concat(const std::vector<T>& lhs, const std::vector<T>& rhs)
{
    if (lhs.empty()) return rhs;
    if (rhs.empty()) return lhs;
    std::vector<T> result {};
    result.reserve(lhs.size() + rhs.size());
    result.insert(result.cend(), lhs.cbegin(), lhs.cend());
    result.insert(result.cend(), rhs.cbegin(), rhs.cend());
    return result;
}

template <typename T>
std::vector<T> concat(std::vector<T>&& lhs, const std::vector<T>& rhs)
{
    lhs.insert(lhs.cend(), rhs.cbegin(), rhs.cend());
    return std::move(lhs);
}

template <typename T>
std::vector<T> concat(const std::vector<T>& lhs, std::vector<T>&& rhs)
{
    rhs.insert(rhs.cbegin(), lhs.cbegin(), lhs.cend());
    return std::move(rhs);
}

template <typename T>
std::vector<T> concat(std::vector<T>&& lhs, std::vector<T>&& rhs)
{
    if (lhs.empty()) return std::move(rhs);
    lhs.insert(lhs.cend(), std::make_move_iterator(rhs.begin()), std::make_move_iterator(rhs.end()));
    return std::move(lhs);
}

Beachten Sie, wie sich dies von append ing zu einer vector unterscheidet.

3
Daniel

Fügen Sie diese zu Ihrer Headerdatei hinzu:

template <typename T> vector<T> concat(vector<T> &a, vector<T> &b) {
    vector<T> ret = vector<T>();
    copy(a.begin(), a.end(), back_inserter(ret));
    copy(b.begin(), b.end(), back_inserter(ret));
    return ret;
}

und benutze es so:

vector<int> a = vector<int>();
vector<int> b = vector<int>();

a.Push_back(1);
a.Push_back(2);
b.Push_back(62);

vector<int> r = concat(a, b);

r enthält [1,2,62]

2

Wenn Sie Vektoren präzise verketten möchten, können Sie den Operator += überladen.

template <typename T>
std::vector<T>& operator +=(std::vector<T>& vector1, const std::vector<T>& vector2) {
    vector1.insert(vector1.end(), vector2.begin(), vector2.end());
    return vector1;
}

Dann können Sie es so nennen:

vector1 += vector2;
1
Daniel Giger

Diese Lösung mag etwas kompliziert sein, aber boost-range hat auch einige andere schöne Dinge zu bieten.

#include <iostream>
#include <vector>
#include <boost/range/algorithm/copy.hpp>

int main(int, char**) {
    std::vector<int> a = { 1,2,3 };
    std::vector<int> b = { 4,5,6 };
    boost::copy(b, std::back_inserter(a));
    for (auto& iter : a) {
        std::cout << iter << " ";
    }
    return EXIT_SUCCESS;
}

Oft besteht die Absicht darin, den Vektor a und b zu kombinieren, indem er einfach iteriert und dabei einige Operationen ausführt. In diesem Fall gibt es die lächerliche einfache join-Funktion. 

#include <iostream>
#include <vector>
#include <boost/range/join.hpp>
#include <boost/range/algorithm/copy.hpp>

int main(int, char**) {
    std::vector<int> a = { 1,2,3 };
    std::vector<int> b = { 4,5,6 };
    std::vector<int> c = { 7,8,9 };
    // Just creates an iterator
    for (auto& iter : boost::join(a, boost::join(b, c))) {
        std::cout << iter << " ";
    }
    std::cout << "\n";
    // Can also be used to create a copy
    std::vector<int> d;
    boost::copy(boost::join(a, boost::join(b, c)), std::back_inserter(d));
    for (auto& iter : d) {
        std::cout << iter << " ";
    }
    return EXIT_SUCCESS;
}

Für große Vektoren kann dies von Vorteil sein, da kein Kopieren erfolgt. Es kann auch zum Kopieren von Generalisierungen in mehr als einen Container verwendet werden. 

Aus irgendeinem Grund gibt es nichts wie boost::join(a,b,c), was vernünftig sein könnte.

1
Aleph0

Sie können eine eigene Vorlage für + Operator erstellen:

template <typename T> 
inline T operator+(const T & a, const T & b)
{
    T res = a;
    res.insert(res.end(), b.begin(), b.end());
    return res;
}

Nächstes, benutze einfach +:

vector<int> a{1, 2, 3, 4};
vector<int> b{5, 6, 7, 8};
for (auto x: a + b)
    cout << x << " ";
cout << endl;

Dieses Beispiel liefert eine Ausgabe:

1 2 3 4 5 6 7 8
0
Vladimir U.

Um ehrlich zu sein, können Sie zwei Vektoren schnell miteinander verbinden, indem Sie Elemente aus zwei Vektoren in einen anderen kopieren oder nur einen von zwei Vektoren anhängen! Das hängt von deinem Ziel ab.

Methode 1: Zuweisen eines neuen Vektors mit seiner Größe ist die Summe der Größe der beiden Originalvektoren.

vector<int> concat_vector = vector<int>();
concat_vector.setcapacity(vector_A.size() + vector_B.size());
// Loop for copy elements in two vectors into concat_vector

Methode 2: Anfügen des Vektors A durch Hinzufügen/Einfügen von Elementen des Vektors B. 

// Loop for insert elements of vector_B into vector_A with insert() 
function: vector_A.insert(vector_A .end(), vector_B.cbegin(), vector_B.cend());
0
nvnhcmus

Wenn Sie nach dem Erstellen einen Vektor an einen anderen anhängen möchten, ist vector::insert Ihre beste Wette, wie bereits mehrfach beantwortet wurde, beispielsweise:

vector<int> first = {13};
const vector<int> second = {42};

first.insert(first.end(), second.cbegin(), second.cend());

Leider gibt es keine Möglichkeit, einen const vector<int> zu erstellen, da Sie oben konstruieren müssen und dann insert.


Wenn Sie nach einem Container suchen, um die Verkettung dieser beiden vector<int>s zu speichern, steht Ihnen möglicherweise etwas Besseres zur Verfügung, wenn:

  1. Ihr vector enthält Grundelemente
  2. Ihre enthaltenen Grundelemente haben eine Größe von 32 Bit oder weniger
  3. Sie möchten einen const-Container

Wenn alle oben genannten Bedingungen zutreffen, würde ich vorschlagen, dass der basic_string who's char_type der Größe des in Ihrem vector enthaltenen Grundelements entspricht. Sie sollten einen static_assert in Ihren Code aufnehmen, um zu bestätigen, dass diese Größen konsistent bleiben:

static_assert(sizeof(char32_t) == sizeof(int));

Mit diesem Wert können Sie einfach Folgendes tun:

const u32string concatenation = u32string(first.cbegin(), first.cend()) + u32string(second.cbegin(), second.cend());

Weitere Informationen zu den Unterschieden zwischen string und vector finden Sie hier: https://stackoverflow.com/a/35558008/2642059

Ein Live-Beispiel für diesen Code finden Sie hier: http://ideone.com/7Iww3I

0
Jonathan Mee

Ich habe diese Funktion implementiert, die eine beliebige Anzahl von Containern verkettet, sich von rvalue-Referenzen entfernt und auf andere Weise kopiert

namespace internal {

// Implementation detail of Concatenate, appends to a pre-reserved vector, copying or moving if
// appropriate
template<typename Target, typename Head, typename... Tail>
void AppendNoReserve(Target* target, Head&& head, Tail&&... tail) {
    // Currently, require each homogenous inputs. If there is demand, we could probably implement a
    // version that outputs a vector whose value_type is the common_type of all the containers
    // passed to it, and call it ConvertingConcatenate.
    static_assert(
            std::is_same_v<
                    typename std::decay_t<Target>::value_type,
                    typename std::decay_t<Head>::value_type>,
            "Concatenate requires each container passed to it to have the same value_type");
    if constexpr (std::is_lvalue_reference_v<Head>) {
        std::copy(head.begin(), head.end(), std::back_inserter(*target));
    } else {
        std::move(head.begin(), head.end(), std::back_inserter(*target));
    }
    if constexpr (sizeof...(Tail) > 0) {
        AppendNoReserve(target, std::forward<Tail>(tail)...);
    }
}

template<typename Head, typename... Tail>
size_t TotalSize(const Head& head, const Tail&... tail) {
    if constexpr (sizeof...(Tail) > 0) {
        return head.size() + TotalSize(tail...);
    } else {
        return head.size();
    }
}

}  // namespace internal

/// Concatenate the provided containers into a single vector. Moves from rvalue references, copies
/// otherwise.
template<typename Head, typename... Tail>
auto Concatenate(Head&& head, Tail&&... tail) {
    size_t totalSize = internal::TotalSize(head, tail...);
    std::vector<typename std::decay_t<Head>::value_type> result;
    result.reserve(totalSize);
    internal::AppendNoReserve(&result, std::forward<Head>(head), std::forward<Tail>(tail)...);
    return result;
}
0
Drew

Es gibt einen Algorithmus std::merge aus C++ 17 , der sehr einfach zu bedienen ist.

Unten ist das Beispiel:

_#include <iostream>
#include <vector>
#include <algorithm>

int main()
{
    //DATA
    std::vector<int> v1{2,4,6,8};
    std::vector<int> v2{12,14,16,18};

    //MERGE
    std::vector<int> dst;
    std::merge(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(dst));

    //PRINT
    for(auto item:dst)
        std::cout<<item<<" ";

    return 0;
}
_
0
Pavan Chandaka