26.09.2022
Lukas Plastinkin

Progress OpenEdge: Why Collections Are Useful in Progress OpenEdge Development

Progress OpenEdge already had a limited implementation of Collections with OpenEdge.Core.Collections package. At the beginning of 2022, Progress OpenEdge version 12.5 was released, which came along with a built-in list collection construct that any ABL application can use.  

In this article, we explain what collections are in Progress OpenEdge and how the Progress.Collections package (introduced in version 12.5) improves application development.

What is a Collection?  

A collection is a class that represents a group of objects of the same type.

You can think of it as similar to a temp-table, but instead of storing records, it stores objects. Because a collection is an object itself, it also provides methods for adding, removing, and iterating over elements.

For Progress OpenEdge version 12.5, the 'List' type collection has been implemented by adding six built-in classes and interfaces:  

  • Progress.Collections.ICollection<T> interface
  • Progress.Collections.IIterable<T> interface
  • Progress.Collections.IIterator<T> interface
  • Progress.Collections.IList<T> interface
  • Progress.Collections.List<T> class
  • Progress.Collections.ListIterator<T> class  

Another feature in tandem with these new classes is the new syntax where type parameters are defined within angle brackets (<T> as seen above). This feature is used to describe generic types.

Why are Collections Useful in Object-Oriented Applications?

Features that will benefit you while creating a list in your code:

  • Simplified process. Collections eliminate the need to use temp-tables or write your data structures, reducing developer effort and code bloat;
  • Familiar code. With Collections, you can simplify the code syntax, which will be immediately familiar to those who are used to writing object-oriented code;
  • Easy-to-use with other technologies. Collections can also be used to serialise your data structures, allowing for easy interoperability between Progress OpenEdge and other technologies. Serialisation is an object's conversion into a) a stream of bytes to be stored in memory; b) on a file to be recreated elsewhere when needed. If your application is split into multiple parts with different technologies, Collections can significantly reduce the effort required to set up data transfer between those other parts.

Explore the Code

Below you will find a code snippet that shows the basics of working with 'List' type Collections. It includes creating the list, adding objects, and using the 'Iterator' class to iterate through stored objects.  

The code snippet below is a great starting point for you to try using Collections in Progress OpenEdge. Learn more about Collections and other features added with version 12.5 by reading release notes about the Collections in object-oriented ABL.

BLOCK-LEVEL ON ERROR UNDO, THROW.  

VAR Progress.Collections.List<Order> orderList.  
VAR Progress.Collections.IIterator<Order> orderIterator.  
VAR order AnyOrder.    

// Create the list  
orderList = NEW Progress.Collections.List<Order>().  

FOR EACH Order NO-LOCK WHERE  
        Order.SalesRep = "HXM":  
   // Add elements to the list  
   orderList:Add(NEW Order(Order.OrderNum)).  
END.  

// Retrieve the first element from the list  
AnyOrder = orderList:Get(1).  

// Replace the first element in the list  
orderList:Set(2, NEW Order(10000)).  

// Remove the second element from the list  
orderList:RemoveAt(3). 

// Print out some information  
MESSAGE 
   "orderList info"  
   orderList:Count  
   orderList:Contains(AnyOrder)  
   orderList:IndexOf(AnyOrder)  
   orderList:Get(4):orderStatus  
   .  

// Iterate over the entries in the list  
orderIterator = orderList:GetIterator().  
REPEAT WHILE orderIterator:MoveNext():  
   orderIterator:Current:calcItems().  
   orderIterator:Current:calcTotalPrice().  
END.  

DEFINE VARIABLE myFileOutStream AS Progress.IO.FileOutputStream.  
DEFINE VARIABLE mySerializer AS Progress.IO.JsonSerializer.    

mySerializer = NEW Progress.IO.JsonSerializer(FALSE).    

/* Serialize object */  
myFileOutStream = NEW Progress.IO.FileOutputStream("OrderList.json").   

mySerializer:Serialize(orderList, myFileOutStream).  
myFileOutStream:Close(). 

/* Also, possible to serialise to memptr */  
DEFINE VARIABLE memptrVar AS MEMPTR NO-UNDO.  
DEFINE VARIABLE myMemoryOutStream AS Progress.IO.MemoryOutputStream.    

myMemoryOutStream = NEW Progress.IO.MemoryOutputStream().  
mySerializer:Serialize(orderList, myMemoryOutStream).  
memptrVar = myMemoryOutStream:Data.   

Performance Testing: Temp-Table VS Collection

We did limited performance testing of temp-table versus collection performance.  

Tests were performed on personal workstations on both Windows and Linux operating systems. Some tests involved reading large amounts of data from CSV files, adding them to temp-tables/lists, and performing operations with these data structures. The duration of each process was measured in milliseconds.  

Overall, the performance was found to be comparable. However, in some cases, there were noticeable disparities in performance between Linux and Windows operating systems. Unfortunately, with the limited scope of testing, we cannot say whether this is an optimisation problem, some bug, a quirk of the operating system, or the hardware.  

Worth mentioning as well is that there is a bug in version 12.5, which limits the maximum number of objects in the list collection (in our case, this was ~4105 objects). This has been fixed in version 12.5.1.  

Here is a table showing the conducted test:

Description Type of test / measuring metric Temp-table on Windows List on Windows Temp-table on Linux List on Linux
Test group 1
Transferring data from a large file with 4k+ lines and 250+ columns into a single layer objects list Creating records: ‘create ttDwhLoan / thelist:Add(new Loan’ ~35ms ~1700ms ~23ms ~90ms
Importing data using ‘import delimiter "#" ~305ms - ~260ms -
Creating records and assigning values from entries ~1130ms ~2680ms ~700ms ~670ms
Iterating until value matched with a line at index around 3k ~23ms ~13ms ~7ms ~8ms
Test group 2
Serialisation and deserialisation of lists created in previous tests Serialisation into .bin type file - ~400ms + ~1200ms - ~170ms + ~200 ms
Serialisation into .json type file - ~1200ms + ~2700ms - ~980ms + ~820ms
Working with sports2020.Order and sports2020.OrderLine, creating two hierarchy layers (many-to-many 18k+ total records) Filling two temp-tables/objects ~250ms ~4300ms ~205ms ~510ms
Finding a specific field value for a specific OrderLine w/o temp-table index ~2ms ~7ms ~1ms ~5ms
Test group 3
Passing 4k line data to a procedure 20 times (not using "by-reference") Total call chain duration ‘input table / input thelist’ ~1000ms ~4ms ~400ms ~1ms
Passing 30k line data to a procedure 20 times (not using "by-reference") Memory usage ‘input table / input oList’ Less than 1 MB increase Less than 1 MB increase Less than 1 MB increase No measurable increase

 

Insights After Testing

The performance testing showed three conclusions:

  • Test group 1: Temp-tables can be more efficient when reading large text files and in certain cases in Windows, but overall, collection performs well;
  • Test group 2: Temp-tables can have a performance advantage working with many records. Data finding performance is quite good with objects considering full scan;
  • Test group 3: Collections should have a significant advantage if a temp-table is not passed by reference.

If you need help with Progress OpenEdge application development, the Baltic Amadeus team is ready to answer your questions.

Let’s talk about your project

Starting something new or need support for an existing project? Reach out, and our experts will get back to you within one business day.

Start the conversation

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.