Mill
Java library to make run-of-the-mill tasks more elegant. Compatible with Java 8+.
Install / Use
/learn @scottashipp/MillREADME
mill 

Java library to make run-of-the-mill tasks more elegant.
Note: This library is deprecated.
Update March, 2020. I will no longer be maintaining Mill. There are now other libraries with more widespread adoption and some actual funding and a set of active maintainers. (See StreamEx).
In addition, Mill is now the name of a build tool for Java and Scala.
Nevertheless, it is still useful at providing some examples of the things you can add to your own codebase to improve working with Java streams or lambdas.
Compatible with Java 8+.
In this README
- How to add Mill to your application
- Some examples
- Contribution guidelines
- Where to find help
- License
How to add Mill to your application
If you are using Maven or another supported dependency management tool, you can use Jitpack to add Mill to your application.
Maven
First, add Jitpack (if you haven't already) to the repositories section of your Maven pom.
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
Second, add Mill to the dependencies section of your Maven pom.
<dependency>
<groupId>com.github.scottashipp</groupId>
<artifactId>mill</artifactId>
<version>{{latest version tag}}</version>
</dependency>
Please see the releases to find the latest version tag, such as v1.0.
Other
Please visit Jitpack.io for instructions for other popular tools like Gradle and sbt.
Some examples
The following are just a few of the things you'll find in mill.
Fluent interface for null conditional operations
If you have a chain of calls into an object graph (such as user.addresses().billingAddress().zipCode()) and you care about avoiding a NullPointerException, you might write code like this:
// standard java
if(user != null) {
UserAddresses userAddresses = user.addresses();
if(userAddresses != null) {
Address billingAddress = userAddresses.billingAddress();
if (billingAddress != null) {
return billingAddress.zipCode();
}
}
}
With the NullSafe class, you can convert the above to:
String zipCode = NullSafe.of(user)
.call(User::addresses)
.call(UserAddresses::billingAddress)
.call(Address::zipCode)
.get();
Join a stream of non-string objects together into a string
Stream<Holiday> majorUsHolidays = Stream.of(newYears, easter, independenceDay, thanksgiving, christmas);
// standard Java requires us to first map Object::toString
majorUsHolidays.map(Object::toString).collect(Collectors.joining(", "));
// mill doesn't require the extra map call
majorUsHolidays.collect(MoreCollectors.joining(", "));
Filter a stream on multiple criteria
Given the following stream, suppose we want to find names with a maximum length of 12 and in the alphabetical range A-M.
Stream<String> engineeringTeam = Stream.of(
null, "Mackenzie Miller", "Jane Brown", "Shannon Smith",
"Riley Joson", "Tracy Roberts", "Frankie Chen",
null, null, "Emerson Lorrie", "Mukesh Jaffery",
"Jean Limon", "Finley Vonnegut", ""
);
With standard Java we might apply the multiple filters in order like this.
// standard java requires multiple filters
String staffAtoMWithShortNames = engineeringTeam
.filter(Objects::nonNull)
.filter(((Predicate<String>)String::isEmpty).negate())
.filter(s -> s.compareTo("A") > 0 && s.compareTo("M") < 0)
.filter(s -> s.length() <= 12)
.collect(Collectors.joining(", "));
// outputs "Jane Brown, Frankie Chen, Jean Limon"
Mill has predicates for these common tasks, allowing fluent predicate composition. This can be very readable with a couple of static imports.
// mill
import static com.scottshipp.code.mill.stream.ComparablePredicates.isBetween;
import static com.scottshipp.code.mill.stream.StringPredicates.*;
Predicate<String> maxLen12AndRangeAToM = nonNull().and(nonEmpty())
.and(isBetween("A", "M"))
.and(withMaximumLength(12));
String staffAtoMWithShortNames = engineeringTeam
.filter(maxLen12AndRangeAToM)
.collect(Collectors.joining(", "));
Filter out elements in a stream that aren't found in other streams
// standard Java, one way to do it
Set<String> distinctItemsInStream2 = stream2.collect(Collectors.toSet());
Set<String> distinctItemsInStream3 = stream3.collect(Collectors.toSet());
Set<String> distinctItemsInStream4 = stream4.collect(Collectors.toSet());
Stream<String> results = stream1.filter(s -> !distinctItemsInStream2.contains(s));
results.filter(s -> !distinctItemsInStream3.contains(s));
results.filter(s -> !distinctItemsInStream4.contains(s));
// mill
StreamOps.intersection(stream1, stream2, stream3, stream4);
More Examples
More examples can be found in the API Documentation.
(To find the API documentation see the Where to find help section below.)
Contribution guidelines
Please contribute by forking the project and opening a pull request.
Where to find help
First, check the API documentation! This is a Maven project so you can always generate the latest API documentation with the javadoc tool as follows:
- Clone the repo to your local system.
- Run
mvn javadoc:javadoc - Look in the target/site/apidocs folder.
You can also check the hosted documentation at code.scottshipp.com. Important! There is no guarantee that this link has the latest documentation.
If the documentation doesn't answer your question, and you still need general help using mill, you can contact me via code.scottshipp.com.
If you found an issue with the library, you are welcome to file an issue on github or open a merge request.
License
Mill is made available under the MIT License. See the LICENSE file for more details.
