2010-03-18 31 views
10

Có một bản đồ Java keySet() tương đương với C++ 's std::map?Có một bản đồ Java keySet() tương đương với bản đồ std :: của C++ không?

Phương pháp Java keySet() trả "a set view of the keys contained in this map."

+1

Nó luôn luôn nhầm lẫn tôi tại sao không có chức năng thành viên này trong 'std :: map'. Tôi biết rất đơn giản để tự mình thực hiện, nhưng có rất nhiều thứ đã làm cho nó thành STL.Tôi tò mò muốn nghe nếu có ai biết lý do là gì cho * không * kể cả nó. –

+6

@Tyler: Không có của nó bởi vì bản đồ là một container. Mục đích duy nhất là cung cấp cấu trúc thùng chứa liên kết và truy cập vào nội dung của nó. Nó không chịu trách nhiệm cung cấp tất cả những thứ nhỏ nhặt gọn gàng mà người ta có thể làm với nó như một thùng chứa, đó là nhiệm vụ cho các thuật toán và mã do người dùng định nghĩa chứ không phải là vùng chứa. –

+0

@darid Điều đó có ý nghĩa, nhưng sau đó lại có 'std :: string' –

Trả lời

1

Có lẽ sau đây có thể được sử dụng:

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

Một tình trạng quá tải cho các make_key_set chức năng lấy chuỗi tương thích STL s uch như std :: vector, std :: deque hoặc std :: danh sách có thể thực hiện như sau:

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

vấn đề duy nhất của tôi là đây là một chức năng thường xuyên được yêu cầu, bằng cách không biến nó thành một phần của STL. Chỉ khuyến khích các lỗi mã hóa. –

+0

Đây là O (n * log (n)). Bạn có thể làm cho nó O (n) bằng cách thay thế 'set.insert ((itr ++) -> đầu tiên);' với 'set.insert (set.end(), (itr ++) -> đầu tiên);' – Notinlist

0

bạn có thể thực hiện nó cho mình:

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

Tại sao không làm cho các phím 'std :: set ' thực sự phù hợp với chức năng? –

+1

phím() nên tiện dụng. nhưng hãy nhớ chúng tôi lấy chìa khóa vì chúng tôi muốn xử lý chúng sau này. ví dụ. chúng ta có thể cần phải lặp qua các khóa, sau đó tại sao không trực tiếp lặp lại trên bản đồ. –

1

Dưới đây là một-và-một-bit lót:

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

Nếu bạn don 't có 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

Cảm ơn, đó là dễ dàng hơn (ngắn hơn) so với các đề xuất khác ... –

8

Tất cả các câu trả lời được trình bày vậy, đến nay kết thúc việc tạo một std::set trực tiếp, mà có thể không lý tưởng: nếu bạn chỉ muốn có thể để lặp qua các phím, bạn không muốn có chi phí tạo ra toàn bộ container mới.

Một tùy chọn linh hoạt hơn là sử dụng trình biến đổi biến đổi để chuyển đổi trình biến đổi std::map thành một loại trình vòng lặp nào đó chỉ tạo ra khóa khi được tham chiếu. Điều này là khá đơn giản bằng cách sử dụng Boost Chuyển 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>() 
    ); 
} 

Với những chức năng tiện ích, bạn có thể chuyển đổi bất kỳ phạm vi của std::map lặp (hoặc lặp vào bất kỳ cặp đựng liên quan nào khác bạn có thể có) vào một loạt các chỉ các phím . Như một ví dụ:

đầu ra
#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, ",")); 
} 

chương trình này:

1,2,3, 

Nếu bạn thực sự muốn có một std::set chứa các phím, bạn có thể dễ dàng tạo ra một cách sử dụng các vòng lặp:

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

Nhìn chung , đó là một giải pháp linh hoạt hơn.

Nếu bạn không có Boost hoặc không muốn sử dụng Boost hoặc không thể sử dụng Boost, chuyển iterator cụ này có thể được thực hiện khá dễ dàng:

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

Việc sử dụng cho việc này là như nhau cho phiên bản Boost.

+1

Hehe - câu trả lời hay đấy Mr. McNellis :) +1. –

+0

Thay đổi SO UN của bạn thành @Jon Skeet. Sau đó, khi ai đó nhấn downvote nó thực sự upvotes bài hai lần thay vì :) – Matt

Các vấn đề liên quan