Java library to make run-of-the-mill tasks more elegant.
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+.
- How to add Mill to your application
- Some examples
- Contribution guidelines
- Where to find help
- License
If you are using Maven or another supported dependency management tool, you can use Jitpack to add Mill to your application.
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
.
Please visit Jitpack.io for instructions for other popular tools like Gradle and sbt.
The following are just a few of the things you'll find in mill.
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();
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(", "));
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(", "));
// 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 can be found in the API Documentation.
(To find the API documentation see the Where to find help section below.)
Please contribute by forking the project and opening a pull request.
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.
Mill is made available under the MIT License. See the LICENSE file for more details.