This chapter introduces the frameworks used by the collection classes in the Base Class Library. Collection frameworks are mainly concerned with the communication between collections and the elements they store.
All the code shown in this chapter uses the Micro Focus alternatives to the ISO 2002 syntax.
The COBOL collection classes assume that their elements are objects. A collection sends messages to its elements to get information such as the element's size and content. Even when you store intrinsic data (as opposed to objects), the collection uses the methods implemented in the intrinsic classes and uses the INVOKE ...AS verb to send messages to the data.
The elements stored in a collection need to include in their interface the methods to respond to those messages. Default versions of most of these are provided in the Base class, but you may want to override these to provide different or more efficient behavior for particular types of object.
You will also need to provide methods which conform to a particular interface for the collection class sort and iterator methods.
The different types of collection in the Base Class Library can all be categorized by the following properties:
You can access any particular element in an indexed collection by giving its position. This is similar to using a conventional array. In non-indexed collections, elements are not stored in a defined order.
Automatically growable collections get bigger when you exceed the capacity for which you created the collection. A manually growable collection only gets bigger when you send it the "grow" message.
Some collections disallow duplicate elements; trying to add an element which has the same value as one already in the collection will cause an exception.
Here are some of the collection classes available, with their properties:
Bag | Non-indexed, automatically growable, duplicates allowed |
Array | Indexed, manually growable, duplicates allowed |
CharacterArray | Indexed, manually growable, duplicates allowed |
OrderedCollection | Indexed by insertion order, automatically growable, duplicates allowed |
SortedCollection | Indexed by sort order, automatically growable, duplicates allowed |
ValueSet | Non-indexed, automatically growable, duplicate values disallowed |
IdentitySet | Non-indexed, automatically growable, duplicate object handles disallowed |
Dictionary | Indexed by key, automatically growable, duplicate key values disallowed |
IdentityDictionary | Indexed by key, automatically growable, duplicate key object handles disallowed |
ValueSet and IdentitySet differ only in the way they determine duplicate elements. ValueSets compare the values of elements and disallows duplicate values; IdentitySets compare object references, and disallow storing the same object more than once.
Dictionary and IdentityDictionary are special types of collection which store key/data pairs. They determine duplicate keys in the same way that ValueSet and IdentitySet determine duplicate elements.
You can create a collection to store either objects (a collection of references) or intrinsic data (a collection of values). A collection of references can contain more than one sort of object, whereas a collection of values is initialized for storing data of a particular type and length.
Before you create a collection, you need to decide which type to create. You need to decide:
The available types of collection cover most of the combinations above.
To create a collection of references, send the "ofReferences" message to the class of collection you want to create passing the initial size and template as parameters.
To create a collection of values:
Many of the collection classes have additional create methods. For further information see the Class Library Reference.
Dictionaries are a special sort of indexed collection, which store key-data pairs (known as associations). In a dictionary, the key is used as the index when you store or retrieve the data. Dictionaries do not allow you to store duplicate keys.
Like the other collection types, you can store either objects or intrinsic data in a dictionary. In a dictionary though, either the key or the data part can be an intrinsic or an object. This gives you the following possible combinations for intrinsic or object storage:
If the keys are objects, they must provide methods for hashing and equality comparisons. Objects like CharacterArray already provide these methods, but if you are using your own types of objects as keys, you need to provide your own mechanisms.
When you create a dictionary you have to give it a template so that it knows how the key and data portions are to be stored. The template is either the Association class, or a clone of the Association class.
The Association class is another clonable class, like the classes for intrinsic data classes, used for creating templates for data storage. An Association template actually consists of two templates; one for the key and one for the data. To create any type of dictionary you need to create an Association template.
Having created a template for your dictionary object, there are two methods you can use to create a dictionary itself; "ofValues" or "ofAssociations". A dictionary "ofValues" stores each element as a key data pair. A dictionary "ofAssociations" stores each element as an instance of the Association template you used to create the dictionary.
A dictionary "ofAssociations" in effect stores three items for every entry in the dictionary:
An object that contains the key and data
This could be an object or an intrinsic value
This could be an object or an intrinsic value
A dictionary "ofValues" stores the key and data directly without wrapping them inside an association object. When would you use a dictionary "ofAssociations" and when would you use a dictionary "ofValues"?
A dictionary "ofValues" is slightly more efficient at run time in terms of speed and memory if you simply want to store data items against key items. However, if your application uses associations elsewhere to manage key/data pairs, a dictionary "ofAssociations" is a better choice as the actual dictionary only stores the object handles to the associations, and you don't have to extract the key and value from the association before putting it in the dictionary.
The following example shows the creation of a dictionary with an intrinsic key and data, and the creation of the templates.
00001 local-storage section. 00002 01 aKeyTemplate object reference. 00003 01 aDataTemplate object reference. 00004 01 anAssocTemplate object reference. 00005 01 aDictionary object reference. 00006 01 aLength pic x(4) comp-5. ... 00020 procedure division. ... 00028 move 3 to aLength 00029 invoke CobolCompX "newClass" using aLength 00030 returning aKeyTemplate 00031 move 20 to aLength 00032 invoke CobolPicX "newClass" using aLength 00033 returning aDataTemplate 00034 invoke Association "newClass using aKeyTemplate 00035 aDataTemplate 00036 returning anAssocTemplate 00037 invoke Dictionary "ofValues" using anAssocTemplate 00038 aLength 00039 returning aDictionary
Lines 1-6 | Declares storage for the templates.. |
Lines 28-29 | Creates a template for a PIC X(3) COMP-X numeric key. |
Line 31-32 | Creates a template for a PIC X(20) data portion. |
Line 34 | Creates an association template. |
Line 37 | Creates a dictionary of values with an initial size of 20 elements. This will grow to accommodate extra elements once 20 elements have been added. Each grow operation approximately doubles the size of the dictionary. |
A CharacterArray is an object that contains a string of any length. CharacterArrays are a convenient way of passing strings as parameters when sending messages or making calls.
There are many ways to create a CharacterArray. The two most convenient are listed below.
invoke CharacterArray "withValue" using zstring returning aCharArray
invoke CharacterArray "withLengthValue" using aLength aLiteral returning aCharArra
where:
zString | Any null-terminated string |
aCharArray | Object reference |
aLiteral | Any COBOL literal, or data item containing one. |
aLength PIC X(4) COMP-5. | Length in characters of aLiteral. |
There are many ways to get data from a CharacterArray object. Three are given below:
invoke aCharArray "size" returning aLength
invoke aCharArray "getValue" returning aValue
invoke aCharArray "asParameter" returning aValue
where:
aCharArray | Object reference; contains the handle to the CharacterArray object |
aLength PIC X(4) COMP-5. | Length in characters of the CharacterArray object |
aValue PIC X(n) | Value of the CharacterArray object. It is your
responsibility to see that aValue is long enough to hold the result. The "asParameter" method returns a string one byte longer than the result from the "getValue" method, to allow for the null at the end. |
Collections carry out two types of comparison on elements:
A collection compares two elements by sending the "equal" message to one element, giving it the other element as a parameter. The default "equal" method in Base simply compares the object references of the two objects (that is, they are equal only if they are the same object).
For many applications you will want to compare part or all of the element's instance data to determine equality. Implement the interface to "equal" as follows:
method-id. "equal". linkage section. 01 lnkElement object reference. 01 lnkEqualityResult pic x comp-5. 88 isEqual value 1. 88 isNotEqual value 0. procedure division using lnkElement returning lnkEqualityResult. * Code to compare lnkElement to self and return * lnkEqualityResult. exit method. end method "equal".
The default sort method of a SortedCollection instance sends the "lessThanOrEqual" message (if it is a collection "ofReferences") or the "lessThanOrEqualByLengthValue" message (if it is a collection "ofValues") to all the elements in the collection. There is no default method for this in Base, although it is implemented by the Intrinsic classes and by CharacterArray.
You may need to implement this method so that two objects can compare themselves using part of their instance data. Use the following interface to implement the "lessThanOrEqual" method:
method-id. "lessThanOrEqual". ... linkage section. 01 lnkString object reference. 01 lnkResult pic x comp-5. 88 isEqual value 1. 88 isNotEqual value 0.
procedure division using lnkString returning lnkResult. * Code to compare lnkElement to self and return a result exit method. end method "lessThanOrEqual".
Use the following interface to implement the "lessThanOrEqualbyLengthValue" method:
method-id. "lessThanOrEqualbyLengthValue". linkage section. 01 lnkElement object reference. 01 lnkSize pic x(4) comp-5. 01 lnkResult pic x comp-5. 88 isLessThanOrEqual value 1. 88 isNotLessThanorEqual value 0. procedure division using lnkElement lnkSize returning lnkResult. * Code to compare lsElement to self and return * isNotLessThanOrEqual if lsElement is greater than * self. The lsSize parameter is the value returned * by element in response to the "size" message. exit method. end method "lessThanOrEqualbyLengthValue".
The dictionary and set classes use hash values to store and retrieve elements. The default "hash" mechanism in Base returns the object reference to the object.
You will often need to override the default with a hash mechanism which creates a hash value using the object's instance data. There are two reasons for this:
Implement the interface to "hash" as follows:
method-id. "hash". linkage section. 01 lnkHashValue pic s9(9) comp-5. procedure division returning lnkHashValue. * Code to return a hashvalue based on object's attributes. exit method. end method "hash".
Separate objects that have the same value must always return identical results from "hash". Hash values do not have to be unique for each element, but the more duplicate hash values a dictionary contains, the less efficient its storage and retrieval of elements. Hash values should always be positive numbers.
There are two mechanisms you can use to display the contents of a collection. You can send the message "display" to the collection or you can display the collection on a Listbox.
If you send the "display" message to a collection, it displays on the console all the elements of the collection between parentheses "()". To do this, it sends the message "display" to every element in the collection. The default "display" method in Base displays a description of the object. For example, if you had a CheckAccount object, the default "display" method would show:
an instance of the class checkaccount
CharacterArray and the COBOL intrinsic classes implement their own "display" methods that display their contents. You can implement "display" in your own objects using the following interface:
method-id. "display". ... procedure division. * Code to display self on the console. exit method. end method "display".
You can display a collection of CharacterArrays or intrinsic data by passing it to a Listbox with the "setContents" message. The Listbox displays each element of the collection as a string on a separate line.
Instances of SortedCollection sort elements into order as they are added. The default sorting mechanism provided sends the "lessThanOrEqual" message (if a collection "ofReferences") or the "lessThanOrEqualbyLengthValue" message (if a collection "ofValues") to elements to compare their relative values (see the section Relative Value of Objects).
You can set your own sort method for an instance of SortedCollection. The method you provide must compare two elements and return a value indicating which element should appear before the other.
To override the default sort method you must create a Callback for your method and pass the Callback to the SortedCollection.
Implement the interface to your sort method as follows:
method-id. "sortMethod". linkage section. 01 lnkElement1 object reference. 01 lnkElement2 object reference. 01 lnkResult pic x comp-5. 88 element1first value 1. 88 element2first value 0. procedure division using lsElement1 lsElement2 returning lnkResult. * Code to compare lsElement1 to lsElement2 and set * lnkResult to determine which element should precede * the other. exit method. end method "sortMethod".
The collection classes implement four iterator methods which enable you to access all the elements of your collection by sending a single message to the collection. To use any of the iterator methods you need to provide a Callback object. The Callback is passed each element of the collection as a parameter in turn.
This section shows you the interface the method in the Callback must have to work with the iterator methods. The iterator methods provided are:
Implement the interface for a callback method for "do" as follows:
method-id. "doMethod". linkage section. 01 lnkElement object reference. procedure division using lnkElement. * Any code to process the element. exit program. end method "doMethod".
Implement the interface for a callback method for "select" as follows:
method-id. "selectMethod". linkage section. 01 lnkElement object reference. 01 lnkselectResult pic x comp-5. 88 selectThisElement value 1. 88 dontSelectElement value 0. procedure division using lnkElement returning lnkSelectResult. * Code to determine whether lnkElement should be included in * the new subcollection. exit method. end method "selectMethod".
Implement the interface for a callback method for "reject" as follows:
method-id. "rejectMethod". linkage section. 01 lnkElement object reference. 01 lnkRejectResult pic x comp-5. 88 rejectThisElement value 1. 88 dontRejectElement value 0. procedure division using lnkElement returning lnkRejectResult. * Code to determine whether lnkElement should be included in * the new subcollection. exit method. end method "rejectMethod".
Implement the interface for a callback method for "collect" as follows:
method-id. "collectMethod". linkage section. 01 lnkElement object reference. 01 lnkReturnElement object reference. procedure division using lnkElement returning lnkReturnElement. * Code to return an element for the new collection. exit method. end method "collectMethod".
Copyright © 2004 Micro Focus International Limited. All rights reserved.