4. Who am I?
● Software Consultant for Ortus Solutions
● Work with ColdBox, CommandBox, ContentBox every day
● Working with Coldfusion for 22 years
● Working with Javascript just as long
● Love learning and sharing the lessons learned
● From New Zealand, live in Bakersfield, Ca
● Loving wife, lots of kids, and countless critters
http://www.gpickin.com and @gpickin on twitter
http://www.ortussolutions.com
5. What are Java Streams
What is CBStreams
Imperative vs Functional Programming
Building Streams
Using Streams
Collecting Streams
6. 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
7. Dr. Venkat Subramaniam
• Streams Changed My Life!
• Devnexus Presentations
• Streams
• Completeable Futures
• Watch these videos
Lambdas + Streams Streams
8. What is CBStreams
• Port of Java 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
10. 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 a 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
11. 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
Functional Programming
13. 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!
14. 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()
15. What about CFML Functions?
• They are limited in input, scope & operations
• No short-circuiting operations
• No lazyness, they all fire top to bottom
• 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()
20. 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 ) )
// same as: .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.
21. 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
22. Empty Streams
emptyStream = streamBuilder.new();
emptyStream = streamBuilder.new().empty();
• Simple way to build streams with no elements
• Useful? Maybe…
23. 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
24. Streams of Characters
stream = streamBuilder.new().ofChars( "Welcome to Streams" );
• Stream of string characters
• Great for parsing, lookups, etc.
25. 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!
26. 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….
27. Ranged Streams
stream = streamBuilder.new().range( 1, 200 );
stream = streamBuilder.new().rangeClosed( 1, 2030 );
stream = streamBuilder.new().rangeClosed( 1, qUsers.recordcount );
• Create open or closed ranges
• Similar to of() but a whole less typing
28. 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
29. 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 }
30. 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
31. Collectors
• Finalizes the stream by converting it to concrete collections
• CBStreams auto-converts Java -> CFML Data Types
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 >.
33. 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
43. Implement JDK 9-10 features
Threading Intricacies to solve
cbORM, Quick Integration
qb Integration
ColdBox Integration
Reactive Streams
Roadmap
44. Questions??
● After the presentation in the Breakout Room
● On the Boxteam slack channel - boxteam.herokuapp.com
● On Twitter @lmajano @gpickin @ortussolutions