On this page, we will extend the CartesianPlane class to plot a graph based on a stream of user-supplied commands. We will rely heavily on the concepts of lambdas, functional interfaces, and streams. When we’re done, our project, while not yet complete, will be fully ready for you to experiment with plotting equations.
GitHub repository: Cartesian Plane Part 8
Previous page: Cartesian Plane Lesson 8 Page 2: Points on the Graph, Properties
CartesianPlane: Functional Interfaces, Streaming
On this page, we will introduce a user interface to the CartesianPlane class. By “user interface,” I refer to the means by which the application programmer presents coordinates to a CartesianPlane object for plotting. (“User interface” in the context of a GUI with push buttons and drop-down lists, and in the context of operator input, will come in later lessons.)
To complete this part of the lesson, you need to be familiar with lambdas, functional interfaces, and streams. These topics are discussed in the Java Anonymous Class Primer. In addition, the Anonymous Class lesson lists a wide variety of source material for studying these topics.
Before jumping into the user interface, let’s warm up by refactoring CartesianPlane to leverage functional interfaces and streaming operations.
Note: The introductory page for this lecture includes additional refactoring not directly related to the current topic; see Miscellaneous Details.
Refactoring the CartesianPlane Class
Two obvious places to introduce lambdas into this class are the drawGridLines and drawAxes methods. Currently, these methods perform their tasks via enhanced for loops, for example:
for ( Line2D line : lineGen )
gtx.draw( line );
These two lines of code can be reduced to a single line using a method reference. Here’s the refactored code for drawGridLines; the code for drawAxes is nearly identical. Note that, in the code, LineGen is iterable, and the Iterable interface includes a default forEach( Consumer<T> ) method. The method reference we use is gtx::draw.
1 2 3 4 5 6 7 8 9 10 11 | private void drawGridLines() { if ( gridLineDraw ) { LineGenerator lineGen = new LineGenerator( gridRect, gridUnit, gridLineLPU ); gtx.setStroke( new BasicStroke( gridLineWeight ) ); gtx.setColor( gridLineColor ); lineGen.forEach( gtx::draw ); } } |

The next change presents a better chance to practice streaming with functional interfaces. As it stands now, if you display a graph and look at it closely, you will see that something funny is going on in the neighborhood of the origin. That’s because we are currently drawing tic marks at the origin, and we really shouldn’t be doing that.
The draw-tic methods must be able to detect the x- and y-axes, and avoid drawing lines there. To facilitate this, we’ll add two methods to the LineGenerator class. These methods can determine if a line is on the x-axis or the y-axis according to the following conditions:
- A horizontal line at the center of the graph lies on the x-axis. (We know we have a horizontal line if the y-coordinates of the endpoints are equal, and the line is at the center of the graph if the y-coordinates == centerYco).
- A vertical line at the center of the graph lies on the y-axis. (We know we have a vertical line if the x-coordinates of the endpoints are equal, and the line is at the center of the graph if the x-coordinates == centerXco).
To test coordinates (which are floating-point values) for equality, we will introduce a method that uses the epsilon test.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | /** Epsilon to determine if 2 float values are approximately equal. */ private static final double epsilon = .0001; // ... public boolean isXAxis( Line2D line ) { double yco1 = line.getY1(); double yco2 = line.getY2(); boolean result = equals( yco1, yco2 ) // is this a horizontal line? && equals( yco1, centerYco ); // does it fall on the x-axis? return result; } public boolean isYAxis( Line2D line ) { double xco1 = line.getX1(); double xco2 = line.getX2(); boolean result = equals( xco1, xco2 ) // is this a vertical line? && equals( xco1, centerXco ); // does it fall on the y-axis? return result; } private boolean equals( double val1, double val2 ) { double diff = Math.abs( val1 - val2 ); boolean result = diff < epsilon; return result; } |
To draw the tick marks, we want to generate a stream, filter out the tick marks that lie on the x- and y-axes, then draw the lines that pass the filters; the pseudocode looks like this:
get Stream<Line2D> of tick marks()
.filter( anything not on the x-axis )
.filter( anything not on the y-axis )
.forEach( gtx::draw )
Now the question is, how do we get a stream of tick marks? Well, LineGenerator implements Iterable<Line2D>, and there is an algorithm for deriving a Stream<T> from an Iterable<T>. Later, we will also need to derive a Stream from an Iterator, and there’s an algorithm to do that, too. Without going into too much detail, here is a description of the algorithms; you can find the sample code in this project’s sandbox package.
Important:Â Spliterators can be a daunting topic, but you only have to know enough about them to implement the algorithms described below. There’s a more thorough discussion of these algorithms in section Iterables, Iterators, and Spliterators in the Anonymous Class Primer.
To generate a stream from an Iterable:
- Get a Spliterator from the iterable:
    Iterable<Integer> iter = new IntIterable( 5, 10, 2 );
    Spliterator<Integer> splitter = iter.spliterator(); - Use the StreamSupport utility class to generate a stream from the spliterator:
    Stream<Integer> stream =
        StreamSupport.stream( splitter, false );
1 2 3 4 5 6 7 8 9 10 11 | public class IterableToStreamDemo { public static void main(String[] args) { Iterable<Integer> iter = new IntIterable( 5, 10, 2 ); Spliterator<Integer> splitter = iter.spliterator(); Stream<Integer> stream = StreamSupport.stream( splitter, false ); stream.forEach( System.out::println ); } } |
To generate a stream from an Iterator
- Use the Spliterators utility class to generate a spliterator from the iterator:
    int prop = Spliterator.ORDERED;   ÂIterator<Integer> iter = new IntIterator( 5, 10, 2 );
    Spliterator<Integer> splitter =
        Spliterators.spliteratorUnknownSize( iter, prop ); - Use the StreamSupport utility class to generate a stream from the Spliterator:
    Stream<Integer> stream =
        StreamSupport.stream( splitter, false );
1 2 3 4 5 6 7 8 9 10 11 | public class IteratorToStreamDemo { public static void main(String[] args) { Iterator<Integer> iter = new IntIterator( 5, 10, 2 ); Spliterator<Integer> splitter = Spliterators.spliteratorUnknownSize( iter, 0 ); StreamSupport.stream( splitter, false ) .forEach( System.out::println ); } } |
Now, in our drawMajorTics and drawMinorTics methods, we can use a LineGenerator object, which implements Iterable, to a) get a spliterator; b) use the spliterator and the StreamSupport class to generate a stream; and c) put a filter on the stream that eliminates lines that lie on the x- or y-axis. Note, by the way, that the code below uses the static not method in the Predicate interface; the not method wasn’t introduced until Java 11.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | private void drawMajorTics() { if ( ticMajorDraw ) { LineGenerator lineGen = new LineGenerator( gridRect, gridUnit, ticMajorMPU, ticMajorLen, LineGenerator.BOTH ); gtx.setStroke( new BasicStroke( ticMajorWeight ) ); gtx.setColor( ticMajorColor ); StreamSupport .stream( lineGen.spliterator(), false ) .filter( Predicate.not(lineGen::isXAxis) ) .filter( Predicate.not(lineGen::isYAxis) ) .forEach( gtx::draw ); } } |
The User Interface
So I decided that the presentation of plotting instructions to the Cartesian plane would be via a stream. This is quite different from earlier in the tutorial when we added a temporary mechanism that used a list. A stream has a couple of advantages over a list:
- Lists are static. For a given plot, we could require storing thousands of PlotCommand objects in the list. And some of these objects contain other objects. That’s a lot of objects taking up heap space for the life of the program. With a stream, we need the same number of objects, but they’re created dynamically, one at a time, put to use, and then immediately (more or less) gobbled up by the garbage collector.
- Streams are more flexible. Imagine having a stream of coordinates to which we could optionally apply rotation and/or translation mappings:
    (streamOfXValues)
        .map( this::toPoint2D )
        .map( this::rotate )
        .map( this::translate )
        .map( this::toPlotCommand)
Yes, streams have disadvantages, too:
- Streams are slower. Objects in a list only have to be instantiated once; objects in a stream have to be instantiated every time we need them, which includes every time the operator changes the size of the window containing the graph.
- Streams cannot be reused. My application can’t pass a stream to the CartesianPlane and then forget about it. Each time we redraw the screen, we need a way to prompt the user for a new stream, which makes our application a bit more complicated, a little harder to understand, and a little more likely to be buggy.
So instead of passing a list, our users will have to provide a functional interface. The functional interface will be a Supplier that returns a Stream<PlotCommand>. The field that stores the functional interface will be declared something like this:
private Supplier<Stream<PlotCommand>> streamSupplier =…
And we’ll have a setter with this footprint: public void setStreamSupplier(
Supplier<Stream<PlotCommand>> supplier
)
Digression: We are about to write some logic that involves several different elements of our program, put together in a non-traditional way. Let’s take a moment to make sure we understand what we’re going to do. Here’s a typical scenario:

- The user (that is, the programmer who wishes to embed a stream of commands in a CartesianPlane object) passes a Stream Supplier to the CartesianPlane object, which stores it for future reference.
- The user invokes the CartesianPlane object’s repaint() method…
- … which prompts the system to trigger the paintComponent(Graphics) method in the CartesianPlane object.
- The paintComponent method uses the Stream Supplier to obtain a new Stream:
    Stream<PlotCommand> stream = streamSupplier.get();
and then to traverse it:
    stream.forEach( c -> c.execute() ); - The paintComponent() method terminates, following which:
- The exhausted stream (the stream variable in the code above) can never be used again; it is garbage-collected.
- The Stream Supplier persists and can be used later to fetch new streams.
- At some later time, the operator resizes the CartesianPlane frame…
- … which prompts the system to trigger the repaint method [go to step 3].
One more thing: I don’t want my stream supplier ever to be null, so it will have an initial value of:
() -> Stream.empty()
The setter will have to check the parameter for null and, if it is, substitute the same value. The code in the method to draw the user’s plot, drawUserPlot, will be: Stream stream = streamSupplier.get(); stream.forEach( c -> c.execute());
or, in a shorter format: streamSupplier.get().forEach( c -> c.execute() );
Stream Supplier Demo
Our project’s sandbox.stream_supplier package contains a sample application that simulates the above process. Here’s a detailed discussion.
Infrastructure
The application’s infrastructure consists of one interface and four classes.
â– interface Command
This is a functional interface whose single method is execute, which has a default implementation that simply prints the object it’s called on:
public interface Command
{
default void execute()
{
System.out.println( this );
}
}
â– class ColorCommand implements Command
â– public class PointCommand implements Command
â– class ShapeCommand implements Command
These three classes all implement the Command interface. PointCommand encapsulates a pair of Cartesian coordinates, and ColorCommand encapsulates a color. ShapeCommand encapsulates a string (its name comes from the class it’s simulating, PlotShapeCommand). Each class has a constructor and overrides the toString method. Here’s the code for PointCommand; the other classes are similar and can be found in the GitHub repository.
public class PointCommand implements Command
{
private final Point point;
public PointCommand( int xco, int yco )
{
point = new Point( xco, yco );
}
@Override
public String toString()
{
String str = "Point command: " + point;
return str;
}
}
â– class CommandStreamer
In the simulation, this class replaces CartesianPlane. It has one instance variable and two methods. The instance variable holds the functional interface we use to obtain a command stream every time we need to draw a plot (in CartesianPlane, this is every time paintComponent is called). Note that its initial value is the empty stream.
public class CommandStreamer
{
private Supplier<Stream<Command>> streamSupplier =
() -> Stream.empty();
// ...
}
The setStreamSupplier method is called by the client to initialize the streamSupplier field. To turn off plotting, clients can pass null to this method, in which case the setter will set the field to the empty stream.
public void
setStreamSupplier( Supplier<Stream<Command>> supplier )
{
if ( supplier == null )
streamSupplier = () -> Stream.empty();
else
streamSupplier = supplier;
}
The getAndTraverseStream method simulates command execution. In the CartesianPlane code, this is a private method that gets invoked from paintComponent. It utilizes streamSupplier to get a new stream, then traverses it. Note that the stream reference is stored in a local variable; when the method terminates, the reference is garbage-collected.
public void getAndTraverseStream()
{
System.out.println( "*** Begin command stream" );
Stream<Command> stream = streamSupplier.get();
stream.forEach( c -> c.execute() );
System.out.println( "*** End command stream" );
}
The Simulation Driver
The main method for our simulation resides in the StreamSupplierDemo class, which has three methods:
public class StreamSupplierDemo
{
public static void main(String[] args) { /* ... */ }
private static void
scheduleCommandExecution( CommandStreamer streamer ) { /* ... */ }
private static Stream<Command> streamGetter() { /* ... */ }
}
The streamGetter() method generates a simulated command stream. It instantiates an array of Command objects, from which it derives a stream using the Arrays utility class.
private static Stream<Command> streamGetter()
{
Command[] commands =
{
new ColorCommand( Color.RED ),
new ShapeCommand( "Square" ),
// ...
};
Stream<Command> stream = Arrays.stream( commands );
return stream;
}
The main method starts by creating a CommandStreamer, then immediately initiates command stream processing:
CommandStreamer streamer = new CommandStreamer();
scheduleCommandExecution( streamer );
At this point, the application has not yet set a stream supplier on the CommandStreamer. Since the initial value of the stream supplier is the empty stream, the CommandStreamer will process a stream of zero Command objects. Then the main method sets the stream supplier:
streamer.setStreamSupplier( () -> streamGetter() );
and enters a loop that asks the operator whether to execute a new command stream. If the operator enters no, the simulation ends. Here’s the complete main method:
public static void main(String[] args)
{
CommandStreamer streamer = new CommandStreamer();
scheduleCommandExecution( streamer );
streamer.setStreamSupplier( () -> streamGetter() );
int status = JOptionPane.OK_OPTION;
while ( status == JOptionPane.OK_OPTION )
{
status = JOptionPane.showConfirmDialog( null, "New stream?" );
if ( status == JOptionPane.OK_OPTION )
scheduleCommandExecution( streamer );
}
}
You’ve no doubt noticed that “initiates command stream processing” does not mean directly calling the CommandStreamer.getAndTraverseStream(). Instead, we invoke scheduleCommandExecution. This is where the client would normally call repaint, and our scheduleCommandExecution simulates calling repaint. Here’s the code for this method.
private static void
scheduleCommandExecution( CommandStreamer streamer )
{
System.out.println( "*** Scheduling execution" );
SwingUtilities.invokeLater(
() -> streamer.getAndTraverseStream()
);
System.out.println( "*** Execution scheduled" );
}
The invokeLater method hands off execution of getAndTraverseStream to another thread. In case you’re not familiar with threads, what happens is:
- scheduleCommandExecution prints a diagnostic message.
- Our code calls SwingUtilities.invokeLater. The invokeLater method makes a note that, at some time in the future, streamer.getAndTraverseStream() needs to be executed; another way to say this is that the getAndTraverseStream method has been scheduled to run later. The invokeLater method immediately returns to our code.
- scheduleCommandExecution prints a diagnostic message and returns to the main method.
- The main method enters the loop that asks the operator to make a decision.
- While the operator is pondering JOptionPane’s confirm dialog, seemingly out of nowhere, CommandStreamer.getAndTraverseStream begins execution.
You’ll find the output from our simulation below. Notice that:
*** Scheduling execution
*** Execution scheduled
*** Begin command stream
Color command: java.awt.Color[r=255,g=0,b=0]
Shape command: Square
Point command: java.awt.Point[x=10,y=20]
Color command: java.awt.Color[r=0,g=255,b=0]
Shape command: Circle
Point command: java.awt.Point[x=15,y=25]
*** End command stream
- The first diagnostic from scheduleCommandExecution is printed; then:
- The second diagnostic from scheduleCommandExecution is printed; then:
- The output from getAndTraverseStream is printed.
Adding the Streaming Logic to CartesianPlane
Finally, getting back to the CartesianPlane class, here’s a sketch of how the above streaming logic is applied to it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // ... private Supplier<Stream<PlotCommand>> streamSupplier = () -> Stream.empty(); // ... public void setStreamSupplier( Supplier<Stream<PlotCommand>> supplier ) { if ( supplier != null ) streamSupplier = supplier; else streamSupplier = () -> Stream.empty(); } // ... private void drawUserPlot() { gtx.setColor( plotColor ); streamSupplier.get().forEach( c -> c.execute() ); } // ... |
For the record, here is the final (at least for now) code for CartesianPlane’s paintComponent method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | @Override public void paintComponent( Graphics graphics ) { // begin boilerplate super.paintComponent( graphics ); currWidth = getWidth(); currHeight = getHeight(); gtx = (Graphics2D)graphics; gtx.setColor( mwBGColor ); gtx.fillRect( 0, 0, currWidth, currHeight ); // end boilerplate // set up the label font // round font size to nearest int int fontSize = (int)(labelFontSize + .5); labelFont = new Font( labelFontName, labelFontStyle, fontSize ); gtx.setFont( labelFont ); labelFRC = gtx.getFontRenderContext(); // Describe the rectangle containing the grid float gridWidth = currWidth - marginLeftWidth - marginRightWidth; float minXco = marginLeftWidth; float gridHeight = currHeight - marginTopWidth - marginBottomWidth; float minYco = marginTopWidth; gridRect = new Rectangle2D.Float( minXco, minYco, gridWidth, gridHeight ); // Set the clip region to the rectangle bounding the grid before // drawing any lines. Don't forget to restore the original clip // region after drawing the lines. Shape origClip = gtx.getClip(); gtx.setClip( gridRect ); // The plot color and plot shape are set to defaults // each time paintComponent is invoked. The user can // change the values but they will only be in effect // for the duration of one paintComponent execute; with // the next paintComponent execution they will return // to their default values. currPlotShape = plotShape; // Values to use in mapping Cartesian coordinates to pixel coordinates xOffset = gridRect.getX() + (gridRect.getWidth() - 1) / 2; yOffset = gridRect.getY() + (gridRect.getHeight() - 1) / 2; drawGridLines(); drawMinorTics(); drawMajorTics(); drawAxes(); drawUserPlot(); gtx.setClip( origClip ); if ( labelDraw ) { gtx.setColor( labelFontColor ); drawHorizontalLabels(); drawVerticalLabels(); } paintMargins(); } |
Examples
Here are some sample programs that use command streams to plot curves using our CartesianPlane class. You can find the code in our project’s app package.
Example 1

We’ll begin with something simple. SimpleCoordinatesPlot begins by creating a list of commands to plot a cubic polynomial. Once we have a list, setting the stream supplier in the CartesianPlane comes down to: canvas.setStreamSupplier( () -> commands.stream() )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public class SimpleCoordinatesPlot { public static void main(String[] args) { CartesianPlane canvas = new CartesianPlane(); Root root = new Root( canvas ); root.start(); List<PlotCommand> commands = new ArrayList<>(); for ( float xco = -10 ; xco <= 10 ; xco += .005f ) { float yco = (float)(Math.pow( xco, 3 ) + 2 * Math.pow( xco, 2 ) - 1); PlotPointCommand coords = new PlotPointCommand( canvas, xco, yco ); commands.add( coords ); } canvas.setStreamSupplier( () -> commands.stream() ); } } |
Example 2

This example’s a little more involved, but it uses the same strategy as SimpleCoordinatesPlot to build a List<PlotCommand> of commands, then uses the List.stream() method as a Supplier<Stream<PlotCommand>> (see line 37 in the program listing below). ListCoordinatesPlot plots the coordinates of a quadratic equation. It keeps track of the sign of the y-coordinate and, when it changes, inserts a PlotCommand to change the color, so that all points above the x-axis are displayed in red and those below in blue. After plotting the graph, it uses the quadratic formula to calculate the roots of the function and highlights them with green circles.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | public class ListCoordinatesPlot { public static void main(String[] args) { CartesianPlane canvas = new CartesianPlane(); Root root = new Root( canvas ); root.start(); Color posColor = Color.RED; Color negColor = Color.BLUE; Color currColor = posColor; double currSign = 0; List<PlotCommand> commands = new ArrayList<>(); for ( float xco = -10 ; xco <= 10 ; xco += .005f ) { float yco = xco * xco - 3; float thisSign = (int)Math.signum( yco ); if ( thisSign != currSign ) { currSign = thisSign; currColor = thisSign < 0 ? negColor : posColor; commands.add( new PlotColorCommand( canvas, currColor ) ); } PlotPointCommand coords = new PlotPointCommand( canvas, xco, yco ); commands.add( coords ); } float[] roots = getRoots( 1, 0, -3 ); if ( roots != null ) { PlotShape circle = new CircleShape( 7 ); commands.add( new PlotColorCommand( canvas, Color.GREEN ) ); commands.add( new PlotShapeCommand( canvas, circle ) ); commands.add( new PlotPointCommand( canvas, roots[0], 0 ) ); commands.add( new PlotPointCommand( canvas, roots[1], 0 ) ); } canvas.setStreamSupplier( () -> commands.stream() ); } private static float[] getRoots( double coefA, double coefB, double coefC ) { float[] roots = null; double discr = coefB * coefB - 4 * coefA * coefC; if ( discr>= 0 ) { roots = new float[2]; roots[0] = (float)((-coefB - Math.sqrt( discr )) / (2 * coefA)); roots[1] = (float)((-coefB + Math.sqrt( discr )) / (2 * coefA)); } return roots; } } |
Example 3

The next example starts with three auxiliary classes. The first is a utilities class for support of functional interface logic. FIUtils doesn’t have much in it for now. As a utilities class, it has a private default constructor to prevent instantiation. It has a static nested class (which is instantiable) to generate a PlotPointCommand object from a Point2D and CartesianPlane objects. A lot of modern thinking eschews the use of public constructors, so I decided that users will not be able to instantiate the nested class directly; instead, they invoke the toPlotPointCommand in the FIUtils class: ToPlotPointCommand toPlotPointCmd =
FIUtils.toPlotPointCommand( plane );
(see lines 18-37 of the PolyIteratorDemo listing, below).
The ToPlotPointCommand class has one public method, of, that takes a Point2D and returns a PlotPointCommand object: Point2D point = // ...
PlotPointCommand cmd = toPlotPointCmd.of( point );
For example, the following two code fragments produce the same PlotPointCommand object:
// ***** Code fragment 1
CartesianPlane plane = new CartesianPlane();
ToPlotPointCommand toPlotPointCmd =
FIUtils.toPlotPointCommand( plane );
Point2D point = new Point( 10, 20 );
PlotPointCommand cmd = toPlotPointCmd.of( point );
// *****
// Is equivalent to:
// ***** Code fragment 2
CartesianPlane plane = new CartesianPlane();
PlotPointCommand cmd = new PlotPointCommand( plane, 10, 20 );
// ****
In our examples, we’ll use this method in streaming operations, and typically invoke it using a lambda/method reference: List list = // ...
list.stream().map( toPlotPointCmd::of );
Try this little program:
public static void main(String[] args)
{
Point2D points[] =
{ new Point( 1, 2 ), new Point( 3, 4 ), new Point( 5, 6 ) };
List<Point2D> list = Arrays.asList( points );
CartesianPlane plane = null;
ToPlotPointCommand toPlotPointCmd =
FIUtils.toPlotPointCommand( plane );
list.stream()
.map( toPlotPointCmd::of )
.forEach( System.out::println );
}
Here’s the code for the utilities class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | public class FIUtils { private FIUtils() { } public static ToPlotPointCommand toPlotPointCommand( CartesianPlane plane ) { ToPlotPointCommand cmd = new ToPlotPointCommand( plane ); return cmd; } public static class ToPlotPointCommand { /** The CartesianPlane to which commands are to be applied. */ private final CartesianPlane plane; private ToPlotPointCommand( CartesianPlane plane ) { this.plane = plane; } public PlotPointCommand of( Point2D point ) { float xco = (float)point.getX(); float yco = (float)point.getY(); PlotPointCommand cmd = new PlotPointCommand( plane, xco, yco ); return cmd; } } } |
Our second auxiliary class implements the DoubleUnaryOperator functional interface. Given the coefficients of a polynomial, its applyAsDouble method calculates the y-coordinate associated with a given x-coordinate. Try this program:
public class PolynomialDemo
{
public static void main(String[] args)
{
// Calculates 2x**3 + x**2 + 3x + 1
Polynomial poly = new Polynomial( 2, 1, 3, 1 );
Stream.of( 1, 2, 3, 4, 5 )
.map( poly::applyAsDouble )
.forEach( System.out::println );
}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public class Polynomial implements DoubleUnaryOperator { private final double[] coefficients; public Polynomial( double... coeff ) { coefficients = Arrays.copyOf( coeff, coeff.length ); } public double applyAsDouble( double xval ) { int degree = coefficients.length - 1; double yval = coefficients[degree]; for ( int inx = degree - 1 ; inx >= 0 ; --inx ) yval = coefficients[inx] + xval * yval; return yval; } } |
Next, we create the FunctionIterator class. This class implements Iterator<Point2D>. Its constructor takes a range of x-coordinates to iterate over and a DoubleUnaryOperator to apply with each iteration. It uses the functional interface to calculate a y-coordinate for each x-coordinate in the range and combine them into a Point2D. Consider FunctionIteratorDemo1 and its output:
public class FunctionIteratorDemo1
{
public static void main(String[] args)
{
// Calculate 3.5x**3 + -5x**2 + 0x + 1
Polynomial poly =
new Polynomial( 3.5f, -5, 0, 1 );
// Invoke poly for -1.5 <= x <= 1.5
Iterator<Point2D> funkIter =
new FunctionIterator( poly, -1.5f, 1.5f, .5f );
while ( funkIter.hasNext() )
{
Point2D point = funkIter.next();
System.out.println( point );
}
}
}
// *** program output
Point2D.Double[-1.5, -22.0625]
Point2D.Double[-1.0, -7.5]
Point2D.Double[-0.5, -0.6875]
Point2D.Double[0.0, 1.0]
Point2D.Double[0.5, 0.1875]
Point2D.Double[1.0, -0.5]
Point2D.Double[1.5, 1.5625]
Application FunctionIteratorDemo2 does the same job, except instead of traversing funkIter using a traditional while loop, it converts the iterator to a stream, then traverses the stream:
public class FunctionIteratorDemo2
{
public static void main(String[] args)
{
// Calculate 3.5x**3 + -5x**2 + 0x + 1
Polynomial poly =
new Polynomial( 3.5f, -5, 0, 1 );
Iterator<Point2D> funkIter =
new FunctionIterator( poly, -2, 3, .5f );
int props = Spliterator.ORDERED;
Spliterator<Point2D> splitter =
Spliterators.spliteratorUnknownSize( funkIter, props );
Stream<Point2D> stream =
StreamSupport.stream( splitter, false );
stream.forEach( System.out::println );
}
}
Here’s the code for the FunctionIterator class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | public class FunctionIterator implements Iterator<Point2D> { private final DoubleUnaryOperator funk; private final double last; private final double incr; private double next; public FunctionIterator( DoubleUnaryOperator funk, double first, double last, double incr ) { this.funk = funk; this.last = last; this.incr = incr; next = first; } @Override public boolean hasNext() { boolean result = next <= last; return result; } @Override public Point2D next() { if ( next > last ) throw new NoSuchElementException(); Point2D point = new Point2D.Double( next, funk.applyAsDouble( next ) ); next += incr; return point; } } |
Finally, PolyIteratorDemo puts it all together to plot a cubic polynomial.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | public class PolyIteratorDemo { private static final CartesianPlane plane = new CartesianPlane(); public static void main(String[] args) { PropertyManager pmgr = PropertyManager.INSTANCE; pmgr.setProperty( CPConstants.TIC_MAJOR_LEN_PN, 21 ); pmgr.setProperty( CPConstants.TIC_MAJOR_WEIGHT_PN, 1 ); pmgr.setProperty( CPConstants.TIC_MAJOR_MPU_PN, 1 ); pmgr.setProperty( CPConstants.TIC_MINOR_LEN_PN, 11 ); pmgr.setProperty( CPConstants.TIC_MINOR_WEIGHT_PN, 1 ); pmgr.setProperty( CPConstants.TIC_MINOR_MPU_PN, 5 ); Root root = new Root( plane ); root.start(); ToPlotPointCommand toPlotPointCmd = FIUtils.toPlotPointCommand( plane ); Polynomial poly = new Polynomial( 3.5f, -5, 0, 1 ); plane.setStreamSupplier( () -> { FunctionIterator iter = new FunctionIterator( poly, -2, 2.5, .005 ); Spliterator<Point2D> splitter = Spliterators.spliteratorUnknownSize( iter, 0 ); Stream<PlotCommand> stream = StreamSupport.stream( splitter, false ) .map( toPlotPointCmd::of ); return stream; } ); } } |
Some notes about the above:
- Line 21: poly is the DoubleUnaryOperator that describes the cubic polynomial.
- Line 23: () -> { begins a “long-form” lambda expression that consists of multiple lines enclosed in an explicit body. The value of the lambda expression is established by the return statement at the end of the body.
- Lines 24 – 25: poly is used to create a FunctionIterator that will traverse the range [-2,2.5] with an increment of .005.
- Lines: 26 – 27: The Spliterators utility class transforms the FunctionIterator into a Spliterator.
- Lines: 28 – 30:
- The StreamSupport utility class uses the Spliterator to generate a Stream<Point2D>.
- The Point2D stream is mapped to a PlotPointCommand stream.
- These two lines constitute a lambda that implements the Supplier<Stream<PlotCommand>> functional interface.
- Line 31: The return statement establishes the value of the lambda expression.
- Line 22: The functional interface defined on lines 23-32 is registered as a stream supplier with the CartesianPlane class.
Example 4

This example starts with the ParametricCoordinates class, which implements Iterable<Point2D>. It encapsulates a parametric function with one parameter (of type double) that produces a two-valued result (the x- and y-coordinates of a Point2D). Its constructor establishes the range over which the parameter is evaluated, and a DoubleFunction<Point2D> functional interface that encapsulates the logic for calculating the Point2D result from a parameter value. It has, as a nested class, an Iterator<Point2D> which looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | public class ParametricCoordinates implements Iterable<Point2D> { private final DoubleFunction<Point2D> funk; private final double first; private final double last; private final double incr; // ... private class Point2DIterator implements Iterator<Point2D> { /** "Next" value in the encapsulated sequence. */ private double control = first; @Override public boolean hasNext() { return control <= last; } @Override public Point2D next() { if ( control > last ) { throw new NoSuchElementException(); } Point2D point = funk.apply( control ); control += incr; return point; } } } |
It also contains a stream method that uses the encapsulated iterator to generate a Stream<Point2D>:
1 2 3 4 5 6 | public Stream<Point2D> stream() { Stream<Point2D> stream = StreamSupport.stream( this.spliterator(), false ); return stream; } |
In regard to the above code, note that:
- ParametricCoordinates implements Iterable, and Iterable has a default method that generates a Spliterator.
- The logic uses the StreamSupport class to convert the Spliterator to a Stream.
To conclude the example, ParametricCoordinatesDemo uses the ParametricCoordinates class to plot a rose.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | public class ParametricCoordinatesDemo { private static final CartesianPlane plane = new CartesianPlane(); public static void main(String[] args) { PropertyManager pmgr = PropertyManager.INSTANCE; pmgr.setProperty( CPConstants.TIC_MAJOR_LEN_PN, 21 ); pmgr.setProperty( CPConstants.TIC_MAJOR_WEIGHT_PN, 1 ); pmgr.setProperty( CPConstants.TIC_MAJOR_MPU_PN, 1 ); pmgr.setProperty( CPConstants.TIC_MINOR_LEN_PN, 11 ); pmgr.setProperty( CPConstants.TIC_MINOR_WEIGHT_PN, 1 ); pmgr.setProperty( CPConstants.TIC_MINOR_MPU_PN, 5 ); Root root = new Root( plane ); root.start(); ToPlotPointCommand toPlotPoint = FIUtils.toPlotPointCommand( plane ); DoubleFunction<Point2D> rose = t -> new Point2D.Double( Math.cos( t ) * Math.sin( 4 * t ), Math.sin( t ) * Math.sin( 4 * t ) ); ParametricCoordinates coords = new ParametricCoordinates( rose, 0, 2 * Math.PI, .005 ); plane.setStreamSupplier( () -> coords.stream() .map( toPlotPoint::of ) ); } } |
Example 5

Our last example uses stream concatenation to plot two functions in different colors. Let’s look at the code first, then discuss it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | public class TwoPlotDemo { private static final CartesianPlane plane = new CartesianPlane(); public static void main(String[] args) { PropertyManager pmgr = PropertyManager.INSTANCE; pmgr.setProperty( CPConstants.TIC_MAJOR_LEN_PN, 21 ); pmgr.setProperty( CPConstants.TIC_MAJOR_WEIGHT_PN, 1 ); pmgr.setProperty( CPConstants.TIC_MAJOR_MPU_PN, 1 ); pmgr.setProperty( CPConstants.TIC_MINOR_LEN_PN, 11 ); pmgr.setProperty( CPConstants.TIC_MINOR_WEIGHT_PN, 1 ); pmgr.setProperty( CPConstants.TIC_MINOR_MPU_PN, 5 ); Root root = new Root( plane ); root.start(); plane.setStreamSupplier( () -> getPlotCommands() ); } private static Stream<PlotCommand> getPlotCommands() { ToPlotPointCommand toPlotPointCmd = FIUtils.toPlotPointCommand( plane ); Polynomial poly = new Polynomial( 3.5f, -5, 0, 1 ); FunctionIterator iter = new FunctionIterator( poly, -2, 2.5, .005 ); Spliterator<Point2D> splitter = Spliterators.spliteratorUnknownSize( iter, 0 ); Stream<PlotCommand> polyStream = StreamSupport.stream( splitter, false ) .map( toPlotPointCmd::of ); DoubleFunction<Point2D> circle = t -> new Point2D.Double( Math.cos( t ) + .5, Math.sin( t ) - .5 ); ParametricCoordinates coords = new ParametricCoordinates( circle, 0, 2 * Math.PI, .005 ); Stream<PlotCommand> circleStream = coords.stream().map( toPlotPointCmd::of ); PlotCommand redCmd = new PlotColorCommand( plane, Color.RED ); PlotCommand blueCmd = new PlotColorCommand( plane, Color.BLUE ); Stream<PlotCommand> streamA = Stream.concat( Stream.of( redCmd ), polyStream ); Stream<PlotCommand> streamB = Stream.concat( streamA, Stream.of( blueCmd ) ); Stream<PlotCommand> streamC = Stream.concat( streamB, circleStream ); return streamC; } } |
Notes:
- Line 21: Helper method getPlotCommands is responsible for producing the stream each time it’s needed.
- Line 26: Creates an instance of a Polynomial, which is a DoubleUnaryOperator functional interface.
- Lines 27 – 28: The functional interface is used to create an Iterator<Point2D>.
- Lines 29 – 30: The iterator is turned into a Spliterator<Point2D> via the Spliterators utility class.
- Lines 31 – 33: The StreamSupport utilities class uses the spliterator to create a Stream<PlotCommand>.
- Lines 35 – 39: “t ->…” is a lambda that implements a DoubleFunction<Point2D> functional interface, which in turn represents the parametric equation for a circle.
- Lines 40 – 41: ParametricCoordinates is an Iterable<Point2D> with a method, stream, which generates a Stream<Point2D>.
- Lines 42 – 43: The Stream<Point2D> from ParametricCoordinates is mapped to a Stream<PlotCommand>.
- Lines 45 – 46: Two PlotColorCommands are instantiated for convenience.
- Lines 47 – 48: Two streams are concatenated into a single stream using the Stream.concat method:
- The first stream, generated by the Stream.of method, consists of a single element;
- The second stream was previously created at line 26.
- Lines 49 – 50: Two streams are concatenated into a single stream using the Stream.concat method:
- The first stream was created at line 47, and
- The second stream is generated by the Stream.of method, and consists of a single element.
- Lines 51 – 52: Two streams are concatenated into a single stream using the Stream.concat method:
- The first stream was created at line 49, and
- The second stream was previously created at line 42.
- Line 18:
() -> getPlotCommands() is a lambda that implements a Supplier<Stream<PlotCommand>> functional interface;- The above functional interface is registered as a stream supplier with the CartesianPlane class.
Summary
On this page, we discussed how to adapt our CartesianPlane class to use streams to plot a graph. We began by refactoring CartesianPlane to replace some of the traditional code with functional interfaces. Next, we learned how to use Spliterators to derive streams from Iterators and Iterables. We applied that technique to LineGenerator (which implements Iterable<Line2D>) to derive a stream, then used stream traversal to draw the lines it produces.
Next, we implemented a mechanism for a client to add a Supplier<Stream<PlotCommand>> to a CartesianPlane object. In its paintComponent method, the CartesianPlane used the supplier to obtain a stream of PlotCommands, then traversed that stream to plot a graph. We finished by looking at several examples that graph equations using the new PlotCommand stream processing facility.
In the next lesson, we’ll begin developing facilities to plot graphs based on input from a human operator.