2010-03-18 21 views
10

Existe-t-il un équivalent KeySet Java Map() pour les std::map de C++?Existe-t-il un équivalent Java MapSet() pour std :: map de C++?

La méthode Java keySet() retourne "a set view of the keys contained in this map."

+1

Il m'a toujours confus pourquoi il n'y a pas de fonction de membre pour cela dans 'std :: map'. Je sais que c'est simple à mettre en œuvre, mais il y a aussi beaucoup de choses qui sont entrées dans le STL.Je serais curieux d'entendre si quelqu'un sait quelle était la raison pour * ne * pas l'inclure. –

+6

@Tyler: Ce n'est pas là parce que la carte est un conteneur. Son seul but est de fournir une structure de conteneur associative et un accès à son contenu. Il n'est pas responsable de fournir toutes les petites choses utiles que l'on pourrait faire avec un conteneur, c'est la tâche pour les algorithmes et le code défini par l'utilisateur et non le conteneur. –

+0

@darid Cela a du sens, mais là encore il y a 'std :: string' –

Répondre

1

Peut-être les éléments suivants pourraient être utiles:

#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; 
} 

Une surcharge pour le make_key_set Fonction prenant des séquences compatibles STL s UCH comme std :: vecteur, std :: deque ou std :: liste peut être comme suit:

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); 
    } 
} 
+2

mon seul problème est que c'est une fonctionnalité qui est souvent nécessaire, en ne faisant pas de facto partie de STL. Seules les erreurs de codage sont encouragées. –

+0

Ceci est O (n * log (n)). Vous pouvez le faire O (n) en remplaçant 'set.insert ((itr ++) -> d'abord);' avec 'set.insert (set.end(), (itr ++) -> d'abord);' – Notinlist

0

vous pouvez implémenter vous-même:

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

Pourquoi ne pas faire les clés 'std :: set ' pour vraiment correspondre à la fonctionnalité? Les touches –

+1

devraient être utiles. mais souvenez-vous que nous obtenons les clés parce que nous voulons les traiter plus tard. par exemple. nous aurons peut-être besoin de parcourir les touches, alors pourquoi ne pas parcourir directement la carte. –

1

Voici une doublure d'un et un peu:

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

Si vous ne « t ont 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; } 
}; 
+0

Merci, c'est beaucoup plus facile (plus courte) que les autres suggestions ... –

8

Toutes les réponses présentées fin à ce jour jusqu'à la création d'un directement std::set, ce qui peut ne pas être idéal: si vous ne voulez pouvoir itérer sur les touches, vous ne voulez pas avoir les frais généraux de la création d'un ensemble nouveau conteneur.

Une option plus flexible consisterait à utiliser un itérateur de transformation qui convertit un itérateur std::map en un type d'itérateur qui ne donne que la clé lorsqu'il est déréférencé. Ceci est assez simple en utilisant le Boost Transformer 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>() 
    ); 
} 

Avec ces fonctions utilitaires, vous pouvez convertir une gamme de std::map itérateurs (ou itérateurs dans une autre paire conteneur associatif, vous devrez peut-être) dans une gamme de seulement les clés . A titre d'exemple:

#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, ",")); 
} 

Ce programme affiche:

1,2,3, 

Si vous ne voulez vraiment un std::set contenant les clés, vous pouvez facilement créer un en utilisant ces itérateurs:

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

Dans l'ensemble , c'est une solution plus flexible.

Si vous n'avez pas Boost ou ne voulez pas utiliser Boost ou ne peut pas utiliser Boost, cette transformation spécifique iterator peut être assez facilement mis en œuvre:

#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()); } 

L'utilisation de c'est le même comme pour la version Boost.

+1

Hehe - belle réponse M. McNellis :) +1. –

+0

Remplacez votre SO UN par @Jon Skeet. Puis, quand quelqu'un appuie sur le downvote, il redirige le message deux fois à la place :) – Matt