Android sort arraylist by properties - java

I want to sort an ArrayList by a property. This is my code...
public class FishDB{
public static Object Fish;
public ArrayList<Fish> list = new ArrayList<Fish>();
public class Fish{
String name;
int length;
String LatinName;
//etc.
public Vis (String name) {
this.name = name;
}
}
public FishDB() {
Fish fish;
fish = new Fish("Shark");
fish.length = 200;
fish.LatinName = "Carcharodon Carcharias";
fish = new Fish("Rainbow Trout");
fish.length = 80;
fish.LatinName = "Oncorhynchus Mykiss";
//etc.
}
}
}
Now I want in want to sort this ArrayList by a property e.g the latinname in another activity. But I don't know how to do that. Does anybody know how?

You need to implement a Comparator, for instance:
public class FishNameComparator implements Comparator<Fish>
{
public int compare(Fish left, Fish right) {
return left.name.compareTo(right.name);
}
}
and then sort it like this:
Collections.sort(fishes, new FishNameComparator());

You can simply do it by this code:
Collections.sort(list, new Comparator<Fish>() {
public int compare(Fish o1, Fish o2) {
return o1.name.compareTo(o2.name);
}
});

Kotlin code
list.sortWith(Comparator { o1, o2 -> o1.name!!.compareTo(o2.name!!) })

There are multiple options how to achieve the same goal.
The easiest solutions would be utilizing Java 8 features:
Collections.sort(Fish, (t1, t2) -> t1.name().compareTo(t2.name()));

Related

Create a generic comparator when classes are not related to each other but have a common property on which sorting is required

I want to create a custom comparator in java that can take different types of classes and sort by a particular method (which will be the same across all the classes). These classes are not related to each other and inheritance can't be applied to them.
For instance, suppose I have 2 classes Car and Bike, and both of them have a getter method which gives the count of how many of that particular vehicle has been sold. Now I want to sort a list of the particular vehicle based on this parameter.
public class Car {
private int selledUnits;
...
public int getSelledUnits() {
return selledUnits;
}
}
public class Bike {
private int selledUnits;
...
public int getSelledUnits() {
return selledUnits;
}
...
}
Now, suppose I have List<Car> cars and List<Bike> bikes and I want to sort these cars based on selledUnits field.
Currently, I am sorting by creating different comparators for a different class .
For example, for Car, I have created below comparator for sorting
class CarComparator implements Comparator<Car> {
public int compare(Car c1, Car c2) {
return c1.selledUnits - c2.selledUnits;
}
}
Similarly for sorting list of bikes below comparator is used :
class BikeComparator implements Comparator<Bike> {
public int compare(Bike b1, Bike b2) {
return b1.selledUnits - b2.selledUnits;
}
}
My question is instead of creating a separate comparator for each of these classes can I create a generic comparator, since both of them are sorting based on the same fields.
I have tried creating a generic comparator by using reflection and it is working fine but wanted to create the same without using reflection.
You should use a parent class.
Like this one =>
class Parent{
int sellingUnit;
String name;
}
class Bike extends Parent{
Bike(int a,String n){
sellingUnit = a;
name=n;
}
}
class Car extends Parent{
Car(int a ,String n){
sellingUnit = a;
name=n;
}
}
class MainComparator{
//sort by name
static Comparator<Parent> sortByName() {
return new Comparator<Parent>() {
#Override
public int compare(Parent p1, Parent p2) {
return p1.name.compareTo(p2.name);
}
};
}
//sort by selling unit
static Comparator<Parent> sortBySellingUnit() {
return new Comparator<Parent>() {
#Override
public int compare(Parent p1, Parent p2) {
return p1.sellingUnit-p2.sellingUnit;
}
};
}
}
public class Myy {
public void display(Parent p[], String type) {
for(Parent e:p) {
System.out.println(type+" name "+e.name+" selling unit "+e.sellingUnit);
}
}
public static void main(String asd[]) throws Exception
{
MainComparator comparator=new MainComparator();
Myy my=new Myy();
//testing bike
Bike b[]=new Bike[4];
b[0]=new Bike(1,"b1");
b[1]=new Bike(2,"b2");
b[2]=new Bike(4,"b4");
b[3]=new Bike(3,"b3");
System.out.println("before sorting by selling unit");
my.display(b, "bike");
Arrays.sort(b,comparator.sortBySellingUnit());
System.out.println("after sorting");
my.display(b, "bike");
//car testing
Car c[]=new Car[3];
c[0]=new Car(10,"c1");
c[1]=new Car(30,"c3");
c[2]=new Car(20,"c2");
System.out.println("before sorting car by name");
my.display(c,"car");
Arrays.sort(c, comparator.sortByName());
System.out.println("after sorting car by name");
my.display(c, "car");
}
}
Java has a built in comparator for comparing objects by mapping to int. It is generic.
List<Bike> bikes;
List<Car> cars;
//populated etc.
bikes.sort( Comparator.comparingInt( item -> item.selledUnits ) );
cars.sort( Comparator.comparingInt( item -> item.selledUnits ) );
Here is an example how you could achieve it using a wrapper:
class NotVehicleException extends RuntimeException {}
class VehicleWrapper{
Object wrappedVehicle;
public VehicleWrapper(Car wrappedVehicle) {
this.wrappedVehicle = wrappedVehicle;
}
public VehicleWrapper(Bike wrappedVehicle) {
this.wrappedVehicle = wrappedVehicle;
}
public int getSelledUnits() {
if(wrappedVehicle instanceof Car)
return ((Car) wrappedVehicle).getSelledUnits();
else if ( wrappedVehicle instanceof Bike)
return ((Bike) wrappedVehicle).getSelledUnits();
else throw new NotVehicleException();
}
}
class VehicleComparator implements Comparator<VehicleWrapper>{
public int compare(VehicleWrapper b1, VehicleWrapper b2) {
return b1.getSelledUnits() - b2.getSelledUnits();
}
}
Of course, this won't give you the flexibility and elegance which you would achieve having a common class or interface, so every time you add another Vehicle type - you'll have to update the wrapper. But in general, wrappers are a common solution to bind the unbound.
Without reflection is not possible.
You can do anything at runtime using reflection.
Probably you should not do this but you can compare totally different objects only (e.g.) taking in account the name of the fields (you can of course take in account any other information like, if one is an Integer and other a Double do the required conversion and so on).
Define an annotation for available "sorting" fields:
#Retention(RetentionPolicy.RUNTIME)
public #interface ComparableField {
}
Let two totally indepent classes to sort together:
#Getter
#Setter
#AllArgsConstructor
#ToString
public class Car {
private int id;
#ComparableField
private String name;
#ComparableField
private int sales;
}
#Getter
#Setter
#AllArgsConstructor
#ToString
public class Bike {
private int id;
#ComparableField
private String name;
#ComparableField
private int stock;
}
note both share the name field but not the sales and stock fields.
We can force to compare any field using a weird generic comparator:
private static Comparator<? super Object> genericFieldComparator(String field) {
return (a, b) -> {
Method fa = getMethods(a.getClass()).stream().filter(f -> f.getName().equalsIgnoreCase("get" + field)).findFirst().orElse(null);
Method fb = getMethods(b.getClass()).stream().filter(f -> f.getName().equalsIgnoreCase("get" + field)).findFirst().orElse(null);
try {
Object va = fa == null ? null : fa.invoke(a);
Object vb = fb == null ? null : fb.invoke(b);
if (va != null)
return unsafeCompare((Comparable<Object>) va, vb);
if (vb != null)
return -unsafeCompare((Comparable<Object>) vb, va);
return 0;
} catch (IllegalAccessException | InvocationTargetException e) {
// cannot compare
throw new RuntimeException("argh!");
}
};
}
private static <B, A extends Comparable<B>> int unsafeCompare(A a, B b) {
return b == null ? -1 : a.compareTo(b);
}
with this, sort any list is simply as
private static Stream<Object> sortUsingField(List<Object> objects, String field) {
return objects.stream().sorted(genericFieldComparator(field));
}
for example if we run
List<Object> objects = asList(
new Car(1, "Audi", 300),
new Bike(2, "MX", 30),
new Car(3, "M Benz", 250),
new Bike(4, "BH", 45)
);
// list possible fields to compare in runtime
System.out.printf("Available sorting fields:%n");
List<String> fields = objects.stream().flatMap(o ->
getAllFields(o.getClass(), withAnnotation(ComparableField.class)).stream().map(Field::getName))
.distinct()
.peek(f -> System.out.printf(" - %s%n", f))
.collect(toList());
// sort using every field
for (String field : fields) {
System.out.printf("Sorting using `%s`:%n", field);
sortUsingField(objects, field).forEach(o -> System.out.printf(" - %s%n", o));
}
we get
Available sorting fields:
- name
- sales
- stock
Sorting using `name`:
- Car(id=1, name=Audi, sales=300)
- Bike(id=4, name=BH, stock=45)
- Car(id=3, name=M Benz, sales=250)
- Bike(id=2, name=MX, stock=30)
Sorting using `sales`:
- Car(id=3, name=M Benz, sales=250)
- Car(id=1, name=Audi, sales=300)
- Bike(id=2, name=MX, stock=30)
- Bike(id=4, name=BH, stock=45)
Sorting using `stock`:
- Bike(id=2, name=MX, stock=30)
- Bike(id=4, name=BH, stock=45)
- Car(id=1, name=Audi, sales=300)
- Car(id=3, name=M Benz, sales=250)
as expected.
Note that there is no need at all for the classes to have any relationship to each other and not even the fields to be compared. Any discrepancies can be resolved at runtime (although this is not desirable).
In particular, classes need not share a common ancestor or interface and fields with different types can also be resolved at runtime.
Using Java 17's pattern matching in instanceof makes it a little neater. The last version allows the comparator to be defined in one line, but gives a confusing NullPointerException if there is an object in the List which is not one of the expected types.
public class ComparatorEg {
static class Car {
private final int sales;
Car(int sales) {this.sales = sales;}
public int getSales() {
return sales;
}
}
static class Bike {
private final int sales;
Bike(int sales) {this.sales = sales;}
public int getSales() {
return sales;
}
}
public static void main(String[] args) {
List<Object> vehicles = new ArrayList<>(List.of(new Car(10), new Bike(9), new Car(8)));
vehicles.sort(Comparator.comparingInt(o -> {
if (o instanceof Bike b) {
return b.getSales();
} else if (o instanceof Car c) {
return c.getSales();
} else {
throw new RuntimeException("unexpected object: " + o);
}
}));
List<Object> vehicles2 = new ArrayList<>(List.of(new Car(10), new Bike(9), new Car(8), new Object()));
// this will throw an NPE because of the presence of the Object in the list
vehicles2.sort(Comparator.comparing(o -> (o instanceof Bike b) ? b.getSales() : (o instanceof Car c ? c.getSales() : null)));
}
}

Convert list of interface type to list of another class using Java 8

Problem: I have a list of interface type which i need to convert to list of database DTO before storing in database.
I am new to Java 8 streams and map functions so any help would be great.
Research: I tried to use instanceOf in map but i always get compile errors.
Below are the different classes and their hierarchy.
public interface Animal {}
Public Class Cat implements Animal{
private String name;
public void getName() {
return name;
}
}
Public Class Dog implements Animal{
private String name;
public void getName() {
return name;
}
}
Public Class DBAnimalDTO{
private String dbName;
public void setDbName(String name) {
this.dbName = name;
}
}
To map the Dog and Cat classes i had created separate Functions.
Function<Dog, DBAnimalDTO> dogFunction = dog -> {
DBAnimalDTO dbAnimalDTO = new DBAnimalDTO();
dbAnimalDTO.setDbName(dog.getName());
return dbAnimalDTO;
}
Function<Cat, DBAnimalDTO> catFunction = cat -> {
DBAnimalDTO dbAnimalDTO = new DBAnimalDTO();
dbAnimalDTO.setDbName(cat.getName());
return dbAnimalDTO;
}
Using streams i tried to use different map functions based on Object type.
List<Animal> animals = new ArrayList();
List<DBAnimalDTO> dbAnimals = animals.stream().map(animal -> {
if(animal instanceOf Dog) {
return dogFunction;
} else {
return catFunction;
}
}).collect(Collectors.<DBAnimalDTO> toList());
I always get compile issue in Collectors.toList().
How do i fix this issue?
Also, any advise on if this is a good design pattern to transform list in this scenario would also be great.
You need to apply the function that you've created as:
List<DBAnimalDTO> dbAnimals = animals.stream().map(animal -> {
if (animal instanceof Dog) {
return dogFunction.apply((Dog) animal);
} else {
return catFunction.apply((Cat) animal);
}
}).collect(Collectors.toList());
On the other hand, using a constructor in the DBAnimalDTO class as:
public DBAnimalDTO(String dbName) {
this.dbName = dbName;
}
you can update your functions to be more cleaner as:
Function<Dog, DBAnimalDTO> dogFunction = dog -> new DBAnimalDTO(dog.getName());
Function<Cat, DBAnimalDTO> catFunction = cat -> new DBAnimalDTO(cat.getName());

Multiple Field Collection Sort

I have a class Arraylist contains value
String word, String expandedword, double confidence, double support
I want to sort the arraylist based on the confidence, and then based on the support.
I have succeed sort the arraylist based on confidence, but i failed to make a new method to sort the arraylist based on the support
this is my code to sort it based on confidence
public class ExpandedTerm implements Comparable<ExpandedTerm> {
String word;
String expandedWord;
double support;
double confidence;
public ExpandedTerm (String word,String expandedWord, double confidence,double support){
this.word = word;
this.expandedWord = expandedWord;
this.support = support;
this.confidence = confidence;
}
public String getWord(){
return word;
}
public String expandedWord(){
return expandedWord;
}
public Double getSupport(){
return support;
}
public Double getConfidence(){
return confidence;
}
#Override
public int compareTo(ExpandedTerm conf) {
return new Double(this.confidence).compareTo(new Double(conf.confidence));
}
I failed to make another method like compareTo, to sort it based on the support value.
How to sort it first by the confidence, and then make another method to sort it by the support value?
User Comparator for this. As compaarble provide functionality to sort on single type basis.
here is link where you found when to use comparable and comapartor
http://iandjava.blogspot.in/2012/10/comparable-and-comparator.html
Use multiple comparartor
one for confidence
public class ConfidanceComparator implements Comparator<ExpandedTerm> {
#Override
public int compare(final ExpandedTerm o1, final ExpandedTerm o2) {
return new Double(o1.confidence).compareTo(new Double(o2.confidence));
}
}
one for support
public class SupportComparator implements Comparator<ExpandedTerm> {
#Override
public int compare(final ExpandedTerm o1, final ExpandedTerm o2) {
return new Double(o1.support).compareTo(new Double(o2.support));
}
}
and use Collections.sort(<List>, <comparator>) adn you will get the desired list.
THis is only required when you want to sort either on confidance basis or support basis.
But if you required then first sort on confidance basis and if confidance is equal then check for support basis. then comparable is sufficient and is
public int compareTo(ExpandedTerm conf) {
int compare = new Double(this.confidence).compareTo(new Double(conf.confidence));
if (compare == 0) {
compare = new Double(this.support).compareTo(new Double(conf.support));
}
return compare;
}
Try this code for your compareTo method:
#Override
public int compareTo(ExpandedTerm other) {
Double thisConfidence = new Double(getConfidence());
Double otherConfidence = new Double(other.getConfidence());
int compare = thisConfidence.compareTo(otherConfidence);
if (compare == 0) {
Double thisSupport = new Double(getSupport());
Double otherSupport = new Double(other.getSupport());
compare = thisSupport.compareTo(otherSupport);
}
return compare;
}
Basically only compare "support" if "confidence" is equal.
I saw your reply that you want to sort once and sort again except differently, so I'm assuming you want to add a custom Comparator when you sort. Is this what you're looking for?
public static void main(String[] args) {
ExpandedTerm term1 = new ExpandedTerm("a", "b", 1, 4);
ExpandedTerm term2 = new ExpandedTerm("c", "d", 3, 2);
List<ExpandedTerm> list = new ArrayList();
list.add(term1);
list.add(term2);
Collections.sort(list);
System.out.println(list);
Collections.sort(list, new Comparator<ExpandedTerm>() {
#Override
public int compare(ExpandedTerm o1, ExpandedTerm o2) {
return new Double(o2.confidence).compareTo(new Double(o1.confidence));
}
});
System.out.println(list);
}
Here's the output
[ExpandedTerm#2eeb3c84, ExpandedTerm#55d2162c]
[ExpandedTerm#55d2162c, ExpandedTerm#2eeb3c84]
Some other tips: make sure you implement the toString(), hashCode(), and equals() functions for your ExpandedTerm. These are essential for debugging and also for use in other Collections like HashMap, etc.

Is it possible to check, if a function was called?

I have an ArrayList of objects, which I need to sort using two attributes (using Comparators). I need to save the sorted output to a text file with a different name, depending on the attribute used to sort. For example, if the list is sorted by attribute1 then file will be attribute1.txt, if attribute2 the file will be attribute2.txt.
How I want it to work (pseudocode):
if(sortedByAtr1){
FileWriter fwstream = new FileWriter(sortedByAtribute1.getName()+".txt");
}
else(sortedByAtr2){
FileWriter fwstream = new FileWriter(sortedByAtribute2.getName()+".txt");
}
Is this possible?
I appreciate any advice.
Thanks.
Servo
Here's an object-oriented approach to solving this requirement.
Use a wrapper for the List and its sorting attribute:
public class ListSorter<V> {
private final List<V> values;
private String sortingAttribute;
public ListSorter(List<V> values) {
this.values = values;
}
public void sort(AttributeComparator<V> comparator) {
Collections.sort(values, comparator);
sortingAttribute = comparator.getSortingAttribute();
}
public String getSortingAttribute() {
return sortingAttribute;
}
}
Extend the Comparator interface so you can get your attribute name:
public interface AttributeComparator<T> extends Comparator<T> {
public String getSortingAttribute();
}
Create custom AttributeComparators like this:
public class FooBarComparator implements AttributeComparator<Foo> {
public int compare(Foo foo1, Foo foo2) {
// skipped nullchecks for brevity
return foo1.getBar().compare(foo2.getBar());
}
public String getSortingAttribute() {
return "bar";
}
}
Use:
List<Foo> yourList = new ArrayList<Foo>();
ListSorter<Foo> example = new ListSorter<Foo>(yourList);
AttributeComparator comparator1 = new FooBarComparator();
example.sort(comparator1);
FileWriter fwstream = new FileWriter(example.getSortingAttribute() +".txt");

How to most elegantly iterate through parallel collections?

Say I have 2 parallel collections, eg: a list of people's names in a List<String> and a list of their age in a List<Int> in the same order (so that any given index in each collection refers to the same person).
I want to iterate through both collections at the same time and fetch the name and age of each person and do something with it. With arrays this is easily done with:
for (int i = 0; i < names.length; i++) {
do something with names[i] ....
do something with ages[i].....
}
What would be the most elegant way (in terms of readability and speed) of doing this with collections?
it1 = coll1.iterator();
it2 = coll2.iterator();
while(it1.hasNext() && it2.hasNext()) {
value1 = it1.next();
value2 = it2.next();
do something with it1 and it2;
}
This version terminates when the shorter collection is exhausted; alternatively, you could continue until the longer one is exhausted, setting value1 resp. value2 to null.
I would create a new object that encapsulates the two. Throw that in the array and iterate over that.
List<Person>
Where
public class Person {
public string name;
public int age;
}
for (int i = 0; i < names.length; ++i) {
name = names.get(i);
age = ages.get(i);
// do your stuff
}
It doesn't really matter. Your code won't get points for elegance. Just do it so that it works. And please don't bloat.
You could create an interface for it:
public interface ZipIterator<T,U> {
boolean each(T t, U u);
}
public class ZipUtils {
public static <T,U> boolean zip(Collection<T> ct, Collection<U> cu, ZipIterator<T,U> each) {
Iterator<T> it = ct.iterator();
Iterator<U> iu = cu.iterator();
while (it.hasNext() && iu.hasNext()) {
if (!each.each(it.next(), iu.next()) {
return false;
}
}
return !it.hasNext() && !iu.hasNext();
}
}
And then you have:
Collection<String> c1 = ...
Collection<Long> c2 = ...
zip(c1, c2, new ZipIterator<String, Long>() {
public boolean each(String s, Long l) {
...
}
});
I took #cletus comment and Improved it abit, And that's what I use:
public static <T,U> void zip(Collection<T> ct, Collection<U> cu, BiConsumer<T, U> consumer) {
Iterator<T> it = ct.iterator();
Iterator<U> iu = cu.iterator();
while (it.hasNext() && iu.hasNext()) {
consumer.accept(it.next(), iu.next());
}
}
Usage:
zip(list1, list2, (v1, v2) -> {
// Do stuff
});
While the submitted solutions are correct I prefer the following one because it follows the guides from effective java item 57: minimize the scope of local variables:
for (Iterator<String> i = lst1.iterator(), ii = lst2.iterator(); i.hasNext() && ii.hasNext(); ) {
String e1 = i.next();
String e2 = ii.next();
....
}
As suggested by jeef3, modeling the true domain rather than keeping separate, implicitly coupled Lists is the right way to go... when this is an option.
There are various reasons why you might not be able to adopt this approach. If so...
A. You can use a callback approach, as suggested by cletus.
B. You can still choose to expose an Iterator that exposes domain object element for each composite instance. This approach doesn't force you to keep a parallel List structure around.
private List<String> _names = ...;
private List<Integer> _ages = ...;
Iterator<Person> allPeople() {
final Iterator<String> ni = _names.iterator();
final Iterator<Integer> ai = _ages.iterator();
return new Iterator() {
public boolean hasNext() {
return ni.hasNext();
}
public Person next() {
return new Person(ni.next(), ai.next());
}
public void remove() {
ni.remove();
ai.remove();
}
};
}
C. You can use a variation of this and use a RowSet style cursor API. Let's say IPerson is an interface that describes Person. Then we can do:
public interface IPerson {
String getName();
void setName(String name);
...
}
public interface ICursor<T> {
boolean next();
T current();
}
private static class PersonCursor implements IPerson, ICursor<IPerson> {
private final List<String> _names;
...
private int _index = -1;
PersonCursor(List<String> names, List<Integer> ages) {
_names = names;
...
}
public boolean next() {
return ++_index < _names.size();
}
public Person current() {
return this;
}
public String getName() {
return _names.get(_index);
}
public void setName(String name) {
_names.set(0, name);
}
...
}
private List<String> _names = ...;
private List<Integer> _ages = ...;
Cursor<Person> allPeople() {
return new PersonCursor(_names, _ages);
}
Note that the B approach also be made to support updates to list by introducing a Domain interface, and having the Iterator return 'live' objects.
I just posted this function in this similar question (which #Nils von Barth asserts is not a duplicate ;) ), but it's equally applicable here:
public static <L,R,M> List<M> zipLists(
BiFunction<L,R,M> factory, Iterable<L> left, Iterable<R> right) {
Iterator<L> lIter = left.iterator();
Iterator<R> rIter = right.iterator();
ImmutableList.Builder<M> builder = ImmutableList.builder();
while (lIter.hasNext() && rIter.hasNext()) {
builder.add(factory.apply(lIter.next(), rIter.next()));
}
// Most of the existing solutions fail to enforce that the lists are the same
// size. That is a *classic* source of bugs. Always enforce your invariants!
checkArgument(!lIter.hasNext(),
"Unexpected extra left elements: %s", ImmutableList.copyOf(lIter));
checkArgument(!rIter.hasNext(),
"Unexpected extra right elements: %s", ImmutableList.copyOf(rIter));
return builder.build();
}
You can then provide a factory operation for the BiFunction, such as a value-type's constructor:
List<Person> people = zipLists(Person::new, names, ages);
If you really just want to iterate over them and do some operation, rather than construct a new collection, you could swap the BiFunction for a BiConsumer and have the function return void.

Categories