C ++有没有订购哈希?

时间:2015-12-08 23:30:24

标签: c++ perl

Perl有一个名为"有序哈希" Tie::IxHash的结构。可以将其用作哈希表/地图。条目按插入顺序排列。

不知道C ++中是否有这样的东西。

以下是Perl代码段示例:

use Tie::IxHash;

tie %food_color, "Tie::IxHash";
$food_color{Banana} = "Yellow";
$food_color{Apple}  = "Green";
$food_color{Lemon}  = "Yellow";

print "In insertion order, the foods are:\n";
foreach $food (keys %food_color) {
    print "  $food\n"; #will print the entries in order
}

更新1

正如@ kerrek-sb指出的那样,可以使用Boost Multi-index Containers Library。只是想知道是否可以用STL做到这一点。

3 个答案:

答案 0 :(得分:2)

是和否。不,没有人专门提供完全相同的功能。但是,是的,您可以通过几种不同的方式做同样的事情。如果您希望主要按插入的顺序访问数据,那么显而易见的方法是成对的简单向量:

config.ready.done(function() { 
    // Use config properties
});

通过简单地按顺序存储项目来保存订单。如果您需要通过密钥访问它们,则可以使用std::vector<std::string, std::string> food_colors; food_colors.push_back({"banana", "yellow"}); food_colors.push_back({"apple", "green"}); food_colors.push_back({"lemon", "yellow"}); for (auto const &f : food_colors) std::cout << f.first << ": " << f.second << "\n"; 对特定项目进行线性搜索。这样可以最大限度地减少使用的额外内存,但如果获得大量项目则会以密钥访问速度为代价。

如果您希望通过具有大量项目的密钥更快地访问,则可以使用Boost MultiIndex。如果你真的想避免这种情况,你可以很容易地创建自己的索引。要执行此操作,您首先要将项目插入std::find(或者std::unordered_map)。这样可以通过密钥快速访问,但不能按插入顺序访问。但是,它会在每个项目插入地图时返回一个迭代器。您可以简单地将这些迭代器存储到向量中,以按插入顺序进行访问。虽然这个原理相当简单,但代码有点笨拙,说得好听:

std::map

这允许通过键访问:

std::map<std::string, std::string> fruit;
std::vector<std::map<std::string, std::string>::iterator> in_order;

in_order.push_back(fruit.insert(std::make_pair("banana", "yellow")).first);
in_order.push_back(fruit.insert(std::make_pair("apple", "green")).first);
in_order.push_back(fruit.insert(std::make_pair("lemon", "yellow")).first);

...或按插入顺序:

// ripen the apple:
fruit["apple"] = "red";

目前,我已经展示了这样做的基本机制 - 如果你想要使用它,你可能想把它包装成一个很好的类来隐藏一些丑陋和保留东西正常使用时非常干净。

答案 1 :(得分:1)

记住插入顺序的关联容器不随C ++标准库一起提供,但使用现有的STL容器实现它是很简单的。

例如,std::map(用于快速查找)和std::list(用于维护键排序)的组合可用于模拟插入有序映射。这是一个展示这个想法的例子:

#include <unordered_map>
#include <list>
#include <stdexcept>

template<typename K, typename V>
class InsOrderMap {
  struct value_pos {
    V value;
    typename std::list<K>::iterator pos_iter;
    value_pos(V value, typename std::list<K>::iterator pos_iter):
      value(value), pos_iter(pos_iter) {}
  };

  std::list<K> order;
  std::unordered_map<K, value_pos> map;

  const value_pos& locate(K key) const {
    auto iter = map.find(key);
    if (iter == map.end())
      throw std::out_of_range("key not found");
    return iter->second;
  }

public:
  void set(K key, V value) {
    auto iter = map.find(key);
    if (iter != map.end()) {
      // no order change, just update value
      iter->second.value = value;
      return;
    }
    order.push_back(key);
    map.insert(std::make_pair(key, value_pos(value, --order.end())));
  }

  void erase(K key) {
    order.erase(locate(key).pos_iter);
    map.erase(key);
  }

  V operator[](K key) const {
    return locate(key).value;
  }

  // iterate over the mapping with a function object
  // (writing a real iterator is too much code for this example)
  template<typename F>
  void walk(F fn) const {
    for (auto key: order)
      fn(key, (*this)[key]);
  }
};

// TEST

#include <string>
#include <iostream>
#include <cassert>

int main()
{
  typedef InsOrderMap<std::string, std::string> IxHash;

  IxHash food_color;
  food_color.set("Banana", "Yellow");
  food_color.set("Apple", "Green");
  food_color.set("Lemon", "Yellow");

  assert(food_color["Banana"] == std::string("Yellow"));
  assert(food_color["Apple"] == std::string("Green"));
  assert(food_color["Lemon"] == std::string("Yellow"));

  auto print = [](std::string k, std::string v) {
    std::cout << k << ' ' << v << std::endl;
  };
  food_color.walk(print);
  food_color.erase("Apple");
  std::cout << "-- without apple" << std::endl;
  food_color.walk(print);
  return 0;
}

将此代码开发成为std::map等完整容器的替代品,需要付出相当大的努力。

答案 2 :(得分:-2)

C ++拥有标准容器。 无序地图似乎就像您要找的那样:

std::unordered_map <std::string, std::string> mymap = {{"Banana", "Yellow" }, {"Orange","orange" } }