Adding some features which would be normally nice to have in Java
You can get it via Gradle:
compile 'com.github.matejtymes:javafixes:1.4.2.0'
or Maven
<dependency>
<groupId>com.github.matejtymes</groupId>
<artifactId>javafixes</artifactId>
<version>1.4.2.0</version>
</dependency>CountDownLatch is great but I always missed the possibility to countUp and reuse it or use it if initial count is not know upfront. Phaser should fulfil this but can only count up to 65,535. And this is exactly where ReusableCountLatch comes in to save us as it can count up to 2,147,483,647.
ReusableCountLatch latch = new ReusableCountLatch(); // creates latch with initial count 0
ReusableCountLatch latch = new ReusableCountLatch(10); // creates latch with initial count 10
latch.increment(); // increments counter
latch.decrement(); // decrement counter
latch.waitTillZero(); // blocks until counts falls to zero
boolean succeeded = latch.waitTillZero(200, MILLISECONDS); // waits for up to 200 milliseconds until count falls to zero
int count = latch.getCount(); // gets actual countThe ReusableCountLatch exposes a method to get its count so you can monitor its progress. Important thing to note is that the count can't go bellow 0 and if and attempt is made to initialize it with negative value it throws an exception. Decrementing 0 count will NOT throw an exception but the count will stay on 0 instead.
Runner is a reusable executor that allows you to wait until submitted tasks (of type Runnable, Callable or Task) have finished or failed.
Great if number of scheduled task is not known upfront but you want to wait till all of them finish.
Runner runner = Runner.runner(numberOfThreads);
runner.runIn(2, SECONDS, callable);
runner.run(callable);
// blocks until all tasks are finished (or failed)
runner.waitTillDone();
// and reuse it
runner.runRunnableIn(500, MILLISECONDS, runnable);
runner.waitTillDone();
// or just repeat tasks until shutdown is triggered
runner.run(shutdownInfo -> {
while (shutdownInfo.wasShutdownTriggered() == false) {
// do some cyclical task
}
})
runner.shutdownAndAwaitTermination();In case you would like to monitor task newly submitted to existing scheduled executor use MonitoringTaskSubmitter instead.
Adds ability to synchronize code on different objects for which the .equals() == true (e.g.: account number)
Synchronizer<AccountId> synchronizer = new Synchronizer();
...
// first thread
synchronizer.synchronizeOn(accountId("accAAA"), () -> {
long balance = loadBalance("accAAA")
if (balance > 10_000) {
decrementBalance("accAAA", 10_000)
}
})
...
// second thread
synchronizer.synchronizeOn(accountId("accAAA"), () -> {
long balance = loadBalance("accAAA")
if (balance > 2_000) {
decrementBalance("accAAA", 2_000)
}
})
...
// third thread - won't be blocked by previous threads
synchronizer.synchronizeOn(accountId("accXYZ"), () -> {
long balance = loadBalance("accXYZ")
if (balance > 3_500) {
decrementBalance("accXYZ", 3_500)
}
})As both first and second thread are for the same id (accAAA) the second thread will be blocked until the first one finishes (so only one operation will run on the account accAAA).
On the other hand the third thread won't be blocked by either the first or the second thread and will run with them in parallel as it is executed for a different id (accXYZ).
Allows to create objects whose initialization is called only once we call it's value() method.
Lazy<HeavyObject> lazyValue = lazy(() -> heavyInitializationMethod());
lazyValue.isInitialized(); // false - as not initialized yet
HeavyObject actualValue = lazyValue.value(); // calls heavyInitializationMethod()
lazyValue.isInitialized(); // true
lazyValue.value(); // does NOT call heavyInitializationMethod() anymore (as already initialized)
Lazy<String> derivedValue = lazyValue.map(heavyObject -> heavyObject.someValueOnHeavyObject());Once initialization is successful the value is cached and initialization is not called anymore. This class is thread/concurrent safe and guarantees to execute only one successful initialization.
Unbiased implementation of Either. Usable if you'd like to return one of two different types.
Here are some example usages:
Either<Exception, Response> result = methodReturningEither();
String outcomeMessage = result
.fold(
exception -> exception.getMessage(),
response -> response.getBody().toString(),
);
if (result.isRight()) {
System.out.println("Yeaaah, we've got a response");
}
ResponseBody body = result
.handleRight(response -> log(response)) // if we had right value
.mapRight(response -> response.getBody())
.ifLeftThrow(exception -> exception) // if we had an exception
.swap()
.getLeft();Adds equals, hashCode and toString methods to domain objects:
public class User extends DataObject {
public final String firstName;
public final String lastName;
public User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
new User("Terry", "Pratchett").equals(new User("Terry", "Pratchett"))Immutable Single value holder. Microtypes help you with error-prone multi-parameter methods like for example constructor with 6 Strings parameters, where you can provide typed parameters instead, like: AccountNumber, UserId, Nationality, ...
It also implements equals, hashCode and toString.
public class UserId extends Microtype<String> {
public UserId(String value) {
super(value);
}
public static UserId userId(String value) {
return (value == null) ? null : new UserId(value);
}
}
userId("mtymes").equals(userId("bjohnes"));
userId("mtymes").value();
String surname = userId("mtymes").map(value -> value.substring(1));Microtype can't contain a null value and initialization with null value will throw an IllegalArgumentException
Immutable Two / Three values holder. Each Tuple / Triple extends DataObject so methods equals, hashCode and toString are provided by default.
Tuple<UserId, User> tuple = tuple(
new UserId("mtymes"),
new User("Matej", "Tymes")
);
// you can access individual values
UserId userId = tuple.a;
User user = tuple.b;
// or map them into something else
String message = tuple.map(
(userId, user) -> user.firstName + " " + user.lastName + "'s ID is: " + userId.value()
);Tuple / Triple can contain null values.
If you have a value that changes over time and want to have derived values that reflects these changes, you could use this hierarchy of classes. (great if you have a config that changes during runtime, e.g.: db connection, memcached location)
// mutable wrapper of connection details
MutableValue<ConnectionDetails> connectionDetails = mutableValue(
originalConnectionDetails()
);
// database connection derived from connection details
// each time connection details change this wrapper will create/refer to a new connection
ChangingValue<DbConnection> dbConnection = connectionDetails.mapValueBuilder(
details -> connectTo(details)
)
// and use this to dispose old connection if new one will be created
.withDisposeFunction(
connection -> releaseConnection(connection)
)
// and call this when value changes
.withEachPotentialValueHandler(
handleUsedValue(value -> System.out.println("we have a new connection"))
)
.build();
// simple mapping - fetching database table using current connection
ChangingValue<DbTable> usersTable = dbConnection.mapValue(
connection -> connection.getDbTable("users")
);
// this will be executed on initial database connection
int recordCount = usersTable.mapCurrentValue(
table -> table.getRecordsCount()
);
usersTable.forCurrentValue(
table -> table.deleteRecord("agent Smith")
);
// update of connection details
connectionDetails.updateValue(
newConnectionDetails()
);
// table in NEW/updated connection is used to get the records count instead
int recordCount2 = usersTable.mapCurrentValue(
table -> table.getRecordsCount()
);In all the cases where multiple items have to be collected just so they can be iterated over in some later stage. More performant or memory efficient than ArrayList or LinkedList while having constant (O(n)) performance on inserting last item as well as removing the first one.
Memory efficient and performant Queue for collecting and retrieving bytes. Great if you want to process upfront undefined amount or big batch of bytes.
ByteQueue queue = new ByteQueue(); // or new ByteQueue(pageSize); - default page size is 4kb (each time a page is filled a new one is created)
// storing data
byte singleByte = ... ;
queue.addNext(singleByte);
byte[] byteArray = ... ;
queue.addNext(byteArray);
byte[] byteArray = ... ;
int offset = ... ;
int length = ... ;
queue.addNext(byteArray, offset, length);
// info about data
int size = queue.size();
boolean isEmpty = queue.isEmpty();
boolean hasNext = queue.hasNext();
// polling data - removes from collection
// streams through all bytes and removes them from Queue as they are being read
ByteIterator pollingIterator = queue.pollingIterator();
while(pollingIterator.hasNext()) {
byte singleByte = pollingIterator.readNext();
// or
byte[] byteArray = ... ;
int removedByteCount = pollingIterator.readNext(byteArray);
// or
byte[] byteArray = ... ;
int offset = ... ;
int length = ... ;
int removedByteCount = pollingIterator.readNext(byteArray, offset, length);
}
byte readByte = queue.pollNext();
byte[] byteArray = ... ;
int removedByteCount = queue.pollNext(byteArray);
byte[] byteArray = ... ;
int offset = ... ;
int length = ... ;
int removedByteCount = queue.pollNext(byteArray, offset, length);
// peeking at data - doesn't remove from collection
// allows you to stream through all bytes without removing them from queue
ByteIterator peekingIterator = queue.peekingIterator(); // or queue.iterator() is peeking as well
while(peekingIterator.hasNext()) {
byte singleByte = peekingIterator.readNext();
// or
byte[] byteArray = ... ;
int readBytesCount = peekingIterator.readNext(byteArray);
// or
byte[] byteArray = ... ;
int offset = ... ;
int length = ... ;
int readBytesCount = peekingIterator.readNext(byteArray, offset, length);
}
// these operations always look at the first byte/s - even on subsequent calls
byte firstByteInQueue = queue.peekAtNext();
byte[] firstBytesInQueue = ... ;
int readBytesCount = queue.peekAtNext(firstBytesInQueue);
byte[] firstBytesInQueue = ... ;
int offset = ... ;
int length = ... ;
int readBytesCount = queue.peekAtNext(firstBytesInQueue, offset, length);
// getting back a byte array (use only when streaming trough data is not possible as it creates additional byte array)
byte[] allReadBytes = queue.peekAtAllBytes();
byte[] allRemovedBytes = queue.pollAllBytes();There are currently 2 issues with ByteArrayOutputStream.
- collected data can't be directly transformed into InputStream
- each time the wrapped byte buffer has to be increased it creates another byte array and has to copy whole data again - which can be costly (from memory and performance point of view for big files)
ByteQueueOutputStream is implemented as a wrapper around ByteQueue which maintains a linked list of byte arrays (by default size of buffer is 4kb).
Also it can be transformed into an peeking (will keep read bytes) or polling (will remove read bytes) InputStream of type ByteQueueInputStream (without need to copy the bytes) or you can get the underlying ByteQueue and access the underlying bytes directly.
ByteQueueOutputStream bqoStream = new ByteQueueOutputStream(); // you can pass in your preferred ByteQueue as well
// fill with bytes, e.g.:
bqoStream.write(text.getBytes(charsetName));
bqoStream.write(singleByte);
...
stream.close();
InputStream collectedBytesStream = bqoStream.toInputStream(removeReadBytesFromQueueBoolean); // no additional memory is allocated for collected bytes
// or = bqoStream.toPeekingInputStream() - will not remove read bytes (so can provide multiple input streams over and over again
// or = bqoStream.toPollingInputStream() - will remove read bytes
// or = new ByteQueueInputStream(bqoStream.getByteQueue(), removeReadBytesFromQueue)
ByteQueue byteQueue = bqoStream.getByteQueue(); // get the underlying ByteQueue
byte[] allBytesButStillKeptInTheQueue = byteQueue.peekAtAllBytes();
byte[] allBytesButRemovedFromTheQueue = byteQueue.pollAllBytes();
just for comparison, when collecting data (in jdk15) we need:
| data size | ByteArrayOutputStream | ByteQueueOutputStream |
|---|---|---|
| 1024 MB | 2.00 seconds / 2192 MB heap | 1.18 seconds / 1067 MB heap |
| 512 MB | 1.10 seconds / 1155 MB heap | 0.66 seconds / 535 MB heap |
| 128 MB | 0.25 seconds / 264 MB heap | 0.24 seconds / 135 MB heap |
Introducing new class Decimal, that should fix the troubles we're currently facing when dealing with BigDecimal. The new class was created as BigDecimal's behavior can't be changed/patched because of backwards compatibility.
The advantages it provides are:
equalsreflects thecompareTobehavior (plushashCodeis fixed respectively)
assertThat(decimal("-1.2").equals(decimal("-1.200")), is(true));
assertThat(decimal("-1.2").hashCode(), equalTo(decimal("-1.200").hashCode()));-
sensible defaults - in case rounding is needed (division, number de-scaling and de-precisioninig)
Decimaluses roundingHALF_UP(the one we used in school) as default (but you can pass in your own rounding mode in case you want different result). Also the division produces by default results with precision of maximum 34 significant digits (once again you can pass your own limit, or define limit in number of decimal digits) -
readable - able to use underscores during creation to improve readability (as in Java 7). Also can use short creation method
d(...):
Decimal value = decimal("29_013_903_171.22");
Decimal sum = d("0.456").plus(value);- groovy and kotlin operators:
def monthlyInterest = d("129_550.00") * d("0.03") / d("12");
def totalDebt = d("129_550.00") + monthlyInterest * d("36"); val monthlyInterest = d("129_550.00") * d("0.03") / d("12");
val totalDebt = d("129_550.00") + monthlyInterest * d("36");- handy constants - one of the confusing thing about
BigDecimalfor newcomers is what is the difference between scale and precision. To ease the understandingDecimalprovides readable constants on theScaleandPrecisionclasses:
d("123.4698").descaleTo(_2_DECIMAL_PLACES); // = 123.47
d("29_013_943_171.22").deprecisionTo(_7_SIGNIFICANT_DIGITS); // = 29_013_940_000
d("125_455_315").descaleTo(SCALE_OF_THOUSANDS); // = 125_455_000
d("125_455_315").descaleTo(SCALE_OF_MILLIONS); // = 125_000_000- non-confusing creation -
Decimalalways uses factory methods for creation, whileBigDecimalsometimes uses constructor and sometimes factory method:
// Decimal creation
decimal(intValue);
decimal(longValue);
decimal(longValue, scale);
decimal(bigIntegerValue);
decimal(bigIntegerValue, scale);
decimal(stringValue);
// Decimal creation - short syntax
d(intValue);
d(longValue);
d(longValue, scale);
d(bigIntegerValue);
d(bigIntegerValue, scale);
d(stringValue);
// BigDecimal creation
new BigDecimal(intValue);
new BigDecimal(longValue);
BigDecimal.valueOf(longValue, scale);
new BigDecimal(bigIntegerValue);
new BigDecimal(bigIntegerValue, scale);
new BigDecimal(stringValue);-
can evolve without affecting you - creation using only static factory methods doesn't expose defined types (you always refer to them as
Decimal), so that the library can evolve without any changes needed on the users/client side. -
extendible (although not by you :D ) -
Decimalis an abstract class, and currently supports two subtypesLongDecimal(for number with precision up to 19 digits - backed bylong) andHugeDecimalfor everything else (backed byBigInteger). The library handles the transitions between them seamlessly when doing math operation and always uses the least memory consuming type. There are plans the introduce additional typesInfinityDecimalandNANDecimal(that will be disabled by default)
It is possible that you'll miss some math functions. To implement your own you can use these Decimal methods:
decimal.signum()- will return -1 for negative value, 0 for zero and 1 for positive valuedecimal.scale()- will return you the number of decimal digitsdecimal.unscaledValue()- will return the unscaled value which can be of these two types: Long or BigIntegerdecimal.precision()- will return the number of significant digits
Also any improvements in forms of patches will be welcomed.