【问题标题】:Is there a Java Map keySet() equivalent for C++'s std::map?是否有与 C++ 的 std::map 等效的 Java Map keySet()?
【发布时间】:2011-01-28 20:21:48
【问题描述】:

是否有与 C++ 的std::map 等效的 Java Map keySet()?

Java 的keySet() 方法返回"a set view of the keys contained in this map."

【问题讨论】:

  • 我一直很困惑为什么std::map 没有成员函数。我知道自己实现很简单,但是很多东西确实使它成为了 STL。我很想知道是否有人知道包括它的理由是什么。
  • @Tyler:它不存在,因为 map 是一个容器。它的唯一目的是提供一个关联的容器结构,并访问它的内容。它不负责提供人们可以用它作为容器做的所有整洁的小事情,这是算法和用户定义代码的任务,而不是容器。
  • @darid 确实有道理,但又是std::string
  • @Tyler 我同意你的观点,喜欢 c++ 的人一定有一些奇怪的学术理由(对象必须反映现实世界的对象)。然而,它只会让程序员的生活变得困难,java和python都有这个能力。如果需要如此严格的学术行为,我只会使用 lisp 或 haskell,遗憾的是我在做课堂项目时遇到了这个问题,因此我无法控制语言。
  • @darid 通过让用户编写该代码,您只会鼓励错误。如果我们必须对谁做什么事持迂腐态度,或者更具体一些,我们应该只使用 Lisp 或 Haskell 或仅使用 Assembly 进行编码。 Java 有,Python 有,C++ 有什么问题 然而 C++ 并不完全面向对象,如果它的设计委员会想要如此迂腐的话!

标签: java c++ stl stdmap


【解决方案1】:

到目前为止提供的所有答案最终都直接创建了std::set,这可能并不理想:如果您只想能够迭代键,您不希望有创建一个全新的容器。

更灵活的选择是使用转换迭代器,将std::map 迭代器转换为某种类型的迭代器,在取消引用时只生成键。这使用 Boost Transform Iterator 相当简单:

#include <functional>
#include <boost/iterator/transform_iterator.hpp>

// You may already have a select1st implementation; if not, you should :-)
template <typename Pair>
struct select1st
    : std::unary_function<const Pair&, const typename Pair::first_type&>
{
    const typename Pair::first_type& operator()(const Pair& p) const 
    { 
        return p.first; 
    }
};

template <typename C>
boost::transform_iterator<
    select1st<typename C::value_type>, typename C::const_iterator
> begin_keys(const C& c) 
{ 
    return boost::make_transform_iterator(
        c.begin(), select1st<typename C::value_type>()
    );
}

template <typename C>
boost::transform_iterator<
    select1st<typename C::value_type>, typename C::const_iterator
> end_keys(const C& c) 
{ 
    return boost::make_transform_iterator(
        c.end(), select1st<typename C::value_type>()
    );
}

使用这些实用函数,您可以将任何范围的 std::map 迭代器(或将迭代器转换为您可能拥有的任何其他对关联容器)到仅包含键的范围。举个例子:

#include <iostream>
#include <iterator>
#include <map>

int main()
{
    std::map<int, int> m;
    m.insert(std::make_pair(1, 2));
    m.insert(std::make_pair(2, 4));
    m.insert(std::make_pair(3, 6));

    std::copy(
        begin_keys(m), end_keys(m), 
        std::ostream_iterator<int>(std::cout, ","));
}

这个程序输出:

1,2,3,

如果您确实想要一个包含密钥的std::set,您可以使用这些迭代器轻松创建一个:

std::set<int> s(begin_keys(m), end_keys(m));

总的来说,这是一个更灵活的解决方案。

如果你没有 Boost 或者不想使用 Boost 或者不能使用 Boost,这个特定的变换迭代器可以很容易地实现:

#include <iterator>

template <typename C>
class key_iterator
    : public std::iterator<
          std::bidirectional_iterator_tag, 
          typename C::key_type, 
          typename C::difference_type, 
          typename C::pointer, 
          typename C::reference
      >
{
public:

    key_iterator() { }
    explicit key_iterator(typename C::const_iterator it) : it_(it) { }

    typename const C::key_type& operator*() const  { return  it_->first; }
    typename const C::key_type* operator->() const { return &it_->first; }

    key_iterator& operator++() { ++it_; return *this; }
    key_iterator operator++(int) { key_iterator it(*this); ++*this; return it; }

    key_iterator& operator--() { --it_; return *this; }
    key_iterator operator--(int) { key_iterator it(*this); --*this; return it; }

    friend bool operator==(const key_iterator& lhs, const key_iterator& rhs)
    {
        return lhs.it_ == rhs.it_;
    }

    friend bool operator!=(const key_iterator& lhs, const key_iterator& rhs)
    {
        return !(lhs == rhs);
    }

private:

    typename C::const_iterator it_;
};

template <typename C>
key_iterator<C> begin_keys(const C& c) { return key_iterator<C>(c.begin()); }

template <typename C>
key_iterator<C> end_keys(const C& c)   { return key_iterator<C>(c.end());   }

其用法与 Boost 版本相同。

【讨论】:

  • 呵呵——麦克内利斯先生的回答很好:) +1。
  • 将您的 SO UN 更改为 @Jon Skeet。然后,当有人按下否决票时,它实际上是对帖子投了两次赞成票:)
【解决方案2】:

也许以下可能有用:

#include <iostream>
#include <iterator>
#include <algorithm>
#include <map>
#include <set>
#include <string>

template< class Key, 
          class T, 
          class Comparator,
          class MapAllocator,
          class SetAllocator>
void make_key_set(const std::map<Key,T,Comparator,MapAllocator>& map, 
                  std::set<Key,Comparator,SetAllocator>& set)
{
   set.clear();
   typedef typename std::map<Key,T,Comparator,MapAllocator> map_type;
   typename map_type::const_iterator itr = map.begin();
   while (map.end() != itr)
   {
      set.insert((itr++)->first);
   }
}

int main()
{
  std::map<std::string, double> m;

  m["one"] = 1.1;
  m["two"] = 2.2;
  m["three"] = 3.3;

  std::set<std::string> key_set;

  make_key_set(m,key_set); 

  std::copy(key_set.begin(), key_set.end(),
            std::ostream_iterator<std::string>(std::cout, "\n"));

  return  0;
}

make_key_set 函数的重载采用 STL 兼容序列,例如 std::vector、std::deque 或 std::list 可以如下:

template< class Key, 
          class T, 
          class Comparator,
          class MapAllocator,
          class SeqAllocator,
          template<class,class> class Sequence>
void make_key_set(const std::map<Key,T,Comparator,MapAllocator>& map, 
                  Sequence<Key,SeqAllocator>& sequence)
{
   sequence.clear();
   typedef typename std::map<Key,T,Comparator,MapAllocator> map_type;
   typename map_type::const_iterator itr = map.begin();
   while (map.end() != itr)
   {
      sequence.push_back((itr++)->first);
   }
}

【讨论】:

  • 我唯一的问题是这是一个经常需要的功能,因为它没有成为 STL 事实上的一部分。只鼓励出现编码错误。
  • 这是 O(n*log(n))。您可以通过将 set.insert((itr++)-&gt;first); 替换为 set.insert(set.end(),(itr++)-&gt;first); 来使其成为 O(n)
【解决方案3】:

这里是一个一点点的班轮:

map<K,V> m;
...
// Useful stuff goes here
...
set<K> s;
transform(m.begin(), m.end(), inserter(s, s.begin()), select1st<pair<K,V> >());

如果你没有select1st:

template <class P>
struct select1st : public std::unary_function<P, typename P::first_type>
{
    const typename P::first_type& operator()(const P &arg) const { return arg.first; }
};

【讨论】:

    【解决方案4】:

    你可以自己实现:

    vector<T> keys;
    for (map<T,S>::iterator it=m.begin(); it!=m.end; it++)
      keys.push_back(it->first)
    

    【讨论】:

    • 为什么不让std::set&lt;T&gt; keys 真正匹配功能?
    • keys() 应该很方便。但请记住,我们得到密钥是因为我们想稍后处理它们。例如我们可能需要遍历键,那为什么不直接遍历映射呢。
    【解决方案5】:

    Boost.Range 提供此为boost::adaptors::map_values

    std::map my_map;
    for (auto &value : my_map | boost::adaptors::map_values) {
        //...
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-08-18
      • 2010-10-22
      • 2020-07-18
      • 2010-10-15
      • 1970-01-01
      • 1970-01-01
      • 2020-02-07
      • 2017-07-18
      相关资源
      最近更新 更多