Minborg

Minborg
Minborg

Wednesday, June 13, 2018

Go Full Stack with Java in a Jiffy

Here is a look at how you can write a full stack database web application without using SQL, HQL, PHP, ASP, HTML, CSS or Javascript and instead relying purely on Java using Vaadin’s UI layer and Speedment Stream ORM.

Ever wanted to quickly create a web application connected to your existing database or build a professional application with short time-to-market requirements? The Java Stream API has unleashed the possibility to write database queries in pure Java.

In this article, we will demonstrate how fast and easy this can be done by leveraging two Java frameworks; Vaadin and Speedment. Because they both use Java Streams, it easy to connect them together. This means we will end up with a short, concise and type-safe application.

For this mini-project, we will use the My SQL sample database named "Employees" which provides approximately 160MB of data spread over six separate tables and comprising 4 million records.

The full application code is available at GitHub and you can clone this repository if you want to run the application in your own environment. You will also need trial licenses from both Vaadin and Speedment to use the features used in this article. These are available for free.


The intended end result is a web application where it is possible to analyze gender balance and salary distribution among different departments. The result is displayed graphically, using pure standard Vaadin Charts Java components as depicted in the video below:




Setting Up the Data Model

We are using Speedment Stream ORM to access the database. It is easy to set up any project using the Speedment initializer. Speedment can generate Java classes directly from the database’s schema data. After generation, we can create our Speedment instance like this:

Speedment speedment = new EmployeesApplicationBuilder()
                .withUsername("...") // Username need to match database
                .withPassword("...") // Password need to match database
                .build();

Create a Dropdown for Departments

In our web application, we want to have a drop-down list of all departments. It is easy to retrieve the departments from the database as can be seen in this method:

public Stream<Departments> departments() {
    DepartmentsManager depts = speedment.getOrThrow(DepartmentsManager.class);
    return depts.stream();
}

Joining Departments and Employees Together

Now we are going to create a join relation between Departments and Employees. In the database, there is a many-to-many relation table that connects these tables together named DeptEmpl.
First, we create a custom tuple class that will hold our three entries from the joined tables:

public final class DeptEmplEmployeesSalaries {

    private final DeptEmp deptEmp;
    private final Employees employees;
    private final Salaries salaries;

    public DeptEmplEmployeesSalaries(
        DeptEmp deptEmp, 
        Employees employees, 
        Salaries salaries
    ) {
        this.deptEmp = requireNonNull(deptEmp);
        this.employees = requireNonNull(employees);
        this.salaries = requireNonNull(salaries);
    }

    public DeptEmp deptEmp() { return deptEmp; }
    
    public Employees employees() { return employees; }

    public Salaries salaries() { return salaries; }

    public static TupleGetter0 deptEmpGetter() {
            return DeptEmplEmployeesSalaries::deptEmp;
    }

    public static TupleGetter1 employeesGetter() {
            return DeptEmplEmployeesSalaries::employees;
    }

    public static TupleGetter2 salariesGetter() {
            return DeptEmplEmployeesSalaries::salaries;
    }

}
The DeptEmplEmployeesSalaries is simply an immutable holder of the three entities, except it has three additional “getter” methods that can be applied to extract the individual entities. Note that they return TupleGetter, which allows joins and aggregations to use optimized versions compared to just using an anonymous lambda or method reference.

Now that we have the custom tuple, we can easily define our Join relation:

   private Join joinDeptEmpSal(Departments dept) {
        // The JoinComponent is needed when creating joins
        JoinComponent jc = speedment.getOrThrow(JoinComponent.class);

        return jc.from(DeptEmpManager.IDENTIFIER)
                    // Only include data from the selected department
                    .where(DeptEmp.DEPT_NO.equal(dept.getDeptNo()))

                // Join in Employees with Employees.EMP_NO equal DeptEmp.EMP_NO
                .innerJoinOn(Employees.EMP_NO).equal(DeptEmp.EMP_NO)

                // Join Salaries with Salaries.EMP_NO) equal Employees.EMP_NO
                .innerJoinOn(Salaries.EMP_NO).equal(Employees.EMP_NO)
                      // Filter out historic salary data
                     .where(Salaries.TO_DATE.greaterOrEqual(currentDate))

                .build(DeptEmplEmployeesSalaries::new);
    }
When we are building our Join expression, we start off by first using the DeptEmp table (as we recall, this is the many-to-many relation table between Departments and Employees). For this table, we apply a where() statement so that we are able to filter out only those many-to-many relation that belongs to the department we want to appear in the join.

Next, we join in the Employees table and specify a join relation where newly joined table’s column Employees.EMP_NO equal DeptEmp.EMP_NO.

After that, we join in the Salaries table and specify another join relation where Salaries.EMP_NO equal Employees.EMP_NO. For this particular join relation, we also apply a where() statement so that we filter out salaries that are current (and not historic, past salaries for an employee).

Finally, we call the build() method and defines the constructor of our DeptEmplEmployeesSalaries class that holds the three entities DeptEmp, Employees, and Salaries.

Counting the Number of Employees for a Department

Armed with the join method above, it is very easy to count the number of Employees for a certain department in the Join stream. This is how we can go about:

public long countEmployees(Departments department) {
    return joinDeptEmpSal(department)
               .stream()
               .count();
}

Calculating a Salary Distribution Aggregation

By using the built-in Speedment Aggregator, we can express aggregations quite easily. The Aggregator can consume regular Java Collections, Java Streams from a single table as well as Join Streams without constructing intermediary Java objects on the heap. This is because it stores all its data structures completely off-heap.

We first start with creating a “result object” in the form of a simple POJO that is going to be used as a bridge between the completed off-heap aggregation and the Java heap world:

public class GenderIntervalFrequency {

    private Employees.Gender gender;
    private int interval;
    private long frequency;

    private void setGender(Employees.Gender gender) { this.gender = gender; }

    private void setInterval(int interval) { this.interval = interval; }

    private void setFrequency(long frequency) { this.frequency = frequency;}

    private Employees.Gender getGender() { return gender; }

    private int getInterval() { return interval; }
        
    private long getFrequency() { return frequency; }

}
Now that we have the POJO, we are able to build a method that returns an Aggregation like this:

public Aggregation freqAggregation(Departments dept) {

    Aggregator aggregator =

        // Provide a constructor for the "result object"
        Aggregator.builder(GenderIntervalFrequency::new)

            // Create a key on Gender
            .firstOn(DeptEmplEmployeesSalaries.employeesGetter())
            .andThen(Employees.GENDER)
            .key(GenderIntervalFrequency::setGender)

            // Create a key on salary divided by 1,000 as an integer
            .firstOn(DeptEmplEmployeesSalaries.salariesGetter())
            .andThen(Salaries.SALARY.divide(SALARY_BUCKET_SIZE).asInt())
            .key(GenderIntervalFrequency::setInterval)

            // For each unique set of keys, count the number of entitites
            .count(GenderIntervalFrequency::setFrequency)
            .build();


    return joinDeptEmpSal(dept)
        .stream()
        .parallel()
        .collect(aggregator.createCollector());

}
This requires a bit of explanation. When we invoke the Aggregator.builder() method, we provide a constructor of the “result object” that we are using as a bridge between the off-heap and the on-heap world.

After we have a builder, we can start defining our aggregation and usually the clearest way is to start off with the keys (i.e. groups) that we are going to use in the aggregation. When we are aggregating results for a Join operation, we first need to specify which entity we want to extract our key from. In this case, we want to use the employee’s gender so we invoke .firstOn(eptEmplEmployeesSalaries.employeesGetter()) which will extract the Employees entity from the tuple. Then we apply .andThen(Employees.GENDER) which, in turn, will extract the gender property from theEmployees entity. The key() method takes a method reference for a method that is going to be called once we want to actually read the result of the aggregation.

The second key is specified in much the same way, only here we apply the .firstOn(DeptEmplEmployeesSalaries.salariesGetter()) method to extract the Salaries entity instead of the Employees entity. When we then apply the .andThen() method we are using an expression to convert the salary so it is divided by 1,000 and seen as an integer. This will create separate income brackets for every thousand dollars in salary.

The count() operator simply says that we want to count the occurrence of each key pair. So, if there are two males that have an income in the 57 bracket (i.e. a salary between 57,000 and 57,999) the count operation will count those two for those keys.

Finally, in the line starting with return, the actual computation of the aggregation will take place whereby the application will aggregate all the thousands of salaries in parallel and return an Aggregation for all the income data in the database. An Aggregation can be thought of as a kind of List with all the keys and values, only that the data is stored off-heap.

Adding In-JVM-Memory Acceleration

By just adding two lines to our application, we can get a high-performance application with in-JVM-memory acceleration.

Speedment speedment = new EmployeesApplicationBuilder()
        .withUsername("...") // Username need to match database
        .withPassword("...") // Password need to match database
        .withBundle(InMemoryBundle.class) // Add in-JVM-acceleration
        .build();

        // Load a snapshot of the database into off-heap JVM-memoory   
        speedment.get(DataStoreComponent.class)
            .ifPresent(DataStoreComponent::load);

The InMemoryBundle allows the entire database to be pulled in to the JVM using off-heap memory and then allows Streams and Joins to be executed directly from RAM instead of using the database. This will improve performance and will make the Java application work more deterministically. Having data off-heap also means that data will not affect Java Garbage Collect allowing huge JVMs to be used with no GC impact.

Thanks to the In-memory acceleration, even the biggest department with over 60,000 salaries will be computed in less than 100 ms on my laptop. This will ensure that our UI stays responsive.

Building the UI in Java

Now that the data model is finished, we move on to the visual aspects of the application. This is as mentioned earlier done utilizing Vaadin, a framework which allows implementation of HTML5 web user interfaces using Java. The Vaadin framework is built on the notion of components, which could be a layout, a button or anything in between. The components are modeled as objects which can be customized and styled in an abundance of ways.

The image above describes the structure of the GUI we intend to build for our DataModel. It constitutes of nine components, out of which five read information from the database and present it to the user while the rest are static. Without further ado, let’s start configuring the UI.

A sketch showing the hierarchy of the components included in our GUI.


The Vaadin UI Layer

To integrate Vaadin in the application, we downloaded a starter pack from Vaadin to set up a simple project base. This will automatically generate a UI class which is the base of any Vaadin application.

@Theme("mytheme")
public class EmployeeUI extends UI {

    @Override // Called by the server when the application starts
    protected void init(VaadinRequest vaadinRequest) { }

    // Standard Vaadin servlet which was not modified 
    @WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
    @VaadinServletConfiguration(ui = EmployeeUI.class, productionMode = false)
    public static class MyUIServlet extends VaadinServlet { }
}

The overridden init() is called from the server when the application is started, hence this is where we soon will state what actions are to be performed when the application is running. EmployeeUI also contains MyUIServlet, which is a standard servlet class used for deployment. No modification was needed for the sake of this application.

Creation of Components

As mentioned above, all of our components will be declared in init(). This is not suggested as a best practice but works well for an application with a small scope. Although, we would like to collectively update the majority of the components from a separate method when a new department is selected, meaning those will be declared as instance variables along the way.

Application Title

We start off simple by creating a Label for the title. Since its value will not change, it can be locally
declared.

Label appTitle = new Label("Employee Application");
appTitle.setStyleName("h2");

In addition to a value, we give it a style name. Style names allow full control of the appearance of the component. In this case, we use the built-in Vaadin Valo Theme and select a header styling simply by setting the parameter to “h2”. This style name can also be used to target the component with custom CSS (for example .h2 { font-family: ‘Times New Roman; }).

Text Fields

To view the number of employees and the average salary for the selected department, we use the TextField component. TextField is mainly used for user text input, although by setting it to read-only, we prohibit any user interaction. Notice how two style name can be used by separating them with a blank space.


noOfEmployees = new TextField("Number of employees"); // Instance variable
noOfEmployees.setReadOnly(true);
// Multiple style names are separated with a blank space 
noOfEmployees.setStyleName("huge borderless"); 

This code is duplicated for the averageSalary TextField although with a different caption and variable name.

Charts

Charts can easily be created with the Vaadin Charts addon, and just like any other component, a chart Java Object with corresponding properties. For this application, we used the COLUMN chart to view gender balance and an AREASPLINE for the salary distribution.



/* Column chart to view balance between female and male employees at a certain department */
genderChart = new Chart(ChartType.COLUMN);
Configuration genderChartConfig = genderChart.getConfiguration();
genderChartConfig.setTitle("Gender Balance");

// 0 is only used as an init value, chart is populated with data in updateUI() 
maleCount = new ListSeries("Male", 0);
femaleCount = new ListSeries("Female", 0);
genderChartConfig.setSeries(maleCount, femaleCount);

XAxis x1 = new XAxis();
x1.setCategories("Gender");
genderChartConfig.addxAxis(x1);

YAxis y1 = new YAxis();
y1.setTitle("Number of employees");
genderChartConfig.addyAxis(y1);
Most of the properties associated with a chart are controlled by its configuration which is retrieved with getConfiguration(). This is then used to add a chart title, two data series, and the axis properties. For the genderChart, a simple ListSeries was used to hold the data because of its simple nature. Although for the salaryChart below, a DataSeries was chosen since it handles a larger and more complicated data sets.

The declaration of the salaryChart is very similar to that of the genderChart. Likewise, the configuration is retrieved and used to add a title and axes.

salaryChart = new Chart(ChartType.AREASPLINE);
Since both charts display data for male and females we decide to use a shared legend that we fix in the upper right corner of the salaryChart.

/* Legend settings */
Legend legend = salaryChartConfig.getLegend();
legend.setLayout(LayoutDirection.VERTICAL);
legend.setAlign(HorizontalAlign.RIGHT);
legend.setVerticalAlign(VerticalAlign.TOP);
legend.setX(-50);
legend.setY(50);
legend.setFloating(true);
Lastly, we add two empty DataSeries which will be populated with data at a later stage.

// Instance variables to allow update from UpdateUI() 
maleSalaryData = new DataSeries("Male"); 
femaleSalaryData = new DataSeries("Female");
salaryChartConfig.setSeries(maleSalaryData, femaleSalaryData);

Department Selector

The final piece is the department selector which controls the rest of the application.



/* Native Select component to enable selection of Department */
NativeSelect<Departments> selectDepartment = new NativeSelect<>("Select department");
selectDepartment.setItems(DataModel.departments());
selectDepartment.setItemCaptionGenerator(Departments::getDeptName);
selectDepartment.setEmptySelectionAllowed(false);


We implement it as a NativeSelect<T> component that calls departments(), which was previously defined in DataModel, to retrieve a Stream of Departments from the database. Next, we specify what property of Department to display in the dropdown list (default is toString()).

Since we do not allow empty selections, we set the defaultDept to the first element of the Department Stream. Note that the defaultDept is stored as a variable for later use.

/* Default department to use when starting application */
final Departments defaultDept = DataModel.departments().findFirst().orElseThrow(NoSuchElementException::new);
selectDepartment.setSelectedItem(defaultDept);

Adding the Components to the UI

So far we have only declared the components without adding them to the actual canvas. To be displayed in the application they all need to be added to the UI. This is usually done by attaching them to a Layout. Layouts are used to create a structured hierarchy and can be nested into one and other.

HorizontalLayout contents = new HorizontalLayout();
contents.setSizeFull();

VerticalLayout menu = new VerticalLayout();
menu.setWidth(350, Unit.PIXELS);

VerticalLayout body = new VerticalLayout();
body.setSizeFull();
As revealed in the code above, three layouts were used for this purpose, one horizontal and two vertical. Once the layouts are defined we can add the components.

menu.addComponents(appTitle, selectDepartment, noOfEmployees, averageSalary);
body.addComponents(genderChart, salaryChart);
contents.addComponent(menu);
// Body fills the area to the right of the menu
contents.addComponentsAndExpand(body); 
// Adds contents to the UI 
setContent(contents);
Components appear in the UI in the order they are added. For a VerticalLayout such as the menu, this means from top to bottom. Notice how the HorizontalLayout contents hold the two VerticalLayouts, placing them next to each other. This is necessary because the UI itself can hold only one component, namely contents which holds all components as one unit.

Reflecting the DataModel in the UI

Now that all visuals are in place, it is time to let them reflect the database content. This means we need to add values to the components by retrieving information from the DataModel. Bridging between our data model and EmployeeUI will be done by handling events from selectDepartment. This is accomplished by adding a selection listener as follows in init():

selectDepartment.addSelectionListener(e ->
    updateUI(e.getSelectedItem().orElseThrow()) 
);
Since updateUI() was not yet defined, that is our next task.

private void updateUI(Departments dept) { }
Here is a quick reminder of what we want updateUI() to accomplish: When a new department is selected we want to calculate and display the total number of employees, the number of males and females, the total average salary and the salary distribution for males and females for that department.

Conveniently enough, we designed our DataModel with this in mind, making it easy to collect the information from the database.

We start with the values of the text fields:

final Map<Employees.Gender, Long> counts = DataModel.countEmployees(dept);

noOfEmployees.setValue(String.format("%,d", counts.values().stream().mapToLong(l -> l).sum()));

averageSalary.setValue(String.format("$%,d", DataModel.averageSalary(dept).intValue()));
The sum of the males and females gives the total number of employees. averageSalary() returns a Double which is cast to an int. Both values are formatted as a String before being passed to the text fields.

We can also use the Map counts to populate the first graph by retrieving the separate counts for male and female.

final List<DataSeriesItem> maleSalaries = new ArrayList<>();
final List<DataSeriesItem> femaleSalaries = new ArrayList<>();
   
DataModel.freqAggregation(dept)
   .streamAndClose()
   .forEach(agg -> {
       (agg.getGender() == Gender.F ? femaleSalaries : maleSalaries)
           .add(new DataSeriesItem(agg.getInterval() * 1_000, agg.getFrequency()));
   });
Our DataModel provides an Aggregation which we can think of as a list containing tuples of a gender, a salary and a corresponding salary frequency (how many persons share that salary). By streaming over the Aggregation we can separate male and female data in two Lists containing DataSeriesItems. A DataSeriesItem is in this case used like a point with an x- and y-value.

Comparator<DataSeriesItem> comparator = Comparator.comparingDouble((DataSeriesItem dsi) -> dsi.getX().doubleValue());

maleSalaries.sort(comparator);
femaleSalaries.sort(comparator);
Before adding the data to the chart, we sort it in rising order of the x-values, otherwise, the graph will look very chaotic. Now our two sorted List<DataSeriesItem> will fit perfectly with the DataSeries of salaryChart.

//Updates salaryChart 
maleSalaryData.setData(maleSalaries);
femaleSalaryData.setData(femaleSalaries);
salaryChart.drawChart();
Since we are changing the whole data set rather than just a single point, we set the data for our DataSeries to the Lists of x and ys we just created. Unlike a change in a ListSeries, this will not trigger an update of the chart, meaning we have to force a manual update with drawChart().

Lastly, we need to fill the components with default values when the application starts. This can now be done by calling updateUI(defaultDept) at the end of init().

Styling in Java

Vaadin offers complete freedom when it comes to adding a personal feel to components. Since this is a pure Java application only the styling options available in their Java framework were used, although CSS styling will naturally give total control of the visuals.

A comparison before and after applying the ChartTheme.


To give our charts a personal touch we created a class ChartTheme which extends Theme. In the constructor, we defined what properties we would like to change, namely the color of the data series, background, legend, and text.

public class ChartTheme extends Theme {
   public ChartTheme() {
       Color[] colors = new Color[2];
       colors[0] = new SolidColor("#5abf95"); // Light green
       colors[1] = new SolidColor("#fce390"); // Yellow
       setColors(colors);

       getChart().setBackgroundColor(new SolidColor("#3C474C"));
       getLegend().setBackgroundColor(new SolidColor("#ffffff"));

       Style textStyle = new Style();
       textStyle.setColor(new SolidColor("#ffffff")); // White text
       setTitle(textStyle);
   }
}
Then theme was applied to all charts by adding this row to init():

ChartOptions.get().setTheme(new ChartTheme());

Conclusion

We have used Speedment to interface the database and Vaadin to interface the end user. The only code needed in between is just a few Java Streams constructs that declaratively describe the application logic, which grants minimal time to market and cost of maintenance.

Feel free to fork this repo from GitHub and start experimenting on your own.

Authors

Julia Gustafsson
Per Minborg

Infinite Sets in Java 9

A Set

A Set is a collection of elements whereby any given element in the Set only appears once. More formally, a set contains no pair of elements e1 and e2 such that e1.equals(e2). We can easily create Set in Java 9 like this:
and use a

final Set<Integer> s = Set.of(1, 2, 3);
System.out.println(s);

This might produce the following output:
[2, 3, 1]

The Set produced above is immutable, i.e. it cannot change and it is also finite because there is a distinct number of elements in the Set, namely three. The order in which the elements are returned via its read methods (such as stream(), iterator() and forEach()) is unspecified.

An Infinite Set

An infinite set contains an unlimited number of elements. One example of an infinite set is the set of all integers [..., -1, 0, 1, 2, ...] where an integer is not of a Java Integer class but an integer according to the mathematical definition of an integer whereby there is always a larger integer n+1 for any given integer n.

There are many infinite sets such as the set of all primes, the set of even integers, the set of fibonacci numbers etc.

For obvious reasons, we cannot precompute and store all the elements of an infinite Java Set. If we try, we would eventually run out of memory.

A fundamental question we have to ask ourselves is: Are there actually infinite sets for the Java types we have? If we have a Set<Byte> there are at most 256 elements in the Set and that is far from infinite, same reasoning goes for Short and even Integer. After all, there are only about four billion different Integer objects and if we would use a bit-map to represent membership, we could fit a Set<Integer> in just 0.5 GB. Albeit big, is not infinite.

But if we are talking about Long or String elements, we are approaching at least virtually infinite sets. To store a bitmap of all Longs would require a number of PB of internal storage. A true infinite Set would be a Set of String with all possible combination of characters [a-z] of any length.

Before we continue, I would like to mention that the code in this post is also available on GitHub as described at the very end of the post.

The ImmutableStreamSet

To move away from a paradigm where we store the elements of a Set, we could create an ImmutableStreamSet that defines the elements of the Set only through its stream() method. The ImmutableStreamSet could be defined as a FunctionalInterface like this:

@FunctionalInterface
public interface ImmutableStreamSet<E> extends Set<E> {

    // This is the only method we need to implements
    @Override
    public Stream<E> stream(); 

    @Override
    default int size() {
        return (int) stream().limit(Integer.MAX_VALUE).count();
    }

    @Override
    default boolean contains(Object o) {
        return stream().anyMatch(e -> Objects.equals(e, o));
    }

    @Override
    default boolean containsAll(Collection<?> c) {
        return (this == c) ? true : c.stream().allMatch(this::contains);
    }

    @Override
    default boolean isEmpty() {
        return !stream().findAny().isPresent();
    }

    @Override
    default <T> T[] toArray(T[] a) {
        return stream().collect(toList()).toArray(a);
    }

    @Override
    default Object[] toArray() {
        return stream().toArray();
    }

    @Override
    default Spliterator<E> spliterator() {
        return stream().spliterator();
    }

    @Override
    default Iterator<E> iterator() {
        return stream().iterator();
    }

    @Override
    default Stream<E> parallelStream() {
        return stream().parallel();
    }

    @Override
    default void forEach(Consumer<? super E> action) {
        stream().forEach(action);
    }

    // We are immutable
    @Override
    default boolean removeIf(Predicate<? super E> filter) {
        throw new UnsupportedOperationException();
    }

    @Override
    default void clear() {
        throw new UnsupportedOperationException();
    }

    @Override
    default boolean removeAll(Collection<?> c) {
        throw new UnsupportedOperationException();
    }

    @Override
    default boolean retainAll(Collection<?> c) {
        throw new UnsupportedOperationException();
    }

    @Override
    default boolean addAll(Collection<? extends E> c) {
        throw new UnsupportedOperationException();
    }

    @Override
    default boolean remove(Object o) {
        throw new UnsupportedOperationException();
    }

    @Override
    default boolean add(E e) {
        throw new UnsupportedOperationException();
    }

    static <E> ImmutableStreamSet<E> of(Supplier<Stream<E>> supplier) {
        // Check out GitHub to see how this Impl class is implemented
        return new ImmutableStreamSetImpl<>(supplier);
    }

}

Awesome, now we can create infinite sets by just providing a stream supplier like this:

    ImmutableStreamSet<Long> setOfAllLong
            = LongStream.rangeClosed(Long.MIN_VALUE, Long.MAX_VALUE)::boxed;

This will create a Set of all Long values (e.g. with 2^64 elements).  When providing a stream supplier, it is imperative to make sure to adhere to the Set property of element uniqueness.  Consider the following illegal Set:

final ImmutableStreamSet<Long> illegalSet = 
            () -> Stream.of(1l, 2l, 1l);
Clearly, 11 occurs two times in the set which makes this object violate the Set requirements.

As we will see, it would be better to create concrete classes of the infinite sets we are considering. One particular problem with the default implementation above is that the contains() method might be very slow. Read the next chapters and find out why and how to solve it.

PositiveLongSet

Let us assume that we want to create a Set with all the positive long values and that we want to be able to use the Set efficiently with other sets and objects. This is how we can go about:

public final class PositiveLongSet implements ImmutableStreamSet<Long> {

    public static final PositiveLongSet INSTANCE = new PositiveLongSet();

    private PositiveLongSet() {
    }

    @Override
    public Stream<Long> stream() {
        return LongStream.rangeClosed(1, Long.MAX_VALUE).boxed();
    }

    @Override
    public int size() {
        return Integer.MAX_VALUE;
    }

    @Override
    public boolean contains(Object o) {
        return SetUtil.contains(this, Long.class, other -> other > 0, o);
    }

    @Override
    public boolean isEmpty() {
        return false;
    }

    @Override
    public String toString() {
        return SetUtil.toString(this);
    }

}

Note how we comply with the formal requirement in the method size() where we return Integer.MAX_VALUE even though the Set is much larger. If Set had been defined today, it is likely that size() would have returned a long instead of an int. But in the beginning of the 90s, internal RAM was usually less than 1 GB. We are using two utility methods in the class:

The SetUtil.toString() takes a Set, iterates over the first eight elements and returns a String representation of those elements.

The SetUtil.contains() method takes a Set, the Element type class (here Long.class) and a Predicate that is called if the object we are comparing agains is of the given Element type class (if the object we are comparing against is null or of another type, then trivially the Set does not contain the object).

Here is how the SetUtil looks like:

final class SetUtil {

    private static final int TO_STRING_MAX_ELEMENTS = 8;

    static <E> String toString(Set<E> set) {
        final List<String> first = set.stream()
            .limit(TO_STRING_MAX_ELEMENTS + 1)
            .map(Object::toString)
            .collect(toList());

        final String endMarker = first.size() > TO_STRING_MAX_ELEMENTS ? ", ...]" : "]";

        return first.stream()
            .limit(TO_STRING_MAX_ELEMENTS)
            .collect(
                joining(", ", "[", endMarker)
            );
    }

    static <E> boolean contains(
        final Set<E> set,
        final Class<E> clazz,
        final Predicate<E> predicate,
        final Object o
    ) {
        if (o == null) {
            return false;
        }
        if (!(clazz.isAssignableFrom(o.getClass()))) {
            return false;
        }
        final E other = clazz.cast(o);
        return predicate.test(other);
    }

}

Armed with the classes ImmutableStreamSet and SetUtil we can now easily create other infinite sets like PostitiveEvenLongSet (not shown hereunder, try writing it by yourself), PrimeLongSet (containing all primes that can be represented by a Long) and even FibonacciLongSet (containing all fibonacci numbers that can be represented by a Long). Here is how these classes may look like:

PrimeLongSet


public final class PrimeLongSet implements ImmutableStreamSet<Long> {

    public static final PrimeLongSet INSTANCE = new PrimeLongSet();

    private PrimeLongSet() {
    }

    private static final LongPredicate IS_PRIME =
        x -> LongStream.rangeClosed(2, (long) Math.sqrt(x)).allMatch(n -> x % n != 0);

    @Override
    public Stream<Long> stream() {
        return LongStream.rangeClosed(2, Long.MAX_VALUE)
            .filter(IS_PRIME)
            .boxed();
    }

    @Override
    public int size() {
        return Integer.MAX_VALUE; 
    }

    @Override
    public boolean contains(Object o) {
        return SetUtil.contains(this, Long.class, IS_PRIME::test, o);
    }

    @Override
    public boolean isEmpty() {
        return false;
    }

    @Override
    public String toString() {
        return SetUtil.toString(this);
    }

}

FibonacciLongSet


public final class FibonacciLongSet implements ImmutableStreamSet<Long> {

    public static final FibonacciLongSet INSTANCE = new FibonacciLongSet();

    private FibonacciLongSet() {
    }

    @Override
    public Stream<Long> stream() {
        return Stream.concat(
            Stream.of(0l),
            Stream.iterate(new Fibonacci(0, 1), Fibonacci::next)
                .mapToLong(Fibonacci::getAsLong)
                .takeWhile(fib -> fib > 0)
                .boxed()
        );
    }

    @Override
    public int size() {
        return 92;
    }

    @Override
    public boolean contains(Object o) {
        return SetUtil.contains(
            this,
            Long.class,
            other -> stream().anyMatch(fib -> Objects.equals(fib, other)),
            o
        );
    }

    @Override
    public boolean isEmpty() {
        return false;
    }

    @Override
    public String toString() {
        return SetUtil.toString(this);
    }

    private static class Fibonacci {

        final long beforeLast;
        final long last;

        public Fibonacci(long beforeLast, long last) {
            this.beforeLast = beforeLast;
            this.last = last;
        }

        public Fibonacci next() {
            return new Fibonacci(last, last + beforeLast);
        }

        public long getAsLong() {
            return beforeLast + last;
        }

    }

}

Note how we are using Stream::takeWhile to break the stream when long wraps around to a negative value. Arguably, we are "cheating" when we precompute and provide a size of 92 but otherwise size() would have been a bit slower.

Stitching it all up

By providing an interface with static providers to instances of these classes, we can encapsulate our predefined sets and make sure that there are only one instance of them in the JVM like this:
public interface Sets {

    static Set<Long> positiveLongSet() {
        return PositiveLongSet.INSTANCE;
    }

    static Set<Long> positiveEvenLongSet() {
        return PositiveEvenLongSet.INSTANCE;
    }

    static Set<Long> primeLongSet() {
        return PrimeLongSet.INSTANCE;
    }

    static Set<Long> fibonacciLongSet() {
        return FibonacciLongSet.INSTANCE;
    }

}

We could also encapsulate our code in a Java 9 module to make sure only the classes Sets and ImmutableStreamSet are visible by exposing them in the projects top-most package and putting all the other classes in a package named "internal" (which is not exposed). This is how our module-info.java could look like provided that the two exposed classes are in the com.speedment.infinite_sets and the implementation classes in a package like com.speedment.infinite_sets.internal:

module-info.java

module com.speedment.infinite_sets {
    exports com.speedment.infinite_sets;
}

Trying it Out

We can now create another module that is using our infinite sets by first declaring usage of our existing module like this:

module-info.java

module Infinite_sets_app {
    requires com.speedment.infinite_sets;
}


And then we have access to the exposed parts of the module. here is one way of trying out the infinite sets:

import static com.speedment.infinite_sets.Sets.*;

public class Main {

    public static void main(String[] args) {

        Stream.of(
            Set.of(1, 2, 3),
            positiveLongSet(),
            positiveEvenLongSet(),
            primeLongSet(),
            fibonacciLongSet()
        ).forEachOrdered(System.out::println);

        // This actually completes fast due to identity equality
        positiveLongSet().containsAll(positiveLongSet());

    }

}

This might produce the following output:


[3, 2, 1]
[1, 2, 3, 4, 5, 6, 7, 8, ...]
[2, 4, 6, 8, 10, 12, 14, 16, ...]
[2, 3, 5, 7, 11, 13, 17, 19, ...]
[0, 1, 2, 3, 5, 8, 13, 21, ...]


Engage on GitHub


The source code in this post is available on GitHub here.




Game, Set and Match...



You can read a Java Set Example and learn more about Set fundamentals.