I am hard stuck on a problem I cannot find a good answer to. I've found
this one about custom comparators, but it is incomplete:
class YourClass {
static Comparator<YourClass> getAttribute1Comparator() {
return new Comparator<YourClass>() {
// compare using attribute 1
};
}
static Comparator<YourClass> getAttribute2Comparator() {
return new Comparator<YourClass>() {
// compare using attribute 2
};
}
}
That should work, but I don't know how the comparison part works. Here is my class:
package ZVCVolkel_Logic;
import java.util.Comparator;
public class Vliegtuig implements Comparator<Vliegtuig>{
private String naam;
private String type;
private String status;
private Hangaar hangaar;
public Vliegtuig(String naam, String type, String status, Hangaar hangaar){
this.naam = naam;
this.type = type;
this.status = status;
this.hangaar = hangaar;
}
}
Now I need a comparator for status and for Hangaar.getName(). Can someone help?
It is not the one, he has only 1 comparator. I can get that working too but not with 2 different ones in 1 class.
The comparator interface has a method compare return an int value to determine the relation ship between two objects.
It will return:
a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.
static Comparator<Vliegtuig> hangaarNameComparator() {
return new Comparator<Vliegtuig>(){
public int compare(Vliegtuig one, Vliegtuig two) {
return one.getHangaar().getName().compareTo(two.getHangaar().getName());
}
}
}
Here you probably want to take care of NullPointerException if getHangaar() or hangaar.getName() return null.
In java 8 you could do this:
Comparator<Vliegtuig> hangaarNameComparator = Comparator.comparing(Vliegtuig::getHagaar,
Comparator.comparing(Hagaar::getName));
In the comparator implementation you need to compare 2 objects. You can refer to most of JDK classes for example, for instance java.lang.Integer.
In your case solution will be to use embedded compactors from objects like this:
Comparator<Vliegtuig> nameComparator = new Comparator<>() {
#Override
public int compare(Vliegtuig o1, Vliegtuig o2) {
return o1.getName().compareTo(o2.getName());
}
}
And you don't need to extend Comparator by the Vliegtuig.
Related
This question already has answers here:
How to use Comparator in Java to sort
(16 answers)
Closed 5 years ago.
I also have an ArrayList items. I have classes derived from Media. Given the code below, how would I sort the arraylist by duration? e.g.
Collections.sort(myMedia, ?);
Here is the class
import java.util.Comparator;
public abstract class Media implements Comparable<Media>{
private int duration;
private String title;
private String imageFileName;
private static String imageFileDirectory = "src/resources/";
public Media(String name, int seconds) {
this.title = name;
this.duration = seconds;
this.imageFileName = "";
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getDuration() {
return duration;
}
public void setDuration(int d) {
this.duration = d;
}
public String getImageFileName() {
return imageFileName;
}
public void setImageFileName(String imageFileName) {
this.imageFileName = imageFileName;
}
public static String getImageFileDirectory() {
return imageFileDirectory;
}
public static void setImageFileDirectory(String imageFileDirectory) {
Media.imageFileDirectory = imageFileDirectory;
}
#Override
public String toString() {
return this.getTitle()
+ ", Duration: " + this.getDuration() + "s, " +
"Cost: " + costInPence() + "p";
}
public abstract int costInPence();
#Override
public int compareTo(Media o) {
return this.getTitle().compareTo(o.getTitle());
}
public static class DurationComparator implements Comparator<Media>{
public int compare(Media m1, Media m2) {
return m2.getDuration() - m1.getDuration();
}
}
public static class CostComparator implements Comparator<Media>{
public int compare(Media m1, Media m2) {
return m2.costInPence() - m1.costInPence();
}
}
}
The Collections#sort method has two variants.
The first variant (documentation) only accepts a collection that is to be sorted. It will sort the elements of the collection by their natural order. Therefore the elements must implement the interface Comparable which yields a compareTo method. Your Media objects already implement this interface with a meaningful natural order, namely sorting by their titles:
public abstract class Media implements Comparable<Media> {
#Override
public int compareTo(Media o) {
return this.getTitle().compareTo(o.getTitle());
}
}
The other variant (documentation) accepts a collection and a Comparator object. It will then sort the elements based on the order defined by the Comparator. You can define Comparator on various ways, since Java 8 it became pretty compact. But first let us take a look at the Comparator you have already defined, it sorts by duration:
public static class DurationComparator implements Comparator<Media> {
public int compare(Media m1, Media m2) {
return m2.getDuration() - m1.getDuration();
}
}
So if you want to sort by titles you should use the first variant. If you want to sort by duration you need to create a new instance of DurationComparator and use the second variant, alternatively use the compact Java 8 statements. The same holds for your CostComparator:
// Sort by title
Collections.sort(myMedia);
// Sort by duration
Collections.sort(myMedia, new DurationComparator<>());
// Sort by duration with Java 8
Collections.sort(myMedia, Comparator.comparingInt(Media::getDuration));
// Sort by cost
Collections.sort(myMedia, new CostComparator<>());
// Sort by cost with Java 8
Collections.sort(myMedia, Comparator.comparingInt(Media::costInPence));
The Comparator#comparing (documentation) method creates a Comparator object that sorts the given elements based on the given keys. The method reference points to a method that yields the keys.
As the methods return int you may choose the method Comparator#comparing (documentation) instead, it is slightly faster since int doesn't need to be boxed into Integer then.
Note that since Java 8 Lists itself provide a sort method too (documentation). So you don't need to call Collections anymore:
myMedia.sort(Comparator.comparingInt(Media::getDuration));
Also note that Comparator now provides some useful methods (documentation), for example to first sort by one key and if keys are equal then sort by a second key:
myMedia.sort(Comparator.comparingInt(Media::getDuration)
.thenComparing(Media::costInPence));
Using the Check class, add the code to have the Checks sorted by checkNumber.
import java.util.Date;
public class Check implements Comparable {
private int checkNumber;
private String payTo;
private Date date;
private float amount;
public int compareTo(Object arg0) {
//Insert code here
}
public int getCheckNumber() {
return checkNumber;
}
public void setCheckNumber(int checkNumber) {
this.checkNumber = checkNumber;
}
public String getPayTo() {
return payTo;
}
public void setPayTo (String payTo) {
this.payTo = payTo;
}
public Date getDate() {
return date;
}
public void setDate (Date date) {
this.date = date;
}
public float getAmount() {
return amount;
}
public void setAmount (float amount) {
this.amount = amount;
}
}
My solution is below, but it does not seem to work.
Can anyone help me with the solution?
public int compareTo(Object arg0) {
if(this.checkNumber == arg0.checkNumber)
return 0;
else
return this.checkNumber > arg0.checkNumber ? 1 : -1;
}
You didn't specify what you mean by "doesn't work", but reading your code, I'm assuming you get a compilation error.
The reason for this is because the code you were given has not specified a type for the comparable, so it doesn't know what type you even want to compare it to. It will use Object by default, which does not (by design) know a thing about Check's fields and methods.
The following modification is the best solution:
public class Check implements Comparable<Check> {
public int compareTo(Check arg0) {/* ...*/ }
}
This will force you to compare this to other Check's only and make arg0 a Check object, rendering its fields and methods available to you.
Should the parameters of the problem you were given not allow you to modify the provided code, then the (very very distant) second best solution is:
public int compareTo(Object arg0) {
Check other = null;
if(arg0 instanceof Check)
other = (Check)arg0;
// Other checks.
}
This adaptation would technically work for your problem given the stipulation that you are not allowed to modify the provided code, but is otherwise not at all recommended, as the contract for Comparable wants the type of object you wish to compare against to be specified, and not specifying it can introduce problems.
In fact, I'd say you are fully allowed to tell the person who gave you this problem that they are a terrible person for giving you a problem with this mistake in it, because not specifying a Comparable type is a really bad practice. Especially if they're teaching you how to program.
I got a class used in an Android app, which is declared like this:
public static class MyData implements Comparable<MyData>
{
public MyEnum myEnum;
#Override
public int compareTo(MyData another)
{
if(this.myEnum.equals(MyEnum.Value1))
{
return 1;
}
if(another.myEnum.equals(MyEnum.Value1))
{
return -1;
}
if(this.myEnum.equals(MyEnum.Value2))
{
return 1;
}
if(another.myEnum.equals(MyEnum.Value2))
{
return -1;
}
return 0;
}
}
I defined a list: List<MyData> myList = new LinkedList<MyData>();
After adding items to the list I call: Collections.sort(myList)
The problem is that when I debug, I see the compareTo method being called after the sort method is invoked, however it doesn't enter the first if even that it should. I even put the Boolean expression in Eclipse in the "Expressions" view and it returned true, but the code simply jumps to the return 0; and the list is not being sorted like I want to.
Why is that?
Eventually I changed that enum class member to an int member which was initialized with the ordinal value inside the Enum.
Then the compareTo method was changed like this:
#Override
public int compareTo(MyData another)
{
Integer myVal = this.intVal;
Integer otherVal = another.intVal;
return myVal.compareTo(otherVal);
}
In Java I want to create a data type, let's say Atom, which contains an index number, and when that index = 0, the Atom will contain a String datum, otherwise the Atom is just an index (Integer).
How can I do that?
I guess it would save some space if most Atoms are just indexes and only some contain Strings.
The following Factory Pattern illustrates how a static factory method createAtom creates instances of different classes (all Atom), with different data.
Here I made index final as changing it to 0, will not change the class of the object. Also Atom has to offer access to the optional datum.
public class Atom {
public final int index;
private Atom(int ix) {
this.index = ix;
}
public String getDatum() {
return null;
}
public static Atom createAtom(int index) {
return index != 0 ? new Atom(index) : new ExtendedAtom(index);
}
}
class ExtendedAtom extends Atom {
private String datum;
ExtendedAtom(int ix) {
super(ix);
}
#Override
public String getDatum() {
return datum;
}
}
There is no way to make a single class like that, because Java does not support "union" types. If you define a class with an int and a String, the space for both the int and the reference to a String would be allocated, defeating the space-saving purpose of what you are trying to achieve.
You could store your "atoms" as java.lang.Object values, but you would need to check their type and cast every time you need to obtain the index or the string. This approach is cumbersome, because primitive ints are wrapped in java.lang.Integer objects, adding to storage requirements.
A cleaner approach would be defining an interface for your Atom, and defining two classes, a StringAtom and an IntAtom, to store the two atom kinds in your program:
interface Atom {
boolean hasInt();
boolean hasString();
int getInt();
String getString();
}
class StringAtom implements Atom {
private final String s;
public StringAtom(String s) {this.s = s;}
boolean hasInt() {return false;}
boolean hasString() {return true;}
int getInt() {throw new IllegalStateException();}
String getString() {return s;}
}
class IntAtom implements Atom {
private final int n;
public IntAtom(int n) {this.b = b;}
boolean hasInt() {return true;}
boolean hasString() {return false;}
int getInt() {return n;}
String getString() {throw new IllegalStateException();}
}
Create a small class with an index member variable, a flag checking if that index is zero and a String datum that will be non-null only if the flag is true (indicating the index is zero).
I have a situation where I will be receiving 2+ ArrayList<Widget> and I need to be able to merge all the lists and remove any duplicate Widget so that I wind up with only 1 ArrayList<Widget> that contains all Widgets from all the merged lists, but without any duplicates.
Assume Widget has an overridden equals method that can be used for determining whether two Widgets are duplicates, although there may be a better way:
public ArrayList<Widget> mergeAndRemoveDupes(ArrayList<Widget> widgets...) {
// ???
}
Looking for the most algorithmically efficient way of accomplishing this. I am happy to use Apache Commons or any other open source libs that would help me out too! Thanks in advance!
For each ArrayList<Widget>, add each element to a Set<Widget> (HashSet or TreeSet, depending on whether they can be ordered in some way, or are hashable) utilizing addAll. Sets contain no duplicates by default.
You can convert this Set back into an (Array)List if you need to at the end.
Note you will need to implement hashCode for your Widget class if you decide to use a HashSet, but if you have an overridden equals, you should do this anyway.
Edit: Here's an example:
//Either the class itself needs to implement Comparable<T>, or a similar
//Comparable instance needs to be passed into a TreeSet
public class Widget implements Comparable<Widget>
{
private final String name;
private final int id;
Widget(String n, int i)
{
name = n;
id = i;
}
public String getName()
{
return name;
}
public int getId()
{
return id;
}
//Something like this already exists in your class
#Override
public boolean equals(Object o)
{
if(o != null && (o instanceof Widget)) {
return ((Widget)o).getName().equals(name) &&
((Widget)o).getId() == id;
}
return false;
}
//This is required for HashSet
//Note that if you override equals, you should override this
//as well. See: http://stackoverflow.com/questions/27581/overriding-equals-and-hashcode-in-java
#Override
public int hashCode()
{
return ((Integer)id).hashCode() + name.hashCode();
}
//This is required for TreeSet
#Override
public int compareTo(Widget w)
{
if(id < w.getId()) return -1;
else if(id > w.getId()) return 1;
return name.compareTo(w.getName());
}
#Override
public String toString()
{
return "Widget: " + name + ", id: " + id;
}
}
If you want to use a TreeSet but don't want to implement Comparable<T> on your Widget class, you can give the set itself a Comparator object:
private Set<Widget> treeSet;
....
treeSet = new TreeSet<Widget>(new Comparator<Widget>() {
public int compare(Widget w1, Widget w2)
{
if(w1.getId() < w2.getId()) return -1;
else if(w1.getId() > w2.getId()) return 1;
return w1.getName().compareTo(w2.getName());
}
});
I would do it this way
Set<Widget> set = new HashSet<>(list1);
set.addAll(list2);
List<Widget> mergeList = new ArrayList<>(set);
Use Set Collection Class,
ArrayList<Widget> mergeList = new ArrayList<widget>();
mergeList.addAll(widgets1);
mergeList.addAll(widgets2);
Set<Widget> set = new HashSet<Widget>(mergeList);
ArrayList<Widget> mergeListWithoutDuplicates = new ArrayList<widget>();
mergeListWithoutDuplicates .addAll(set);
return mergeListWithoutDuplicates;
Now here Set will remove all duplicates values from your ArrayList.