【问题标题】:Map, pair-vector or two vectors...?映射、对向量或两个向量...?
【发布时间】:2014-12-31 10:46:59
【问题描述】:

我阅读了一些帖子和“wiki”,但仍然无法确定哪种方法适合我的问题。

我创建了一个名为Sample 的类,其中包含一定数量的化合物(假设这是另一个类Nuclide),具有一定的相对数量(双倍)。

因此,类似于(伪):

class Sample {
    map<Nuclide, double>;
}

如果我在示例中有核素 Ba-133Co-60Cs-137,我将不得不在代码中使用这些名称来访问地图中的这些核素。但是,我唯一需要做的就是遍历地图以执行计算(它们是哪些核素并不重要),因此,我将使用 for 循环。我想在不关注键名的情况下进行迭代,因此,我需要为地图使用迭代器,对吗?

另一种选择是vector&lt;pair&lt;Nuclide, double&gt; &gt;

class Sample {
    vector<pair<Nuclide, double> >;
}

或者只是两个独立的向量

Class Sample {
    vector<Nuclide>;
    vector<double>;
}

而在最后一个选项中,核素与其数量之间的联系将是“元信息”,仅由相应向量中的位置给出。

由于我缺乏深厚的经验,我会恳请您对选择哪种方法提出建议。我希望通过所有可用的化合物快速轻松地进行迭代,同时保持相应键和值的逻辑结构。

PS.:样本中的化合物数量可能非常少(1 到 5)! PPS.: 是否可以通过某些const 语句修改最后一个选项以防止更改,从而保持正确的顺序?

【问题讨论】:

  • 前两个都不错。我想第一个将为您提供更有用的界面(例如,按键访问)。如果您开始衡量性能问题,我只会开始进一步研究。
  • 使用boost::container::flat_map&lt;&gt; 获得最佳语义和性能。
  • 如果你有固定数量的核素,你可以把它们变成枚举并使用 double[NUCLIDES_COUNT]。使用更多内存,但访问时间更短。
  • 您是否在基于Nuclide 的结构中进行任何查找?此外,根据您的描述,尚不清楚Nuclidedouble 是否都用于计算或仅double。最后,序列中元素的顺序是否重要(除了Nuclidedouble 一起出现)?
  • @DietmarKühl 实际上,它主要是 Nuclides 的属性,用于计算。但是,计算会随着时间的推移而变化,会执行不同的计算 - 不幸的是,目前尚不清楚,因此我需要最大的灵活性。 (所有这些都是模拟的一部分,将随着时间的推移而改进。)我希望Sample 类提供结构良好的信息,然后可以以简单的方式访问这些信息,主要用于 for 循环中的计算......顺序没关系。

标签: c++ vector structure std-pair


【解决方案1】:

如果需要快速迭代,你不想要std::map&lt;...&gt;:它的迭代是一个快速变坏的树状遍历。 std::map&lt;...&gt; 仅在您对序列 有许多突变时才真正合理,并且您需要按键排序的序列。如果你有突变但你不关心订单std::unordered_map&lt;...&gt; 通常是一个更好的选择。不过,这两种地图都假设您正在按键查找内容。根据您的描述,我认为情况并非如此。

std::vector&lt;...&gt; 迭代速度很快。但是,它并不适合查找。如果您保持它的有序性,您可以使用std::lower_bound() 进行类似std::map&lt;...&gt; 的查找(即复杂性也是O(log n)),但是保持它的有序性可能会使该选项过于昂贵。但是,它是一个理想的容器,可以将一堆迭代的对象放在一起。

您想要一个std::vector&lt;std::pair&lt;...&gt;&gt; 还是两个std::vector&lt;...&gt;s 取决于您如何访问元素:如果一个元素的两个部分必须一起访问,您需要一个std::vector&lt;std::pair&lt;...&gt;&gt;,因为它可以保存数据一起访问。另一方面,如果您通常只访问这两个组件中的一个,那么使用两个单独的std::vector&lt;...&gt;s 将使迭代更快,因为更多的迭代元素适合缓存行,特别是如果它们像doubles 这样相当小.

无论如何,我建议不要将外部结构暴露给外界,而是提供一个接口,让您稍后更改底层表示。也就是说,为了获得最大的灵活性,您不想将表示烘焙到所有代码中。例如,如果您使用访问器函数对象(property maps 根据 BGLprojections 根据 Eric Niebler 的 Range Proposal)来访问基于迭代器的元素,而不是访问元素,您可以更改内部布局而无需触及任何算法(不过,您需要重新编译代码):

// version using std::vector<std::pair<Nuclide, double> >
// - it would just use std::vector<std::pair<Nuclide, double>::iterator as iterator
auto nuclide_projection = [](Sample::key& key) -> Nuclide& {
    return key.first;
}
auto value_projecton = [](Sample::key& key) -> double {
    return key.second;
}

// version using two std::vectors:
// - it would use an iterator interface to an integer, yielding a std::size_t for *it
struct nuclide_projector {
    std::vector<Nuclide>& nuclides;
    auto operator()(std::size_t index) -> Nuclide& { return nuclides[index]; }
};
constexpr nuclide_projector nuclide_projection;
struct value_projector {
    std::vector<double>& values;
    auto operator()(std::size_t index) -> double& { return values[index]; }
};
constexpr value_projector value_projection;

使用一对就位,例如一个算法简单地在它们上面运行并打印它们可能看起来像这样:

template <typename Iterator>
void print(std::ostream& out, Iterator begin, Iterator end) {
    for (; begin != end; ++begin) {
         out << "nuclide=" << nuclide_projection(*begin) << ' '
             << "value=" << value_projection(*begin) << '\n';
    }
}

两种表示完全不同,但访问它们的算法完全独立。这样也很容易尝试不同的表示:只有表示和访问它的算法的粘合需要更改。

【讨论】:

  • 哇,就像我以前从未见过的示例代码一样(我是一名物理专业的学生,​​所以我对高级 C++ 的经验有限:P)。我不知道这个auto something = [](someref&amp;) -&gt; type {return...},发生了什么......你的方法似乎真的很好,但是对于我这里的情况来说有点太多了(至少就我的知识水平而言......)。但是解释我引用的那一行,可以帮助我。另外,我看不到您的算法示例如何对beginend 之间的所有元素执行相同的操作(我看不到“循环”)...抱歉给您带来不便。
  • @LCsa:循环很简单:我忘了 :) 现在添加了。 [](...)... 只是一个 lambda 函数,即一种快速编写函数对象的方法。 auto 只是推断其类型(不可能编写 lambda 表达式的类型)。
  • Ohhhhkay,这让我很舒服……用更简单的话来说:Sample 类应该提供“get”函数?
猜你喜欢
  • 1970-01-01
  • 2015-06-02
  • 2012-08-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多