Table of Contents Previous Next
Logo
Freeze : 36.3 The Freeze Map
Copyright © 2003-2008 ZeroC, Inc.

36.3 The Freeze Map

A Freeze map is a persistent, associative container in which the key and value types can be any primitive or user-defined Slice types. For each pair of key and value types, the developer uses a code-generation tool to produce a language-specific class that conforms to the standard conventions for maps in that language. For example, in C++, the generated class resembles a std::map, and in Java it implements the java.util.SortedMap interface. Most of the logic for storing and retrieving state to and from the database is implemented in a Freeze base class. The generated map classes derive from this base class, so they contain little code and therefore are efficient in terms of code size.
You can only store data types that are defined in Slice in a Freeze map. Types without a Slice definition (that is, arbitrary C++ or Java types) cannot be stored because a Freeze map reuses the Ice-generated marshaling code to create the persistent representation of the data in the database. This is especially important to remember when defining a Slice class whose instances will be stored in a Freeze map; only the "public" (Slice-defined) data members will be stored, not the private state members of any derived implementation class.

36.3.1 Freeze Connections

In order to create a Freeze map object, you first need to obtain a Freeze Connection object by connecting to a database environment.
As illustrated in Figure 36.2, a Freeze map is associated with a single connection and a single database file. Connection and map objects are not thread-safe: if you want to use a connection or any of its associated maps from multiple threads, you must serialize access to them. If your application requires concurrent access to the same database file (persistent map), you must create several connections and associated maps.
Figure 36.2. Freeze connections and maps.
Freeze connections provide operations that allow you to begin a transaction, access the current transaction, get the communicator associated with a connection, close a connection, and remove a map index. See the online Slice reference for more information on these operations.

36.3.2 Transactions

You may optionally use transactions with Freeze maps. Freeze transactions provide the usual ACID (atomicity, concurrency, isolation, durability) properties. For example, a transaction allows you to group several database updates in one atomic unit: either all or none of the updates within the transaction occur.
A transaction is started using the beginTransaction operation on the Connection object. Once a connection has an associated transaction, all operations on the map objects associated with this connection use this transaction. Eventually, you end the transaction by calling commit or rollback: commit saves all your updates while rollback undoes them.
module Freeze {

local interface Transaction {
    void commit();
    void rollback();
}; 

local interface Connection {
    Transaction beginTransaction();
    idempotent Transaction currentTransaction();
    // ...
};
};
If you do not use transactions, every non-iterator update is enclosed in its own internal transaction, and every read-write iterator has an associated internal transaction that is committed when the iterator is closed.

36.3.3 Iterators

Iterators allow you to traverse the contents of a Freeze map. Iterators are implemented using Berkeley DB cursors and acquire locks on the underlying database page files. In C++, both read-only (const_iterator) and read-write iterators (iterator) are available; in Java, only read-write iterators are supported.
Locks held by an iterator are released when the iterator is closed (if you do not use transactions) or when the enclosing transaction ends. Releasing locks held by iterators is very important to let other threads access the database file through other connection and map objects. Occasionally, it is even necessary to release locks to avoid self-deadlock (waiting forever for a lock held by an iterator created by the same thread).
To improve ease of use and make self-deadlocks less likely, Freeze often closes iterators automatically. If you close a map or connection, associated iterators are closed. Similarly, when you start or end a transaction, Freeze closes all the iterators associated with the corresponding maps. If you do not use transactions, any write operation on a map (such as inserting a new element) automatically closes all iterators opened on the same map object, except for the current iterator when the write operation is performed through that iterator.
There is, however, one situation where an explicit iterator close is needed to avoid self-deadlock:
• you do not use transactions, and
• you have an opened iterator that was used to update a map (it holds a write lock), and
• in the same thread, you read that map.
Read operations never close iterators automatically. In that situation, you need to either use transactions or explicitly close the iterator that holds the write lock.

36.3.4 Recovering from Deadlock Exceptions

If you use multiple threads to access a database file, Berkeley DB may acquire locks in conflicting orders (on behalf of different transactions or iterators). For example, an iterator could have a read-lock on page P1 and attempt to acquire a write-lock on page P2, while another iterator (on a different map object associated with the same database file) could have a read-lock on P2 and attempt to acquire a write-lock on P1.
When this occurs, Berkeley DB detects a deadlock and resolves it by returning a "deadlock" error to one or more threads. For all non-iterator operations performed outside any transaction, such as an insertion into a map, Freeze catches such errors and automatically retries the operation until it succeeds. (In that case, the most-recently acquired lock is released before retrying.) For other operations, Freeze reports this deadlock by raising Freeze::DeadlockException. In that case, the associated transaction or iterator is also automatically rolled back or closed. A properly written application is expected to catch deadlock exceptions and retry the transaction or iteration.

36.3.5 Key Sorting

Keys in Freeze maps and indexes are always sorted. By default, Freeze sorts keys according to their Ice-encoded binary representation; this is very efficient but the resulting order is rarely meaningful for the application.
Starting with Ice 3.0, Freeze offers the ability to specify your own comparator objects. In C++, you specify these comparators as options to the slice2freeze utility (see below). In Java, the generated Java map class provides a number of constructors to accept these comparator objects.
The generated map provides the standard features of std::map (in C++) and java.util.SortedMap (in Java). Iterators return entries according to the order you have defined for the main key with your comparator object. In C++, lower_bound, upper_bound, and equal_range provide range-searches (see the definition of these functions on std::map). In Java, use headMap, tailMap, and subMap for range-searches; these methods come from the java.util.SortedMap interface.
Apart from these standard features, the generated map provides additional functions and methods to perform range searches using secondary keys. In C++, the additional functions are lowerBoundForMember, upperBoundForMember, and equalRangeForMember, where Member is the name of the secondary-key member. These functions return regular iterators on the Freeze map.
In Java, the additional non-standard methods are:
public java.util.SortedMap
headMapForIndex(String indexName, Object toKey)

public java.util.SortedMap
tailMapForIndex(String indexName, Object fromKey)

public java.util.SortedMap
subMapForIndex(String indexName,
               Object fromKey,
               Object toKey)
The key of the returned submap is the secondary key (the index), and its value is a java.util.Set of Map.Entry objects from the main Freeze map. This set provides all the entries in the main Freeze map with the given secondary key. When iterating over this submap, you may need to close iterators explicitly, such as iterators obtained for the main Freeze map. (See Section 36.3.3 for more information.)
Please note that the key comparator of a Freeze map should remain the same throughout the life of this map. Berkeley DB stores records according to the key order provided by this comparator; switching to another comparator will cause undefined behavior.

36.3.6 Indexing a Map

Freeze maps support efficient reverse lookups: if you define an index when you generate your map (with slice2freeze or slice2freezej), the generated code provides additional methods for performing reverse lookups. If your value type is a structure or a class, you can also index on a member of the value, and several such indexes can be associated with the same Freeze map.
Indexed searches are easy to use and very efficient. However, be aware that an index adds significant write overhead: with Berkeley DB, every update triggers a read from the database to get the old index entry and, if necessary, replace it.
If you later add an index to an existing map, Freeze automatically populates the index the next time you open the map. Freeze populates the index by instantiating each map entry, so it is important that you register the object factories for any class types in your map before you open the map.
Please note that the index key comparator of a Freeze map index should remain the same throughout the life of the index. Berkeley DB stores records according to the key order provided by this comparator; switching to another comparator will cause undefined behavior.

36.3.7 Using a Freeze Map in C++

This section describes the code generator and demonstrates how to use a Freeze map in a C++ program.

slice2freeze Command-Line Options

The Slice-to-Freeze compiler, slice2freeze, creates C++ classes for Freeze maps. The compiler offers the following command-line options in addition to the standard options described in Section 4.19:
• headerext EXT
Changes the file extension for the generated header files from the default h to the extension specified by EXT.
• sourceext EXT
Changes the file extension for the generated source files from the default cpp to the extension specified by EXT.
• addheader HDR[,GUARD]
This option adds an include directive for the specified header at the beginning of the generated source file (preceding any other include directives). If GUARD is specified, the include directive is protected by the specified guard. For example, addheader precompiled.h,__PRECOMPILED_H__ results in the following directives at the beginning of the generated source file:
#ifndef __PRECOMPILED_H__
#define __PRECOMPILED_H__
#include <precompiled.h>
#endif
As this example demonstrates, the addheader option is useful mainly for integrating the generated code with a compiler’s precompiled header mechanism.
This option can be repeated to create include directives for several files.
• includedir DIR
Modifies #include directives in source files to prepend the path name of each header file with the directory DIR. See Section 6.15.1 for more information.
• dllexport SYMBOL
Use SYMBOL to control DLL exports or imports. See the slice2cpp description for details.
• dict NAME,KEY,VALUE[,sort[,COMPARE]]
Generate a Freeze map class named NAME using KEY as key and VALUE as value. This option may be specified multiple times to generate several Freeze maps. NAME may be a scoped C++ name, such as Demo::Struct1ObjectMap. By default, keys are sorted using their binary Ice-encoded representation. Include sort to sort with the COMPARE functor class. If COMPARE is not specified, the default value is std::less<KEY>.
• dictindex MAP[,MEMBER]
[,casesensitive|caseinsensitive][,sort[,COMPARE]]
Add an index to the Freeze map named MAP. If MEMBER is specified, the map value type must be a structure or a class, and MEMBER must be a member of this structure or class. Otherwise, the entire value is indexed. When the indexed member (or entire value) is a string, the index can be case-sensitive (default) or case-insensitive. An index adds additional member functions to the generated C++ map:
iterator findByMEMBER(MEMBER_TYPE, bool = true);
const_iterator findByMEMBER(MEMBER_TYPE,
                            bool = true) const;
iterator beginForMEMBER();
const_iterator beginForMEMBER() const;
iterator endForMEMBER();
const_iterator endForMEMBER() const;
iterator lowerBoundForMEMBER(MEMBER_TYPE);
const_iterator lowerBoundForMEMBER(MEMBER_TYPE) const;
iterator upperBoundForMEMBER(MEMBER_TYPE);
const_iterator upperBoundForMEMBER(MEMBER_TYPE) const;
std::pair<iterator, iterator>
equalRangeForMEMBER(MEMBER_TYPE);
std::pair<const_iterator, const_iterator>
equalRangeForMEMBER(MEMBER_TYPE) const;
int MEMBERCount(MEMBER_TYPE) const;
When MEMBER is not specified, these functions are findByValue (const and non-const), lowerBoundForValue (const and non-const), valueCount, and so on. When MEMBER is specified, its first letter is capitalized in the findBy function name. MEMBER_TYPE corresponds to an in-parameter of the type of MEMBER (or the type of the value when MEMBER is not specified). For example, if MEMBER is a string, MEMBER_TYPE is a const std::string&.
By default, keys are sorted using their binary Ice-encoded representation. Include sort to sort with the COMPARE functor class. If COMPARE is not specified, the default value is std::less<MEMBER_TYPE>.
findByMEMBER returns an iterator to the first element in the Freeze map that matches the given index value. It returns end() if there is no match. When the second parameter is true (the default), the returned iterator provides only the elements with an exact match (and then skips to end()). Otherwise, the returned iterator sets a starting position and then provides all elements until the end of the map, sorted according to the index comparator.
lowerBoundForMEMBER returns an iterator to the first element in the Freeze map whose index value is not less than the given index value. It returns end() if there is no such element. The returned iterator provides all elements until the end of the map, sorted according to the index comparator.
upperBoundForMEMBER returns an iterator to the first element in the Freeze map whose index value is greater than the given index value. It returns end() if there is no such element. The returned iterator provides all elements until the end of the map, sorted according to the index comparator.
beginForMEMBER returns an iterator to the first element in the map.
endForMEMBER returns an iterator to the last element in the map.
equalRangeForMEMBER returns a range (pair of iterators) of all the elements whose index value matches the given index value. This function is similar to findByMEMBER (see above).
MEMBERCount returns the number of elements in the Freeze map whose index value matches the given index value.
Please note that index-derived iterators do not allow you to set new values in the underlying map.
• index CLASS,TYPE,MEMBER
[,casesensitive|caseinsensitive]
Generate an index class for a Freeze evictor (see Section 36.5.7). CLASS is the name of the class to be generated. TYPE denotes the type of class to be indexed (objects of different classes are not included in this index). MEMBER is the name of the data member in TYPE to index. When MEMBER has type string, it is possible to specify whether the index is case-sensitive or not. The default is case-sensitive.
Section 6.15.1 provides a discussion of the semantics of #include directives that is also relevant for users of slice2freeze.

Generating a Simple Map

As an example, the following command generates a simple map:
$ slice2freeze dict StringIntMap,string,int StringIntMap
This command directs the compiler to create a map named StringIntMap, with the Slice key type string and the Slice value type int. The final argument is the base name for the output files, to which the compiler appends the .h and .cpp suffixes. As a result, this command produces two C++ source files, StringIntMap.h and StringIntMap.cpp.

The Map Class

If you examine the contents of the header file created by the example in the previous section, you will discover that a Freeze map is an instance of the template class Freeze::Map:
// StringIntMap.h
typedef Freeze::Map<std::string, Ice::Int, ...> StringIntMap;
The Freeze::Map template class closely resembles the STL container class std::map, as shown in the following class definition:
namespace Freeze {
template<...> class Map {
public:
    typedef ... value_type;
    typedef ... iterator;
    typedef ... const_iterator;

    typedef size_t size_type;
    typedef ptrdiff_t difference_type;

    Map(const Freeze::ConnectionPtr& connection, 
        const std::string& dbName,
        bool createDb = true,
        const Compare& compare = Compare());

    template<class _InputIterator>
    Map(const Freeze::ConnectionPtr& connection, 
        const std::string& dbName, 
        bool createDb,
        _InputIterator first, _InputIterator last,
        const Compare& compare = Compare());

    static void recreate(const Freeze::ConnectionPtr& connection,
                         const std::string& dbName,
                         const Compare& compare = Compare());

    bool operator==(const Map& rhs) const;
    bool operator!=(const Map& rhs) const;

    void swap(Map& rhs);

    iterator begin();
    const_iterator begin() const;

    iterator end();
    const_iterator end() const;

    bool empty() const;
    size_type size() const;
    size_type max_size() const;

    iterator insert(iterator /*position*/,
                    const value_type& elem);

    std::pair<iterator, bool> insert(const value_type& elem);

    template <typename InputIterator>
    void insert(InputIterator first, InputIterator last);

    void put(const value_type& elem);

    template <typename InputIterator>
    void put(InputIterator first, InputIterator last);

    void erase(iterator position);
    size_type erase(const key_type& key);
    void erase(iterator first, iterator last);

    void clear();

    void destroy(); // Nonstandard.

    iterator find(const key_type& key);
    const_iterator find(const key_type& key) const;

    size_type count(const key_type& key) const;

    iterator lower_bound(const key_type& key);
    const_iterator lower_bound(const key_type& key) const;
    iterator upper_bound(const key_type& key);
    const_iterator upper_bound(const key_type& key) const;

    std::pair<iterator, iterator>
    equal_range(const key_type& key);

    std::pair<const_iterator, const_iterator> 
    equal_range(const key_type& key) const;

    const Ice::CommunicatorPtr&
    communicator() const;

    ...
};
}
The semantics of the Freeze::Map methods are identical to those of std::map unless otherwise noted. In particular, the overloaded insert method shown below ignores the position argument:
iterator insert(iterator /*position*/,
                const value_type& elem);
A Freeze map class supports only those methods shown above; other features of std::map, such as allocators and overloaded array operators, are not available.
Non-standard methods that are specific to Freeze maps are discussed below:
• Constructors
The following overloaded constructors are provided:
Map(const Freeze::ConnectionPtr& connection, 
    const std::string& dbName,
    bool createDb = true,
    const Compare& compare = Compare());

template<class _InputIterator>
Map(const Freeze::ConnectionPtr& connection, 
    const std::string& dbName, 
    bool createDb,
    _InputIterator first, _InputIterator last,
    const Compare& compare = Compare());
The first constructor accepts a connection, the database name, a flag indicating whether to create the database if it does not exist, and an object used to compare keys. The second constructor accepts all of the parameters of the first, with the addition of iterators from which elements are copied into the map.
Note that a database can only contain the persistent state of one map type. Any attempt to instantiate maps of different types on the same database results in undefined behavior.
• Map copy
The recreate function copies an existing database:
static void recreate(const Freeze::ConnectionPtr& connection,
                     const std::string& dbName,
                     const Compare& compare = Compare())
The dbName parameter specifies an existing database name. The copy has the name<dbName>.old-<uuid>. For example, if the database name is MyDB, the copy might be named
MyDB.old-edefd55a-e66a-478d-a77b-f6d53292b873. (Obviously, a different UUID is used each time you recreate a database).
• destroy
This method deletes the database from its environment and from the Freeze catalog (see Section 36.7). If a transaction is not currently open, the method creates its own transaction in which to perform this task.
• communicator
This method returns the communicator with which the map’s connection is associated.

Iterators

A Freeze map’s iterator works like its counterpart in std::map. The iterator class supports one convenient (but nonstandard) method:
void set(const mapped_type& value)
Using this method, a program can replace the value at the iterator’s current position.

Sample Program

The program below demonstrates how to use a StringIntMap to store <stringint> pairs in a database. You will notice that there are no explicit read or write operations called by the program; instead, simply using the map has the side effect of accessing the database.
#include <Freeze/Freeze.h>
#include <StringIntMap.h>

int
main(int argc, char* argv[])
{
    // Initialize the Communicator.
    //
    Ice::CommunicatorPtr communicator =
        Ice::initialize(argc, argv);

    // Create a Freeze database connection.
    //
    Freeze::ConnectionPtr connection =
        Freeze::createConnection(communicator, "db");

    // Instantiate the map.
    //
    StringIntMap map(connection, "simple");

    // Clear the map.
    //
    map.clear();

    Ice::Int i;
    StringIntMap::iterator p;

    // Populate the map.
    //
    for (i = 0; i < 26; i++) {
        std::string key(1, 'a' + i);
        map.insert(make_pair(key, i));
    }

    // Iterate over the map and change the values.
    //
    for (p = map.begin(); p != map.end(); ++p)
        p.set(p>second + 1);

    // Find and erase the last element.
    //
    p = map.find("z");
    assert(p != map.end());
    map.erase(p);

    // Clean up.
    //
    connection>close();
    communicator>destroy();

    return 0;
}
Prior to instantiating a Freeze map, the application must connect to a Berkeley DB database environment:
Freeze::ConnectionPtr connection =
    Freeze::createConnection(communicator, "db");
The second argument is the name of a Berkeley DB database environment; by default, this is also the file system directory in which Berkeley DB creates all database and administrative files.
Next, the code instantiates the StringIntMap on the connection. The constructor’s second argument supplies the name of the database file, which by default is created if it does not exist:
StringIntMap map(connection, "simple");
After instantiating the map, we clear it to make sure it is empty in case the program is run more than once:
map.clear();
Next, we populate the map using a single-character string as the key:
for (i = 0; i < 26; i++) {
    std::string key(1, 'a' + i);
    map.insert(make_pair(key, i));
}
Iterating over the map will look familiar to std::map users. However, to modify a value at the iterator’s current position, we use the nonstandard set method:
for (p = map.begin(); p != map.end(); ++p)
    p.set(p‑>second + 1);
Next, the program obtains an iterator positioned at the element with key z, and erases it:
p = map.find("z");
assert(p != map.end());
map.erase(p);
Finally, the program closes the database connection:
connection‑>close();
It is not necessary to explicitly close the database connection, but we demonstrate it here for the sake of completeness.

36.3.8 Using a Freeze Map in Java

This section describes the code generator and demonstrates how to use a Freeze map in a Java program.

slice2freezej Command-Line Options

The Slice-to-Freeze compiler, slice2freezej, creates Java classes for Freeze maps. The compiler offers the following command-line options in addition to the standard options described in Section 4.19:
• dict NAME,KEY,VALUE
Generate a Freeze map class named NAME using KEY as key and VALUE as value. This option may be specified multiple times to generate several Freeze maps. NAME may be a scoped Java name, such as Demo.Struct1ObjectMap.
• dictindex MAP[,MEMBER]
[,casesensitive|caseinsensitive]
Add an index to the Freeze map named MAP. If MEMBER is specified, the map value type must be a structure or a class, and MEMBER must be a member of that type. If MEMBER is not specified, the entire value is indexed. When the indexed member (or entire value) is a string, the index can be case-sensitive (default) or case-insensitive.
An index adds three methods to the generated Java map:
public Freeze.Map.EntryIterator
            findByMEMBER(MEMBER_TYPE index);
public Freeze.Map.EntryIterator
            findByMEMBER(MEMBER_TYPE index,
                         boolean onlyDups;
public int MEMBERCount(MEMBER_TYPE index);
When MEMBER is not specified, these functions are findbyValue and valueCount. When MEMBER is specified, its first letter is capitalized in the findBy function name. MEMBER_TYPE corresponds to an in-parameter of the type of MEMBER (or the type of the value when MEMBER is not specified). For example, if MEMBER is a string, MEMBER_TYPE is a java.lang.String.
When MEMBER is not specified, these functions are findbyValue and valueCount.
By default, index keys are sorted using their binary Ice-encoded representation. You can choose a different order by providing a comparator for this index to the constructor of your Freeze map. For example:
java.util.Comparator myIndexKeyComparator = ...;
String myIndexName = ...;
java.util.Map indexComparators = new java.util.HashMap();
indexComparators.put(myIndexName, myIndexKeyComparator);
MyIndexedFreezeMap map =
    new MyIndexedFreezeMap(connection, dbName, true,
                           myMainKeyComparator,
                           indexComparators);
findByMEMBER returns an iterator over elements of the Freeze map starting with an element with whose index value matches the given index value. If there is no such element, the returned iterator is empty (hasNext always returns false). When the second parameter is true (or is not provided), the returned iterator provides only "duplicate" elements, that is, elements with the very same index value. Otherwise, the iterator sets a starting position in the map, and then provides elements until the end of the map, sorted according to the index comparator.
MEMBERCount returns the number of elements in the Freeze map whose index-value matches the given index value.
• index CLASS,TYPE,MEMBER
[,casesensitive|caseinsensitive]
Generate an index class for a Freeze evictor (see Section 36.5.7). CLASS is the name of the index class to be generated. TYPE denotes the type of class to be indexed (objects of different classes are not included in this index). MEMBER is the name of the data member in TYPE to index. When MEMBER has type string, it is possible to specify whether the index is case-sensitive or not. The default is case-sensitive.
• meta META
Define the global metadata directive META. Using this option is equivalent to defining the global metadata META in each named Slice file, as well as in any file included by a named Slice file. See Section 10.15 for more information.

Generating a Simple Map

As an example, the following command generates a simple map:
$ slice2freezej dict StringIntMap,string,int
This command directs the compiler to create a map named StringIntMap, with the Slice key type string and the Slice value type int. The compiler produces one Java source file: StringIntMap.java.

The Map Class

If you examine the contents of the source file created by the example in the previous section, you will discover that a Freeze map is a subclass of Freeze.Map:
public class StringIntMap extends Freeze.Map {
    public StringIntMap(Freeze.Connection connection,
                        String dbName,
                        boolean createDb,
                        java.util.Comparator comparator);

    public StringIntMap(Freeze.Connection connection,
                        String dbName,
                        boolean createDb);

    public StringIntMap(Freeze.Connection connection,
                        String dbName);
}
The class defines several overloaded constructors whose arguments are described below:
• connection
The Freeze connection object (see Section 36.3.1).
• dbName
The name of the database in which to store this map’s persistent state. Note that a database can only contain the persistent state of one map type. Any attempt to instantiate maps of different types on the same database results in undefined behavior.
• createDb
A flag indicating whether the map should create the database if it does not already exist. If this argument is not specified, the default value is true.
• comparator
An object used to compare the map’s keys. If this argument is not specified, the default behavior compares the encoded form of the keys.
The map’s base class, Freeze.Map, implements standard Java interfaces and provides nonstandard methods that improve efficiency and support database-oriented features such as indexes:
package Freeze;

public abstract class Map extends java.util.AbstractMap 
    implements java.util.SortedMap ... {
    //
    // Methods from java.util.AbstractMap and java.util.SortedMap.
    ...

    //
    // Nonstandard methods.

    public java.util.SortedMap headMapForIndex(String indexName,
                                               Object toKey);
    public java.util.SortedMap tailMapForIndex(String indexName,
                                               Object fromKey);
    public java.util.SortedMap subMapForIndex(String indexName,
                                              Object fromKey,
                                              Object toKey);
    public java.util.SortedMap mapForIndex(String indexName);

    public void fastPut(Object key, Object value);
    public boolean fastRemove(Object key);

    public void closeAllIterators();

    public void close();
}
For the sake of brevity, we have omitted the methods inherited from java.util.AbstractMap and java.util.SortedMap; refer to the JDK documentation for more details.
Several methods are provided that support Freeze indexes. These methods mirror the semantics of the methods from java.util.SortedMap, except that a particular index is selected for ordering purposes. (mapForIndex returns a map for then entire index).
The fastPut and fastRemove methods are more efficient versions of the standard put and remove, respectively. In contrast to the standard methods, fastPut and fastRemove do not return the prior value associated with the key, thereby eliminating the overhead of reading and decoding it. The boolean value returned by fastRemove indicates whether an element was found with the given key.
As its name implies, the closeAllIterators method ensures that all outstanding iterators are closed. We discuss iterators in more depth in the next section.
Finally, the close method closes all outstanding iterators and then closes the database. A Freeze map is also closed implicitly when the object is finalized, but given the uncertain nature of Java’s garbage collection facility, we recommend explicitly closing a map when it is no longer needed.

Iterators

You can iterate over a Freeze map just as you can with any container that implements the java.util.Map interface. For example, the code below displays the key and value of each element:
StringIntMap m = ...;
java.util.Iterator i = m.entrySet().iterator();
while (i.hasNext()) {
    java.util.Map.Entry e = (java.util.Map.Entry)i.next();
    System.out.println("Key: " + e.getKey());
    System.out.println("Value: " + e.getValue());
}
When an iterator is no longer necessary, a program should explicitly close it. (An iterator that is garbage collected without being closed emits a warning message.) To close an iterator, you must cast it to a Freeze-specific type, as shown below:
((Freeze.Map.EntryIterator)i).close();

Sample Program

The program below demonstrates how to use a StringIntMap to store <stringint> pairs in a database. You will notice that there are no explicit read or write operations called by the program; instead, simply using the map has the side effect of accessing the database.
public class Client
{
    public static void
    main(String[] args)
    {
        // Initialize the Communicator.
        //
        Ice.Communicator communicator = Ice.Util.initialize(args);

        // Create a Freeze database connection.
        //
        Freeze.Connection connection =
            Freeze.Util.createConnection(communicator, "db");

        // Instantiate the map.
        //
        StringIntMap map = 
            new StringIntMap(connection, "simple", true);

        // Clear the map.
        //
        map.clear();

        int i;
        java.util.Iterator p;

        // Populate the map.
        //
        for (i = 0; i < 26; i++) {
            final char[] ch = { (char)('a' + i) };
            map.put(new String(ch), new Integer(i));
        }

        // Iterate over the map and change the values.
        //
        p = map.entrySet().iterator();
        while (p.hasNext()) {
            java.util.Map.Entry e = (java.util.Map.Entry)p.next();
            Integer in = (Integer)e.getValue();
            e.setValue(new Integer(in.intValue() + 1));
        }

        // Find and erase the last element.
        //
        boolean b;
        b = map.containsKey("z");
        assert(b);
        b = map.fastRemove("z");
        assert(b);

        // Clean up.
        //
        map.close();
        connection.close();
        communicator.destroy();

        System.exit(0);
    }
}
Prior to instantiating a Freeze map, the application must connect to a Berkeley DB database environment:
Freeze.Connection connection =
    Freeze.Util.createConnection(communicator, "db");
The second argument is the name of a Berkeley DB database environment; by default, this is also the file system directory in which Berkeley DB creates all database and administrative files.
Next, the code instantiates the StringIntMap on the connection. The constructor’s second argument supplies the name of the database file, and the third argument indicates that the database should be created if it does not exist:
StringIntMap map = new StringIntMap(connection, "simple", true);
After instantiating the map, we clear it to make sure it is empty in case the program is run more than once:
map.clear();
We populate the map, using a single-character string as the key. As with java.util.Map, the key and value types must be Java objects.
for (i = 0; i < 26; i++) {
    final char[] ch = { (char)('a' + i) };
    map.put(new String(ch), new Integer(i));
}
Iterating over the map is no different from iterating over any other map that implements the java.util.Map interface:
p = map.entrySet().iterator();
while (p.hasNext()) {
    java.util.Map.Entry e =
        (java.util.Map.Entry)p.next();
    Integer in = (Integer)e.getValue();
    e.setValue(new Integer(in.intValue() + 1));
}
Next, the program verifies that an element exists with key z, and then removes it using fastRemove:
b = map.containsKey("z");
assert(b);
b = map.fastRemove("z");
assert(b);
Finally, the program closes the map and its connection.
map.close();
connection.close();
Table of Contents Previous Next
Logo