I recently wrote code that would query a collection of objects and rank them according to a given criteria and then filter based on an object property. I attach the code below. I would like to know if my approach can be improved upon such as made more efficient and extensible. Is there a better approach or just another approach?
I know that lambdas in Java 8 would make the code much cleaner. What other improvements are available?
Here's the scenario:
Rank journals numerically by score, those with a shared rank ordered lexicographically and filter out journals that are review journals.
Here's the data:
Given the following journals have scores:
Journal A = 2.2
Journal B = 6.2
Journal C = 6.2
Journal D = 1.2
And Journal D is a review journal.
The result should be:
Rank Journal Name Score
1 Journal B 6.2
1 Journal C 6.2
3 Journal A 2.2
Note: Journal D is filtered out of the list.
Here's my code:
This code produces the actual sort and filter:
List<Journal> journals = // add journals to collection.
Collate<Journal> collateJournals = new Collate<>();
Collate<Journal> collatedJournals = collateJournals.from(journals).filter(new OutReview()).sort(new ByScoreThenName(Direction.ASC)).rank(new Numerical());
This code controls the collation of sorted and filtered data.
public class Collate<T> {
List<T> collection = new ArrayList<>();
public Collate<T> sort(Comparator<T> sortComparator) {
Collections.sort(collection, sortComparator);
return this;
}
public T get(int i) {
return this.collection.get(i);
}
public boolean contains(T element){
return collection.contains(element);
}
public Collate<T> from(List<T> collection) {
this.collection = collection;
return this;
}
public Collate<T> filter(Predicate<T> predicate) {
List<T> result = new ArrayList<T>();
for (T element : (Collection<T>) collection) {
if (predicate.apply(element)) {
result.add(element);
}
}
this.collection = result;
return this;
}
public Collate<T> rank(Rank<T> rankEngine) {
rankEngine.doRank(collection);
return this;
}
}
This code implements a Comparator interface and is used to sort the collection: NOTE: there is also an Enum called direction that I have omitted.
public class ByScoreThenName implements Comparator<Journal> {
Direction direction;
public ByScoreThenName(){
this.direction = Direction.ASC;
}
public ByScoreThenName(Direction direction){
this.direction = direction;
}
@Override
public int compare(Journal j1, Journal j2) {
int comparatorValue = 0;
if (j1.getScore() == j2.getScore()) {
comparatorValue = j2.getName().compareToIgnoreCase(j1.getName());
} else {
if ((j1.getScore() < j2.getScore())) {
comparatorValue = -1;
} else if (j1.getScore() > j2.getScore()) {
comparatorValue = 1;
}
}
return direction.coefficent() * comparatorValue;
}
}
This code ranks numerically the journals based on their score, setting the journals rank in the journal object:
public class Numerical implements Rank<Journal> {
@Override
public void doRank(List<Journal> journals) {
for(Journal journal : journals){
int index = journals.lastIndexOf(journal);
if(index != 0 && journals.get(index-1).getScore() == journal.getScore()){
journal.setRank(journals.get(index-1).getRank());
} else {
journal.setRank(++index);
}
}
}
}
This filters out the review journals. It implements a predicate interface:
public class OutReview implements Predicate<Journal>{
@Override
public boolean apply(Journal j) {
return !j.isReview();
}
}
And finally the journal object:
public class Journal implements Serializable{
private static final long serialVersionUID = 8964756258469682390L;
private String name;
private float score;
private boolean review;
private int rank;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public float getScore() {
return score;
}
public void setScore(float score) {
this.score = score;
}
public boolean isReview() {
return review;
}
public void setReview(boolean review) {
this.review = review;
}
public int getRank() {
return rank;
}
public void setRank(int rank) {
this.rank = rank;
}
public int hashCode() {
// Code omitted for brevity
}
public boolean equals(Object obj) {
// Code omitted for brevity
}
@Override
public String toString() {
// Code omitted for brevity
}
}
I would appreciate any kind of feedback big or small.
EDIT:
I have followed the advise given below and implemented the from() method as a static factory method as follows:
public static <T> Collation<T> from(List<T> items) {
return new Collation<T>(new ArrayList<T>(items));
}
I have changed the names of the class and variables following the given advice:
The Direction Enum class is now imported as a static import. This cleans the code a bit.
And the array list field is now private.