A tiny library for working with tuples as first-class citizens, in Java.
This library has been designed to play well with Java 8+'s collections and streams APIs and fits nicely into the functional programming paradigm familiar to Java 8 developers.
This code is well tested, and is easy to parse.
To download the code and use it in your project, you can either clone this project and start using it:
$ git clone https://github.com/mmnaseri/tuples4j.git
or you can add a maven dependency since it is now available in Maven central:
<dependency>
<groupId>com.mmnaseri.utils</groupId>
<artifactId>tuples4j</artifactId>
<version>${tuples4j.version}</version>
</dependency>
To get started, use one of the many utility methods under the Tuple
interface as the entrypoint:
ThreeTuple<Object, Integer, String, Boolean> t = Tuple.three(1, "x", true);
// Or, if you prefer to get rid of the type-safety mechanism:
Tuple<?> t = Tuple.three(1, "x", true);
Tuples are immutable, so, feel free to pass them around:
// Create a bunch of one-tuples.
Set<Tuple<?>> tuples = Stream.of(Tuple.of(1), Tuple.of(2))
// Extend them each to two-tuples.
.map(Tuple.extendOne("Hello")) // this is the same as: .map(t -> t.extend("Hello"))
// Change the first element.
.map(t -> t.first(Objects::toString))
// Sort by the second element.
.sorted(Comparator.comparing(HasSecond::second))
// Collect them all as a set.
.collect(toSet());
This project is regular built against and tested with OpenJDK 8 - 14. Click on the build badge above to find out more.
This library offers a wide set of tuples, from size 0 (the empty tuple), to size 12 and beyond.
The build-in tuple sizes are:
EmptyTuple
,OneTuple
,TwoTuple
, also available asPair
andKeyValue
,ThreeTuple
,FourTuple
,FiveTuple
,SixTuple
,SevenTuple
,EightTuple
,NineTuple
,TenTuple
,ElevenTuple
,TwelveTuple
.
Beyond these, there is the ThirteenOrMoreTuple
class which holds data for thirteen or more elements.
All elements are bound to an individual type argument, meaning that you can carry that type information and will not need to cast the items to the actual types.
Moreover, each tuple type has a tightly bound, generically defined constructor and factory method, that can be used if you want to enforce an upperbound on the datatypes. For instance, to create a number-only three-tuple, you can write:
ThreeTuple<Number, Integer, Double, Long> tuple = ThreeTuple.of(1, 1.2D, 3L);
All tuples have their own implementation of these three basic methods. These are also well-tested and you can consult the tests for examples.
For instance:
Tuple.of(1, 2, 3).equals(Tuple.of(1, 2, 3, "a").dropFourth()) // => true
Some applications might require access to datum by labels. In this case, you can easily label your tuples like so:
LabeledTuple<?> t = Tuple.of("USA", 3.2).withLabels("country", "population");
System.out.println(t.get("population")); // => 3.2
All tuples which have a fixed dimension, have facades that allow named interactions. For instance,
a four-tuple, would implement all the facades for HasFirst
, HasSecond
, HasThird
, and HasFourth
.
This means that to get the second element in the tuple, instead of writing tuple.get(1)
, you can
write tuple.second()
. This also applies to changing values on the tuples. Instead of calling
tuple.change(2, "a")
, you can write tuple.third("a")
. Not only is this syntax semantically
stronger, but also it can help preserve type arguments on the tuple itself.
The companion module tuples4j-reflection
can be used to get tuples represented as classes:
ReflectiveTuple<?> reflectiveTuple = ReflectiveTuple.of(tuple);
Customer customer = reflectiveTuple.as(Customer.class);
String name = customer.name();
BigDecimal income = customer.income();
You can do all sorts of customizations in how a value is read from the tuple and conveyed as a method's return value:
public interface Customer {
String name();
BigDecimal income();
State state();
@Provided(by = IncomeBracketProvider.class)
IncomeBracket incomeBracket();
double raise();
default nextYearIncome() {
return income().multiply(raise());
}
enum State {
TX, WA, CA, OR
}
}
Look at tuples4j-reflection//test//model/Country.java for a better example of the sort of things you can do with the reflective code.
To use the reflection module, get yourself a copy by either cloning this project or using Maven:
<dependency>
<groupId>com.mmnaseri.utils</groupId>
<artifactId>tuples4j-reflection</artifactId>
<version>${tuples4j.version}</version>
</dependency>
Contributions are more than welcome :)
To build the code and/or to contribute, you can use the provided Dockerfile
image. This image sets up an Ubuntu Bionic (18.04 LTS) with OpenJDK 8. This is the minimum required environment for the project to work.
You can get a working Docker image by running:
docker build -t tuples4j:jdk8 .
docker run -it tuples4j:jdk8