Best way to combine multiple sorts in API 21 - java

My goal is to apply 2 sorts on a list of objects made of string and boolean.
I have accounts and active/inactive states, so I want to show active first (sort boolean values) and then to sort rest of elements in alphabetical order.
For instance:
[John,inactive], [Craig,active], [Mike,inactive], [Dennis,innactive]
I want to have:
[Craig,active], [Dennis,innactive], [John,inactive], [Mike,inactive]
What I plan to do is to use Comparable<> but I wonder if there is another way to do that.
I don't want to use Guava or any additional libraries.
This should be used for Android API 21 as well, so list.sort() cannot be used.
Thanks in advance!

Simply create a new Comparator like this:
public class AccountComparator implements Comparator<Account> {
#Override
public int compare(Account o1, Account o2) {
if (o1.isActive() && !o2.isActive()) {
return -1;
}
if (!o1.isActive() && o2.isActive()) {
return 1;
}
return o1.getName().compareTo(o2.getName());
}
}
Minimal tested example:
public static void main(String[] args) {
Account account2 = new Account("B", true);
Account account4 = new Account("D", false);
Account account3 = new Account("C", true);
Account account1 = new Account("A", false);
List<Account> list = new ArrayList<>();
list.add(account1);
list.add(account2);
list.add(account3);
list.add(account4);
Collections.sort(list, new AccountComparator());
list.forEach(System.out::println);
}
with the expected output of
Account{name='B', active=true}
Account{name='C', active=true}
Account{name='A', active=false}
Account{name='D', active=false}
Or with a lambda expression: (Thanks to #Wow for using Comparator.comparing)
Collections.sort(list, Comparator.comparing(Account::isActive).reversed()
.thenComparing(Account::getName));

There's no magic/easy way to do it without Java 8 or some third-party library. You'll have to implement Comparable and do the heavy lifting yourself:
public class Person implements Comparable<Person> {
private final boolean isActive;
private final String name;
#Override
public int compareTo(Person other) {
if (isActive && !other.isActive) {
return -1;
} else if (!isActive && other.isActive) {
return 1;
} else {
return name.compareTo(other.name);
}
}
}

Related

Java Stream API with non-pure functions chain

Let's assume I have a class Person
public class Person {
private final String name;
private final int age;
private boolean rejected;
private String rejectionComment;
public void reject(String comment) {
this.rejected = true;
this.rejectionComment = comment;
}
// constructor & getters are ommited
}
and my app is something like that
class App {
public static void main(String[] args) {
List<Person> persons = Arrays.asList(
new Person("John", 10),
new Person("Sarah", 20),
new Person("Daniel", 30)
)
persons.forEach(p -> {
rejectIfYoungerThan15(p);
rejectIfNameStartsWithD(p);
// other rejection functions
}
}
private static void rejectIfYoungerThan15(Person p) {
if (!p.isRejected() && p.getAge() < 15) {
p.reject("Too young")
}
}
private static void rejectIfNameStartsWithD(Person p) {
if (!p.isRejected() && p.getName().startsWith("D")) {
p.reject("Name starts with 'D'")
}
}
// other rejection functions
}
The thing is I don't like that I have to perform !p.isRejected() check in every rejection function. Moreover, it doesn't make sense to pass an already rejected person to next filters.
So my idea is to use a mechanism of Stream.filter and make something like
persons.stream().filter(this::rejectIfYoungerThan15).filter(this::rejectIfNameStartsWithD)...
And change signature for these methods to return true if a passed Person has not been rejected and false otherwise.
But it seems to me that it's a very bad idea to use filter with non-pure functions.
Do you have any ideas of how to make it in more elegant way?
When you change the check functions to only check the condition (i.e. not to call p.isRejected()) and return boolean, you already made the necessary steps to short-circuit:
private static boolean rejectIfYoungerThan15(Person p) {
if(p.getAge() < 15) {
p.reject("Too young");
return true;
}
return false;
}
private static boolean rejectIfNameStartsWithD(Person p) {
if(p.getName().startsWith("D")) {
p.reject("Name starts with 'D'");
return true;
}
return false;
}
usable as
persons.forEach(p -> {
if(rejectIfYoungerThan15(p)) return;
if(rejectIfNameStartsWithD(p)) return;
// other rejection functions
}
}
A Stream’s filter operation wouldn’t do anything other than checking the returned boolean value and bail out. But depending on the Stream’s actual terminal operation the short-circuiting could go even farther and end up in not checking all elements, so you should not bring in a Stream operation here.
Calling these methods from lambda is fine, however, for better readability, you can rename these methods to show what they are doing and return boolean, e.g.:
private boolean hasEligibleAge(Person p){..}
private boolean hasValidName(Person p){..}
Another approach would be to wrap these methods into another method (to reflect the business logic/flow), e.g.:
private boolean isEligible(Person p){
//check age
//check name
}
You should make Person immutable, and let the reject-methods return a new Person. That will allow you to chain map-calls. Something like this:
public class Person {
private final String name;
private final int age;
private final boolean rejected;
private final String rejectionComment;
public Person reject(String comment) {
return new Person(name, age, true, comment);
}
// ...
}
class App {
// ...
private static Person rejectIfYoungerThan15(Person p) {
if (!p.isRejected() && p.getAge() < 15) {
return p.reject("Too young");
}
return p;
}
}
Now you can do this:
persons.stream()
.map(App::rejectIfYoungerThan15)
.map(App::rejectIfNameStartsWithD)
.collect(Collectors.toList());
If you want to remove rejected persons, you can add a filter after the mapping:
.filter(person -> !person.isRejected())
EDIT:
If you need to short circuit the rejections, you could compose your rejection functions into a new function and make it stop after the first rejection. Something like this:
/* Remember that the stream is lazy, so it will only call new rejections
* while the person isn't rejected.
*/
public Function<Person, Person> shortCircuitReject(List<Function<Person, Person>> rejections) {
return person -> rejections.stream()
.map(rejection -> rejection.apply(person))
.filter(Person::isRejected)
.findFirst()
.orElse(person);
}
Now your stream can look like this:
List<Function<Person, Person>> rejections = Arrays.asList(
App::rejectIfYoungerThan15,
App::rejectIfNameStartsWithD);
List<Person> persons1 = persons.stream()
.map(shortCircuitReject(rejections))
.collect(Collectors.toList());

Java 8 Lambda Expression validation

I was reading the article about validation using Predicates here. I am trying to implement it in Spring Boot framework where I am having some questions.
In the code:
public class LamdaPersonValidator implements PersonValidator {
public void validate(Person person) {
notNull.and(between(2, 12)).test(person.getFirstName()).throwIfInvalid("firstname");
notNull.and(between(4, 30)).test(person.getLastName()).throwIfInvalid("secondname");
notNull.and(between(3, 50)).and(contains("#")).test(person.getEmail()).throwIfInvalid("email");
intBetween(0, 110).test(person.getAge()).throwIfInvalid("age");
}
}
it is not mentioned on what could be the standard way to check if the person object in the validate method is itself is null. Is it OK to just put a null check like if(persone != null) { // notNull.and..} or there could be some better way to do null check.
Another thing is suppose, I want to do some custom checks like if person exists in the database or not. In this case, I need to connect to the database to check so. In this case, I need to Autowire the interface where static variable and method is not possible.
So, what could be best approach to use this when doing validation from the database?
We are not the code judges of the holy inquisition, so it’s not our duty to tell you, whether it is “OK to just put a null check”.
Of course, it is ok to write is as an ordinary if statement, like we did the last 25 years, just like it is ok to invent a verbose framework encapsulating the null check and bringing the term “lambda” somehow into it. The only remaining question would be if you really intent to write if(person != null) { /* do the checks */ }, in other words, allow a null person to pass the test.
In case, you want to reject null persons (which would be more reasonable), there is already a possibility to write it without an explicit test, Objects.requireNonNull, since Java 7, which demonstrates that you don’t need an “everything’s better with lambdas” framework to achieve that goal. Generally, you can write validating code reasonably with conventional code, contrary to the article’s example, utilizing simple tools like the && operator and putting common code into methods:
public void validate(Person person) {
Objects.requireNonNull(person, "person is null");
checkString(person.getFirstName(), "first name", 2, 12);
checkString(person.getLastName(), "last name", 4, 30);
checkString(person.getEmail(), "email", 3, 50);
if(!person.getEmail().contains("#"))
throw new IllegalArgumentException("invalid email format");
checkBounds(person.getAge(), "age", 0, 110);
}
private void checkString(String nameValue, String nameType, int min, int max) {
Objects.requireNonNull(nameValue, () -> nameType+" is null");
checkBounds(nameValue.length(), nameType, min, max);
}
private void checkBounds(int value, String valueType, int min, int max) {
if(value < min || value > max)
throw new IllegalArgumentException(valueType+" is not within ["+min+" "+max+']');
}
This does the same as your code, without any framework with “Lambda” in its name, still having readable validation code and allowing to reuse the checking code. That said, instead of a class name LamdaPersonValidator, which reflects how you implemented it, you should use class names reflecting the responsibilities of a class. Clearly, a validator responsible for validating some properties of an object should not get mixed up with a validator checking the presence of an entity in the database. The latter is an entirely different topic on its own and should also be in a question on its own.
The code above is only meant to be an example how to achieve the same as the original code. It should never appear in production code in this form, as it is a demonstration of a widespread anti-pattern, to apply arbitrary unreasonable constraints to properties, most likely invented by the programmer while writing the code.
Why does it assume that a person must have a first name and a last name and why does it assume that a first name must have at least two and at most twelve characters, while the last name must be between four and thirty characters?
It’s actually not even characters, as the association between char units and actual characters is not 1:1.
A must read for every programmer thinking about implementing name validation, is Falsehoods Programmers Believe About Names (With Examples).
Likewise, Wikipedia’s List of the verified oldest people lists one hundred people having an age above 110.
And there is no reason to assume that an email address can’t have more than fifty characters. A true validation of the correct Email pattern may turn out to be something to omit deliberately…
You can write GenericValidator like that also:
Write AbstractValidator class for common work:
public abstract class AbstractValidator {
private Map<Predicate, String> validatorMap = new LinkedHashMap<>();
protected List<String> messages;
public AbstractValidator() {
this.messages = new ArrayList<>();
}
protected <E> AbstractValidator add(Predicate<E> predicate, String reason) {
validatorMap.put(predicate, reason);
return this;
}
protected AbstractValidator apply(String fieldName, Object val) {
AtomicBoolean flag= new AtomicBoolean(true);
this.validatorMap.forEach((modifier, reason) -> {
if (flag.get() && !modifier.test(val)) {
String message = MessageFormat.format("{0} {1}", fieldName, reason);
messages.add(message);
flag.set(false);
}
});
this.validatorMap.clear();
return this;
}
public void end(String exceptionStatus) {
Optional.ofNullable(messages).filter(CollectionUtils::isEmpty)
.orElseThrow(() -> {
RuntimeException ex = new RuntimeException(exceptionStatus, messages);
messages.clear();
return ex;
});
}
}
Write GenericValidator class which will extend the AbstractValidator for your validation implementation:
public class GenericValidator extends AbstractValidator {
private GenericValidator() {
super();
}
public static GenericValidator of() {
return new GenericValidator();
}
public GenericValidator nonNull() {
add(Objects::nonNull, "Field value is null");
return this;
}
public GenericValidator notEmpty() {
add(StringUtils::isNotEmpty, "Field is empty");
return this;
}
public GenericValidator min(int min) {
add(s -> ((String) s).length() >= min, "Field min size is " + min);
return this;
}
public GenericValidator max(int max) {
add(s -> ((String) s).length() <= max, "Field max size is " + max);
return this;
}
public GenericValidator notEmptyList() {
add(CollectionUtils::isNotEmpty, "Field List is null/Empty");
return this;
}
public GenericValidator apply(String fieldName, Object val) {
return (GenericValidator) super.apply(fieldName, val);
}
}
Please test accordingly. Example for test cases:
class GenericValidatorTest {
#Test
void genericValidationSuccessCase() {
Abc abc = new Abc();
abc.setName("a");
abc.setVal(1);
abc.setAbslist(Collections.singletonList(new ChildAbc()));
GenericValidator of = GenericValidator.of();
of.nonNull().apply("abc", abc).end(GENERIC_JSON_SERIALIZATION);
of.notEmpty().min(1).max(1).apply("name", abc.getName())
.nonNull().apply("value", abc.getVal())
.notEmptyList().apply("childAbc", abc.getAbslist())
.end(GENERIC_JSON_SERIALIZATION);
}
#Test
void genericValidationWhenObjectNull() {
GenericValidator of = GenericValidator.of();
Assertions.assertThrows(BusinessException.class, () -> of.nonNull()
.apply("abc", null).end(GENERIC_JSON_SERIALIZATION));
}
#Test
void genericValidationWithExceptionInput() {
Abc abc = new Abc();
abc.setName("a");
abc.setVal(1);
GenericValidator of = GenericValidator.of();
of.nonNull().apply("abc", abc).end(GENERIC_JSON_SERIALIZATION);
GenericValidator apply = of.notEmpty().min(1).max(1).apply("name", abc.getName())
.nonNull().apply("value", abc.getVal())
.notEmptyList().apply("childAbc", abc.getAbslist());
Assertions.assertThrows(BusinessException.class, () -> apply.end(GENERIC_JSON_SERIALIZATION));
}
}
class Abc {
String name;
Integer val;
List<ChildAbc> abslist;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getVal() {
return val;
}
public void setVal(Integer val) {
this.val = val;
}
public List<ChildAbc> getAbslist() {
return abslist;
}
public void setAbslist(List<ChildAbc> abslist) {
this.abslist = abslist;
}
}
class ChildAbc {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

Using Java Comparator for sorting using multiple fields based on condition

I have a question on effective use of Java Comparator.
class MyClass {
//Active State: OPEN, PENDING, RUNNING
private String state;
private Date startDate;
private Date endDate;
}
Here the possible values of state field are OPEN, PENDING, RUNNING, CLOSED, CANCELLED etc., out of which OPEN, PENDING and RUNNING are the Active state. Now I want to write comparator which sorts the List<MyClass>, so that the active ones comes first and are sorted by startDate followed by non-active ones which are sorted based on endDate.
static final Set<String> ACTIVE;// this set contains OPEN, PENDING, RUNNING
List<MyClass> myList;//This is my list
...
Collections.sort(myList, new Comparator<MyClass>() {
#Override
public int compare(MyClass o1, MyClass o2) {
int c;
boolean isO2 = ACTIVE.contains(o2.getState());
boolean isO1 = ACTIVE.contains(o1.getState());
if (isO2 && isO1) {
c = DateTimeComparator.getInstance().compare(o2.getStartDate(), o1.getStartDate());
} else if (isO2) {
c = 1;
} else if (isO1) {
c = -1;
} else {
c = DateTimeComparator.getInstance().compare(o2.getEndDate(), o1.getEndDate());
}
return c;
}
});
My question is whether my above implementation of having single comparator good? or are there better ways to do it?
Most likely I have to stick with Java 7 but solutions with Java 8 are welcome too.
In Java 8, I think it would be a bit cleaner to use Comparator::comparing. For example:
Comparator<MyClass> comparator = Comparator.nullsFirst(Comparator.comparing((MyClass myClass) -> !isActive(myClass))
.thenComparing((MyClass myClass) -> isActive(myClass) ? myClass.startDate : myClass.endDate, Comparator.nullsFirst(DateTimeComparator.getInstance())));
private static boolean isActive(MyClass myClass)
{
switch (myClass.state)
{
case "OPEN":
case "PENDING":
case "RUNNING":
return true;
default:
return false;
}
}
In Java 7, assuming you have Guava on the classpath, you could use Ordering. For example:
Comparator<MyClass> comparator = Ordering.natural().reverse().onResultOf(new Function<MyClass, Boolean>() {
#Override
public Boolean apply(MyClass myClass) {
return isActive(myClass);
}
})
.compound(Ordering.from(DateTimeComparator.getInstance()).nullsFirst().onResultOf(new Function<MyClass, Date>() {
#Override
public Date apply(MyClass myClass) {
return isActive(myClass) ? myClass.startDate : myClass.endDate;
}
}))
.nullsFirst();

Sorting ArrayList by multiple properties

I have an ArrayListof players with an attribute of username of type string and winratio of type long which is just a ratio of gameswon/gamesplayed*100.
I want to sort the ArrayList by username which I have already done using a Comparator class, however I also want to make another method that sorts the players based winratio and if they have equal winratio to order them in terms of their username. I am not sure on how to combine the two comparators together and give them a hierarchy so it knows which sort to preference over another.
Thanks
Calculate the difference in winRatio, if it is 0, return the difference in name, for example...
public class MultiComparator implements Comparator<Player> {
#Override
public int compare(Player o1, Player o2) {
int result = (int) (o1.getWinRatio() - o2.getWinRatio());
if (result == 0) {
result = o1.getUserName().compareTo(o2.getUserName());
}
return result;
}
}
And because I had nothing to go on, I used this
public interface Player {
public String getUserName();
public long getWinRatio();
}
As the base object
Another (weird) idea might be to create a "chained" Comparator, allowing you to take two (or more) Comparator and chain them together, so that while the result of any one Comparator is 0, it will keep trying to compare the values to the Comparator in the list...
public class RatioComparator implements Comparator<Player> {
#Override
public int compare(Player o1, Player o2) {
return (int) (o1.getWinRatio() - o2.getWinRatio());
}
}
public class NameComparator implements Comparator<Player> {
#Override
public int compare(Player o1, Player o2) {
return o1.getUserName().compareTo(o2.getUserName());
}
}
public class ChainedComparator implements Comparator<Player> {
private Comparator<Player>[] comparators;
public ChainedComparator(Comparator<Player>... comparators) {
this.comparators = comparators;
}
#Override
public int compare(Player o1, Player o2) {
int result = -1;
for (Comparator<Player> proxy : comparators) {
result = proxy.compare(o1, o2);
if (result != 0) {
break;
}
}
return result;
}
}
Which you could use something like...
Collections.sort(list, new ChainedComparator(new RatioComparator(), new NameComparator()));
This is untested and is just a rough idea ;)
What you want can be achieved using below code inside compare method,
int compare(T o1, T o2){
if(o1.winratio < o2.winratio){
return -1;
}else if(o1.winratio < o2.winratio){
return +1;
}else{
return o1.userName.compareTo(o2.userName);
}
}
This should work, I would suggest implementing Comparable interface for handling default sorting case...
if they have equal winratio to order them in terms of their username
This indicates that you should use userName for implementing comparable (for natural ordering). For winRatio, use comparator and in case of equal winRatio - also check their userName.

Easy way to filter elements from a collection in Java?

I want to write a method that removes all elements from a collection that follow a certain pattern. In functional languages, I would use filter() with a lambda expression. However, in Java, it seems I'm stuck with this:
public void removeAllBlueCars() {
LinkedList<Car> carsToRemove = new LinkedList<Car>();
for (Car c : cars) {
if (c.getCarColor() == Color.BLUE) {
carsToRemove.add(c);
}
}
cars.removeAll(carsToRemove );
}
Removing elements directly causes a ConcurrentModificationException. Is there a better way to do this without resorting to Google Collections?
With Java 8, you can filter with a lambda expression using Collection.removeIf.
cars.removeIf(c -> c.getCarColor() == Color.BLUE);
Maybe you could use iterators, which are a little more efficient:
public void removeAllBlueCars() {
Iterator<Car> carsIterator = cars.iterator();
while (carsIterator.hasNext()) {
Car c = carsIterator.next();
if (c.getCarColor() == Color.BLUE) {
carsIterator.remove();
}
}
}
Also, if you want to make this solution more generic, I'd suggest you something like:
public interface Filter<T> {
public boolean shouldRemove(T t);
}
And you could use it like this:
public void removeCars(Filter<Car> filter) {
Iterator<Car> carsIterator = cars.iterator();
while (carsIterator.hasNext()) {
Car c = carsIterator.next();
if (filter.shouldRemove(c)) {
carsIterator.remove();
}
}
}
Your method gets called like this:
removeCars(new Filter<Car>() {
public boolean shouldRemove(Car car) {
return car.getCarColor() == Color.BLUE;
}
});
You could iterate through the list using a ListIterator, which has a remove method.
Btw you should declare your list as List<Car> - program for interfaces, not implementation.
You can use CollectionUtils.filter(). It works with an Iterator, so it should have no problems removing items directly from the Collection. It is another dependency though. If you want the code standalone it would be:
public interface Predicate {
boolean evaluate(Object o);
}
public static void filter(Collection collection, Predicate predicate) {
if ((collection != null) && (predicate != null))
for (Iterator it = collection.iterator(); it.hasNext(); )
if (!predicate.evaluate(it.next()))
it.remove();
}
...
filter(collection, new Predicate() {
public boolean evaluate(Object o) { return whatever; }
});
Here is the Android way to implement a generic solution for this:
Usage:
Remove all null strings from my list
LinkedList<String> list = ...
ListUtils.filter(list, new ListUtils.Filter<String>() {
#Override
public boolean keepItem(String item) {
return item != null;
}
});
Source:
public class ListUtils {
public interface Filter<T>{
boolean keepItem(T item);
}
public static <T> void filter(#NonNull List<T> items, #NonNull Filter<T> filter) {
for (Iterator<T> iterator = items.iterator(); iterator.hasNext();){
if(!filter.keepItem(iterator.next())){
iterator.remove();
}
}
}
}
See if lambdaj's filter option can help you.
You could always go backwards and delete the elements..
for (int i = array.size() - 1; i >= 0; i--) {
if (array.get(i).getCarColor() == Color.BLUE)
array.remove(i);
}
edit: Noticed it was a LinkedList which might make my answer a bit non-relevant.
I'm a big fan of the Iterator solution provided by Vivien Barousse and gpeche. But I wanted to point out that you don't have to actually remove any elements from the collection, you just need to prevent the filter from returning them. That way, you basically have multiple views of the same collection, which can be very convenient and efficient. The Filter object is basically your lamda expression, or as close as you're gonna get in Java until version 7...
It's really an old post but how abt using the way given in Oracle Java tutorial.
static void filter(Collection<?>c) {
for (Iterator<?>it = c.iterator(); it.hasNext(); )
if (!cond(it.next()))
it.remove();
}
public static <T> void filter(List<T> list, Predicate<? super T> removeIf) {
if(list == null) return;
Iterator<T> iterator = list.iterator();
while (iterator.hasNext()) {
if (removeIf.apply(iterator.next())) iterator.remove();
}
}
Pass a list of some generic type to this function with a predicate to remove unwanted elements.
With Java8 introducing lambda expressions, it's much easier to implement filtering on a collection in a more functional approach.
I wrote an example for you. Please also note how nicer it is to print Collection content using forEach:
public class Java8Filtering {
public static void main(String[] argc) {
LinkedList<Car> cars = new LinkedList<>();
cars.add(new Car("car 1", Color.BLUE));
cars.add(new Car("car 2", Color.GREEN));
cars.add(new Car("car 3", Color.RED));
cars.add(new Car("car 4", Color.BLUE));
List<Car> filteredCars = cars.stream()
.filter(car -> car.getCarColor() != Color.BLUE)
.collect(Collectors.toList());
filteredCars.forEach(car -> System.out.println("Car: " + car.getCarName() + " with color: " + car.getCarColor()));
}
}
class Car {
private Color carColor;
private String carName;
public Car(String carName, Color carColor) {
this.carName = carName;
this.carColor = carColor;
}
public Color getCarColor() {
return carColor;
}
public void setCarColor(Color carColor) {
this.carColor = carColor;
}
public String getCarName() {
return carName;
}
public void setCarName(String carName) {
this.carName = carName;
}
}
enum Color {
BLUE, GREEN, RED
}
For those of you who come across this thread and might be working on Android with RxJava/RxAndroid, there's a quick way to do this without adding the Apache Commons Collections dependency:
cars = Observable.from(cars).filter(car -> {
if (car.getCarColor() == Color.BLUE) {
return false;
}
return true;
}).toList().toBlocking().first();
Note that I also happen to be using lambda expressions with Retrolambda. If you aren't using Retrolambda, you can express the same thing using the following:
cars = Observable.from(cars).filter(new Func1<Car, Boolean>() {
#Override
public Boolean call(Car car) {
if (car.getCarColor() == Color.BLUE) {
return false;
}
return true;
}
}).toList().toBlocking().first();

Categories

Resources