This library employs builder and fluent design patterns to enable easy iteration and manipulation of Android sparse collections.
##Version
##Overview
The Android platform introduced a few collection classes based on the sparse array implementation. Although they bring improved performance and optimized memory usage to the classic Java collections world, they sometimes become awkward to use when implementing the most basic pieces of code. For example, good old iteration is not supported, so that, while all Java developers are by now used to the short and elegant syntax:
for (final Entry<Integer,MyClass> entry : map.entrySet()) {
doSomethingWithKey(entry.getKey());
doSomethingWithValue(entry.getValue());
}
when employing Android sparse collections, you quickly end up in rewriting one time too often the same ugly code:
final int size = collection.size();
for (int i = 0; i < size; ++i) {
doSomethingWithKey(collection.keyAt(i));
doSomethingWithValue(collection.valueAt(i));
}
The autocomplete feature of most Java or Android IDEs is not of much help here...
The purpose of this library is to provide an easy way to iterate through the elements of the Android sparse collections and manipulate them in the while.
##Usage
The specific sparse collection can be iterated both by using the classic Java syntax:
for (final SparseArrayEntry<String> entry: SparseCollections.iterate(array)) {
// ... do your stuff ...
}
Through a more fluent syntax, like:
SparseCollections.iterate(array)
.only()
.from(4)
.forEach((element, index) -> {
// ... do your stuff ...
});
Or even by mixing the two:
for (final SparseArrayEntry<String> entry: SparseCollections.iterate(array)
.only()
.first(3)) {
// ... do your stuff ...
}
As shown in the above examples inclusion and exclusion filters can be applied to the iteration. Besides, multiple filters can be concatenated so to include in the iteration only the elements matching specific requirements. In order to fully understand the filter concatenation behavior, note that the output of the following code:
final SparseArrayCompat<String> array = new SparseArrayCompat<String>();
for (int i = 0; i < 5; i++) {
array.append(i, String.valueOf(i));
}
SparseCollections.iterate(array)
.only()
.first(3)
.only()
.last(2)
.reverse()
.forEach((element, index) -> {
System.out.println(element.getValue());
});
will be:
2
1
The Filters class already provides several filter implementations (like last N elements or first N elements), but additional custom filters can be easily implemented and concatenated with the existing ones.
The iterable implementation is not thread safe (unless differently specified) and not fail-fast, that is, if the wrapped collection is changed during the iteration no exception will be thrown, and further call to the iterator or to the entries methods might lead to unexpected results.
The entries returned by the iterator can be safely accessed only inside the iteration loop, since they have direct access to the wrapped sparse collection. This is an explicit design choice to try preserving both highest performance and lowest memory consumption. In case the caller needed to retain an entry instance outside the loop, it must create an immutable or parcelable copy and retain that instead:
final SparseArrayCompat<String> array = new SparseArrayCompat<String>();
for (int i = 0; i < 5; i++) {
array.append(i, String.valueOf(i));
}
IntSparseObjectEntry<String> myEntry;
for (final SparseArrayEntry<String> entry: SparseCollections.iterate(array)) {
if ("3".equals(entry.getValue())) {
myEntry = entry.toImmutable();
break;
}
}
##Documentation
Complete Javadoc with insights and examples are available.
The project contains an additional sample module showing how to implement an enhanced version of a SimpleArrayMap supporting parcelable serialization, iteration and proper equals() implementation (see issue 68406).
##Performance
The iterator implementation is generally faster than the correspondent Java map collection, but, in any case, slower than directly accessing the Android collection object. In fact, there could be nothing faster than reading an element from a primitive array by its index. It is also true that the sparse collections were designed for best performance when holding a relative small number of elements. In such cases iteration is for sure not the bottleneck, so you'd better have flexibility and readability than speed.
##Further development
When Java 8 will be ported to Android, the library might need to be updated in order to support the new Stream APIs.
##Build instructions
To generate the library JAR just run on a command line:
gradlew jarR
For the Javadocs run:
gradlew javadocR
To run the tests on a connected device:
gradlew cAT
To run the Android lint:
gradlew lR
For additional commands please refer to the Gradle Android Plugin User Guide.
##Dependencies
####Source dependencies
- Android SDK (Terms and Condition)
- Android Support Library (Apache License v2.0)
####Test dependencies
##License
The Apache Software License, Version 2.0
It's open! Feel free to contribute!!
###References
- Stackoverflow answer on how to generate a library Jar from an Android project with Gradle
- Very useful blog on how to automate the deployment of artifacts with Gradle
- Sonatype User Guide on how to deploy project artifacts to the Maven Central Repository
- Another useful blog post on how to upload Javadocs to a GitHub repository