C ++高效创建单一条目不同的地图

时间:2015-08-14 10:40:54

标签: c++ dictionary

给定具有n条目的地图的主副本,复制起来很昂贵。我需要一次更改一个地图中的条目值,并在新地图上执行一些操作。我想知道是否有一种有效的方法来实现它,而无需复制主地图n次。

在下面的代码中,XYZ是一个复制成本很高的类,我需要执行的操作不是必需的总和。我选择总结是为了说明目的。

class BigMap
{
private:
  std::map<int, XYZ> m;
public
  std::map<int, XYZ>::const_iterator begin() const
  {
    return m.begin();
  }
  std::map<int, XYZ>::iterator begin()
  {
    return m.begin();
  }
  std::map<int, XYZ>::const_iterator end() const
  {
    return m.end();
  }
  std::map<int, XYZ>::iterator end()
  {
    return m.end();
  }
  void PopulateBigMapWithLotsOfData(int size)
  {
    for (int i = 0; i < size; ++i)
    {
      m[i] = XYZ();
    }
  }
};

double Aggregate(const BigMap& bm)
{
  double result = 0.0;
  for (const auto& entry : bm)
  {
    result += entry.second.Value();
  }
}

int main()
{
  BigMap bm;
  bm.PopulateBigMapWithLotsOfData(1000);

  double result = 0.0;
  // This for loop will be in a multi-threaded environment.
  // Thus I do need a shallow copy of bm in each thread.
  for (int i = 0; i < 1000; ++i)
  {
    BigMap newBm(bm);  // An expensive copy

    newBm[i].ChangeValue();  // Modify single entry

    result += Aggregate(newBm);  // Perform some operation on the new map
  }

  return result;
}

我正在考虑类似下面这样的内容,但我被困在迭代器部分,因为Aggregate函数需要能够使用begin和{{循环遍历地图的所有条目1}}迭代器被定义。

end

基本上,原始地图中具有特定键的条目永远不会被访问,因为它被class BigMap { public: XYZ& Find(int index) { return ptr->Find(index); } const XYZ& Find(int index) const { return ptr->Find(index); } private: AbstractBigMap* ptr; // Pointer so that I can apply virtual function. }; class AbstractBigMap { public: virtual XYZ& Find(int index); virtual const XYZ& Find(int index) const; } class OriginalBigMap : public AbstractBigMap { public: XYZ& Find(int index) { return m.find(index)->second; } const XYZ& Find(int index) const { return m.find(index)->second; } private: std::map<int, XYZ> m; }; class ModifiedBigMap : public AbstractBigMap { public: // Reference on the original map to avoid copying. ModifiedBigMap(const OriginalBigMap& original, int index) : _original(original), _index(index) { // Copy single entry from the original map so we can modify it _m[index] = _original.m.find(index)->second; } XYZ& Find(int index) { assert(index == _index); return _m[index].second; } const XYZ& Find(int index) const { if (index == _index) return _m[index].second; else return _original.Find(index); } private: const OriginalBigMap& _original; std::map<int, XYZ> _m; int _index; }; 中的单个条目替换。

编辑: 我应该指出for循环实际上是在一个多线程环境中,因此我不能简单地复制该条目并在操作后替换它。

1 个答案:

答案 0 :(得分:1)

您可以在调用函数时替换地图中某个位置的值:

#include <iostream>
#include <algorithm>

template <typename Map>
struct Invoke
{
    using value_type = typename Map::mapped_type;
    using const_iterator = typename Map::const_iterator;

    const_iterator position;
    value_type replace_value;

    Invoke(const_iterator position, value_type replace_value)
    :   position(position), replace_value(replace_value)
    {}

    template <typename Function>
    value_type operator () (const Map& map, const Function& function) const
    {
        value_type result = value_type();
        for(auto pos = map.begin(); pos != map.end(); ++pos) {
            if(pos == position) result = function(result, replace_value);
            else result = function(result, pos->second);
        }
        return result;
    }
};

int main(int argc, const char *argv[])
{
    using map_type = std::map<int, int>;
    map_type map = { {0, 0}, {1, 1}, {2, 2} };
    for(auto pos = map.begin(); pos != map.end(); ++pos) {
        Invoke<map_type> invoke(pos, -1);
        std::cout << invoke(map, std::plus<int>()) << '\n';
    }
}

或者,您可以使用自定义迭代器:

#include <iostream>
#include <algorithm>

template <typename Iterator>
struct ReplaceIterator
{
    public:
    using iterator = Iterator;
    using iterator_category = std::forward_iterator_tag ;
    using difference_type = typename iterator::difference_type;
    using value_type = typename iterator::value_type;
    using reference = typename iterator::reference;
    using pointer = typename iterator::pointer;


    // Construction
    // ============

    public:
    explicit ReplaceIterator(iterator position, iterator replace_position, value_type replace_value)
    :   m_position(position), m_replace_position(replace_position), m_replace_value(replace_value)
    {}


    // Element Access
    // ==============

    public:
    const iterator& position() const { return m_position; }
    reference& value() const {
        return (m_position == m_replace_position)
            ? m_replace_value
            : *m_position;
    }

    // Iterator
    // ========

    public:
    reference operator * () const { return value(); }
    pointer operator -> () const { return &value(); }

    ReplaceIterator& operator ++ () {
        ++m_position;
        return *this;
    }
    ReplaceIterator operator ++ (int) {
        ReplaceIterator tmp(*this);
        ++m_position;
        return tmp;

    }

    // Compare
    // =======

    public:
    friend bool operator == (const ReplaceIterator& a, const ReplaceIterator& b) {
        return a.m_position == b.m_position;
    }

    friend bool operator == (const iterator& a, const ReplaceIterator& b) {
        return a == b.m_position;
    }

    friend bool operator == (const ReplaceIterator& a, const iterator& b) {
        return a.m_position == b;
    }

    friend bool operator != (const ReplaceIterator& a, const ReplaceIterator& b) {
        return a.m_position != b.m_position;
    }

    friend bool operator != (const iterator& a, const ReplaceIterator& b) {
        return a != b.m_position;
    }

    friend bool operator != (const ReplaceIterator& a, const iterator& b) {
        return a.m_position != b;
    }

    private:
    iterator m_position;
    iterator m_replace_position;
    mutable value_type m_replace_value;
};



int main(int argc, const char *argv[])
{
    using map_type = std::map<int, int>;
    map_type map = { {0, 0}, {1, 1}, {2, 2} };
    for(auto pos0 = map.begin(); pos0 != map.end(); ++pos0) {
        int result = 0;
        ReplaceIterator<map_type::const_iterator> pos1(map.begin(), pos0, {0, -1});
        for( ; pos1 != map.end(); ++pos1)
            result += pos1->second;
        std::cout << result << '\n';
    }
}

两者都会打印:

2
1
0