    <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:admin="http://webns.net/mvcb/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:content="http://purl.org/rss/1.0/modules/content/">
     <channel>
        <title>ACCU  :: Indexing STL lists with maps</title>
        <link>https://members.accu.org/index.php/articles/2011</link>
        <description>Professionalism in Programming</description>
        <dc:language>en-us</dc:language> 
        <dc:creator>Administrator</dc:creator> 
        <admin:generatorAgent rdf:resource="http://www.xaraya.org" /> 
        <admin:errorReportsTo rdf:resource="mailto:webeditor@accu.org" />
       <sy:updatePeriod>hourly</sy:updatePeriod>
       <sy:updateFrequency>1</sy:updateFrequency>
       <docs>http://backend.userland.com/rss</docs>




<div class="xar-mod-head"><span class="xar-mod-title">Programming Topics + Overload Journal #53 - Feb 2003</span></div>

<table border="0" cellpadding="1" cellspacing="0">
    <tbody>
    <tr>
        <td valign="top">
            Browse in :
       </td>
       <td valign="top">

                                            <a href="https://members.accu.org/index.php/articles/">All</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c13/">Topics</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c65/">Programming</a>
<br />

                                            <a href="https://members.accu.org/index.php/articles/">All</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c76/">Journals</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c78/">Overload</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c193/">53</a>
<br />

                                            <a href="https://members.accu.org/index.php/articles/c65-193/">Any of these categories</a>

                    -                        <a href="https://members.accu.org/index.php/articles/c65+193/">All of these categories</a>
<br />
</td>
   </tr>
   </tbody>
</table>




<div class="xar-error">
   <p>
 <strong>Note:</strong> when you create a new publication type,
the articles module will automatically use the templates
<em>user-display-[publicationtype].xt</em>
and <em>user-summary-[publicationtype].xt</em>.
If those templates do not exist when you try to preview or display a new article,
you'll get this warning :-)  Please place your own templates in themes/<em>yourtheme</em>/modules/articles . The templates will get the extension .xt there. </p>
</div>
<div class="xar-norm xar-standard-box-padding">
   <h1><strong>Title:</strong>&nbsp;Indexing STL lists with maps</h1>
<p><strong>Author:</strong>&nbsp;Martin Moene</p>
<p>
<strong>Date:</strong> 05 February 2003 21:10:07 +00:00 or Wed, 05 February 2003 21:10:07 +00:00</p>
<p><strong>Summary:</strong>&nbsp;</p>
<p><strong>Body:</strong>&nbsp;<p>A list will keep its elements in the same order that they were added, but searching for elements takes linear time (i.e. time proportional to the length of the list). Removing an element from a list is a constant-time operation if you have an iterator that refers to the element in question, but if you donâ€™t have such an iterator then the element must first be found and this will also take linear time.</p>
<p>In the case of a set, searching and removing is faster (logarithmic, and in the case of the SGI extension <code>hash_set</code>, amortized linear). However, sets donâ€™t remember the order that the elements were added in.</p>
<p>If you want to have an ordered list of elements, but you also want to be able to find given elements quickly, then you can combine a list with a map to index it. This article describes a brief template class called <code>hashed_list</code>, which uses the SGI extension <code>hash_map</code> and the STL class list; it could also use the STL class <code>map</code> with little modification. The template class described here does not support all STL semantics (for example, you canâ€™t get a nonÂ­const iterator out of it); this article is meant to demonstrate the concept.</p>
<p>Note that <code>hash_map</code> will assume that all the elements are unique. Generalising this class to support non-unique elements is left as an exercise to the reader (besides using <code>multimap</code>, you need to think about the <code>erase()</code> method that is defined below).</p>
<h2>The general idea</h2>
<p>The objects are stored in a list, and also in a map. The map will map objects to list iterators. Whenever an object is added to the list, an iterator that refers to that object is taken and stored in the map under that object. Then when objects need to be counted or deleted, the map is used to quickly retrieve the list iterator, so that the list does not need to be searched linearly.</p>
<p>This relies on the fact that iterators in a list will remain valid even if other parts of the list are modified. The only thing that invalidates a list iterator is deleting the object that it points to. Hence it is acceptable to store list iterators for later reference.</p>
<h2>The code</h2>
<p>First, we include the two container classes that we will be using:</p>
<pre><code>#include &lt;list&gt;
#include &lt;hash_map&gt;
using namespace std;
</code></pre>

<p>Now for the <code>hashed_list</code> class itself. Since <code>hash_map</code> is a template class with three arguments (the object type, the hash function, and the equality function), we need to support the same three arguments if weâ€™re making a general template:</p>
<pre><code>template&lt;class object, class hashFunc, class equal&gt;
class hashed_list {
</code></pre>

<p>I like to start with some typedefs to save typing later. We will need iterators and const iterators to the list, and also a type for the map:</p>
<pre><code>    typedef list&lt;object&gt;::iterator iterator;
    typedef list&lt;object&gt;::const_iterator const_iterator;
    typedef hash_map&lt;object,iterator, hashFunc,equal&gt; mapType;
</code></pre>

<p>And the data itself. For brevity Iâ€™ll be storing it directly and I wonâ€™t write an explicit constructor, so we donâ€™t have to worry about the allocation.</p>
<pre><code>    list&lt;object&gt; theList;
    mapType theMap;
</code></pre>

<p>Now for some methods. Obtaining iterators is simply a matter of getting them from the list; we only allow const iterators because otherwise weâ€™d have to write lots more code to deal with what happens when the user changes things via the iterators. Similarly, obtaining a count of objects just involves getting it from the map (since <code>hash_map</code> does not allow duplicate keys, the result will be <code>0</code> or <code>1</code>).</p>
<pre><code>public:
    const_iterator begin() const {
        return theList.begin();
    }
    const_iterator end() {
        return theList.end();
    }
    mapType::size_type count(const object&amp; k) const {
        return theMap.count(k);
    }
</code></pre>

<p>Now if we want to add an object to the list, we must also add it to the map. Since the listâ€™s <code>insert()</code> method returns an iterator that refers to the object we just added, we can give its return value to the map, so our <code>add()</code> method is one statement:</p>
<pre><code>    void add(const object &amp;o) {
        theMap[o] = theList.insert(theList.end(),o);
    }
</code></pre>

<p>To check that the â€œobjects must be uniqueâ€ constraint is not being violated, you might wish to add</p>
<pre><code>assert(theMap.count(o)==0);
</code></pre>

<p>to the beginning of the above method (and <code>#include &lt;cassert&gt;</code>).</p>
<p>The following method will erase a given object. First, a map iterator is obtained; this is dereferenced to get the list iterator and erase it from the list, and then the map iterator is used to erase it from the map. That way, only one lookup operation is needed in the map (when the iterator is found); it is not necessary to look up the object twice (once to reference it, again to erase it).</p>
<pre><code>    void erase(const object &amp;o) {
        mapType::iterator i=theMap.find(o);
        theList.erase((*i).second);
        theMap.erase(i);
    }
</code></pre>

<p>For readability you could also write it like this, but it will be less efficient if the lookup takes longer (even in a hashtable it can take a while in the worst case):</p>
<pre><code>void slower_erase(const object &amp;o) {
    theList.erase(theMap[o]);
    theMap.erase(o);
}
</code></pre>

<p>Finally, finish the class:</p>
<pre><code>};
</code></pre>

<p>To test it, you might want to write something like this:</p>
<pre><code>#include &lt;string.h&gt;
struct strEqual {
    bool operator()(const char* s1, const char* s2) const {
        return (s1==s2 || !strcmp(s1,s2));
        // (although strcmp() might already have the s1==s2 check)
    }
};

typedef hashed_list&lt;const char*, hash&lt;const char*&gt;, strEqual&gt; TestType;

int main() {
    TestType t;
    t.add(&quot;one&quot;);
    t.add(&quot;two&quot;);
    t.add(&quot;three&quot;);
    cout &lt;&lt; t.count(&quot;two&quot;) &lt;&lt; endl;
                                    // should be 1
    t.erase(&quot;two&quot;); cout &lt;&lt; t.count(&quot;two&quot;) &lt;&lt; endl;
                                    // should be 0
    copy(
        t.begin(), t.end(),
        ostream_iterator&lt;const char*&gt;(cout,&quot; &quot;));
    cout &lt;&lt; endl;
}
</code></pre>

<h2>Conclusion</h2>
<p>The above code will increase the speed of finding items in lists, particularly when they are long. This is at the expense of consuming more memory (because of the map) and making the maintenance of the list slightly slower (because the map needs to be maintained with it). If <code>hash_map</code> is being used then the overhead of maintaining the map is (in the amortized case) a constant for each maintenance operation, which may or may not be acceptable depending on the application and on what proportion of its operations are maintenance.</p>
<p>Silas S Brown<br>
<a href="mailto:ssb22@cam.ac.uk">ssb22@cam.ac.uk</a></p>
</p>
<p><strong>Notes:</strong>&nbsp;</p>
<p><em>More fields may be available via dynamicdata ..</em></p>
</div>
</channel>
</rss>
