Welcome to the wonderful world of Java Streams ported for the CFML world!The beauty of streams is that the elements in a stream are processed and passed across the processing pipeline. Unlike traditional CFML functions like map(), reduce() and filter() which create completely new collections until all items in the pipeline are processed. With streams, the elements are streamed across the pipeline to increase efficiency and performance.
3. WHO AM I?
• Luis Majano
• Computer Engineer
• Born in El Salvador ->Texas
• CEO of Ortus Solutions
• Sandals -> ESRI -> Ortus
@lmajano
@ortussolutions
4. What are Java Streams
What is CBStreams
Imperative vs Functional Programming
Building Streams
Using Streams
Collecting Streams
5. What are Java Streams
• Introduced in JDK 8+
• Not I/O Streams
• A data abstraction layer
• Does not store any data, it wraps the data
• Designed to process streams of data elements
• map(), reduce(), filter(), collect()
• Enables functional-style operations on such elements
https://www.oracle.com/technetwork/articles/java/ma14-java-se-8-streams-2177646.html
6. What is CBStreams
• Port of Java 8+ Streams to CFML Land!
• 90% of all Java functionality is there
• Plus some CFML Dynamic Goodness
• Box Module (ColdBox, CommandBox, etc)
https://forgebox.io/view/cbstreams
install cbstreams
8. Imperative Programming
• Major OO languages are imperative (C,++,C#, Java)
• Follow a top-down or procedural design to reach a goal
• Each statement changes the state (side-effect) of the program
• Each statement tells the computer what to change and in what order
• Always cons and pros
function isPrime( number ) {
for( var i = 2; i <= sqr( number ); i++) {
if(number % i == 0) return false;
}
return number > 1;
}
isPrime(9220000000000000039) // Output: true
9. Functional Programming
• Declarative programming
• We tell the computer what things, actions, etc are
• Runtime determines the best way how to do it
• Functions are first class citizens
• No side-effect or iterating state to worry about
• Always cons and pros
function isPrime(number) {
return number > 1 &&
stream
.rangeClosed( 2, sqr( number ) )
.noneMatch( index => number % index == 0 );
}
isPrime( 9220000000000000039 ) // Output: true
12. Streams Functional Heaven!
• All about functional programming
• Heavy Lambda/Closure usage
• Must focus on the what and not on the how!
• Create a data processing pipeline
• Not for everything, choose wisely….
You have been warned!
13. Streams Functional Heaven!
var errors = [];
var errorCount = 0;
var oFile = fileOpen( filename );
var thisLine = fileReadLine( oFile );
while( errorCount < 40 && !isNull( thisLine ) ){
if( line.startsWith( "ERROR" ) ){
errors.append( line );
errorCount++;
}
line = fileReadLine( oFile );
}
var errors = streamBuilder.ofFile( filePath )
.filter( line => line.startsWith( "ERROR" ) )
.limit( 40 )
.collect();
What if I
want to multi-
thread this?
.parallel()
14. What about CFML Functions?
• They are limited in input, scope & operations
• No short-circuiting operations
• No lazyness, they all fire top-down
• Each operation blocks until it finishes
processing ALL elements
• Creates new arrays/queries/structs for each
new concatenated operation
• What about infinite input or biiiiig files?
• map(), reduce(), each(), filter()
19. Lazy Example
var empIds = [ 1, 2, 3, 4 ];
var employee = streamBuilder.new( empIds )
// Convert ID's to Employee Objects, passing function reference
.map( employeeService.findByID )
// only valid employees
.filter( (employee) => !isNull( employee ) )
.filter( function( employee ){ return !isNull (employee); } )
// only salaries > 10000
.filter( (employee) => employee.getSalary() > 100000 )
// Find the first one
.findFirst()
// Return null
.orElse( null );
expect( employee.getSalary() ).toBe( 200000 );
• Stream performs the map and two filter operations, one element at a time.
• Since the salary of id 1 is not greater than 100000, the processing moves on to the next
element.
• Id 2 satisfies both of the filter predicates and hence the stream evaluates the terminal
operation findFirst() and returns the result.
• No operations are performed on id 3 and 4.
20. Let’s Get Started!
install cbstreams
StreamBuilder@cbstreams
• The StreamBuilder is injected where needed
• Helps you build streams out of native CFML data types
• Strings, Files, Arrays, Structs, Queries, Nulls
• Helps you build infinite or closure based streams
• You can strong type elements for the stream if needed
• For mathematical operations
• int, long, or double
21. Empty Streams
emptyStream = streamBuilder.new();
emptyStream = streamBuilder.new().empty();
• Simple way to build streams with no elements
• Useful? Maybe…
22. Building Custom Streams
builder = streamBuilder.builder();
myData.each( function( item ){
builder.add( item );
} );
myStream = builder.build();
stream = streamBuilder.new()
.of( "a", "hello", "stream" );
stream = streamBuilder.new()
.of( argumentCollection=myData );
• Two approaches:
• builder() - Add your own data via the add() method
• Of( arguments ) -Via an array of arguments
23. Streams of Characters
stream = streamBuilder.new().ofChars( "Welcome to Streams" );
• Stream of string characters
• Great for parsing, lookups, etc.
24. File Streams
stream = streamBuilder.new().ofFile( absolutePath );
try{
//work on the stream
} finally{
stream.close();
}
• Non Blocking I/O Classes
• Stream of file lines
• Throw any file size to it, I dare ya!
25. Generate Infinite Streams
// Generate 100 random numbers
stream = streamBuilder.new().generate( function(){
return randRange( 1, 100 );
} ).limit( 100 );
// Seeded iteration
stream = streamBuilder.new().iterate( 40, function( x ){
return x + 2;
} ).limit( 20 );
• Infinite streams of data
• Start with a seed or no seeded results
• Make sure you limit them or wait forever….
26. Ranged Streams
stream = streamBuilder.new().range( 1, 200 );
stream = streamBuilder.new().rangeClosed( 1, 2030 );
• Create open or closed ranges
• Similar to of() but a whole less typing
27. Intermediate Operations
• Remember, they are lazy, nothing gets done until a terminator is called.
• Result is always a stream
Operation Description
limit( maxSize ) Limit the stream processing
distinct() Return only distinct elements
skip( n ) Skip from the first element to n
sorted( comparator ) Sort a stream using a compactor closure
unordered() Return an unordered stream (default)
onClose( closeHandler ) Attach a listener to when the close operation is called
concat( stream1, stream2 ) Concatenates two streams together
peek( action ) Allows you to peek on the element in the order is called
Map( mapper ) Transform the elements into something else
filter( predicate ) Returns a new stream containing only the requested elements
parallel() Convert the stream to a parallel multi-threaded stream
28. Terminal Operations
• They kick off processing of elements sequentially or in parallel
Operation Description
iterator() Returns a java iterator
spliterator() Returns a java spliterator
close() Close the stream
toArray() Convert the stream back into an array
count() Count the elements in the stream
forEach( action ) Iterate through the elements calling the action closure
forEachOrdered( action ) Iterate through the elements calling the action closure in order, even in parallel
reduce( accumulator, identity ) Fold, reduces the stream to a single element.
max( comparator ) Returns the max value in the stream, if a comparator is passed its called for you
min( comparator ) Returns the min value in the stream, if a comparator is passed its called for you
average( comparator ) Returns the avg value in the stream, if a comparator is passed its called for you
summaryStatistics() Gives you a struct of stats containing: { min, max, count, sum, average }
29. Short-Circuit Operations
• Also terminal, but can short-circuit processing of the stream
Operation Description
findAny() Find any element in the stream
findFirst() Find the first element in the stream
anyMatch( predicate ) Returns a boolean that indicates if any of the elements match the predicate closure
allMatch( predicate ) Returns a boolean that indicates if ALL of the elements match the predicate closure
noneMatch( predicate ) Returns a boolean that indicates if none of the elements match the predicate closure
30. Collectors
• Finalizes the stream by converting it to concrete collections
• CBStreams auto-converts Java -> CFML DataTypes
Operation Description
collect() Return an array of the final elements
collectGroupingBy( classifier )
Build a final collection according to the classifier lambda/closure that will
classify the keys in the group. End result is usually a struct of data
collectAverage( mapper, primitive=long )
Collect an average according to the mapper function/closure and data
type passed
collectSum( mapper, primitive=long )
Collect a sum according to the mapper function/closure and data type
passed
collectSummary( mapper, primitive=long )
Collect a statistics struct according to the mapper function and data type
passed
collectAsList( delimiter=“,”, prefix, suffix )
Collect results into a string list with a delimiter and attached prefix and/or
suffix.
collectAsStruct( keyId, valueID, overwrite=true )
Collect the elements into a struct by leveraging the key identifier and the
value identifier from the stream of elements to pass into the collection.
collectPartitioningBy( predicate )
partitions the input elements according to a Predicate closure/lambda, and
organizes them into a Struct of <Boolean, array >.
32. CBStreams Optionals
• Most return values are not the actual values but a CFML Optional
• Wraps a Java Optional
• Simple functional value container instead of doing null checks, with some cool
functions
Operation Description
isPresent() Returns boolean if value is present
ifPresent( consumer ) If value is present call the consumer closure for you
filter( predicate ) If a value is present and the value matches the predicate then return another
Optional :)
map( mapper ) If a value is present, apply the mapping function and return another Optional
get() Get the value!
orElse( other ) Get the value or the `other` if the value is null
orElseGet( other ) Get the value or if not present call the other closure to return a value
hashCode() Unique hash code of the value
toString() Debugging
42. Still in infancy
Implement JDK 9-10 features
CFML Query Support
ORM Integration
ColdBox Core
Reactive Streams
Help me:
Lucee/Adobe Lambda -> Java lambda
Roadmap