Understanding Java Enum Methods Implementation through an Example - java

So, I'm working on a project right now for school with a few people and one of them has committed some code that I'm really having difficulty wrapping my head around. The basis of the project is creating a music library with songs, albums, and playlists. These playlists, in particular, are arraylists of songs need different ways of sorting and thus he's implemented comparator for sorting. He did so using enums, which I understand from the perspective of just instances to represent items. Like
public enum Suit {
SPADES, CLUBS, HEARTS, DIAMONDS
}
to represent different suits of a card. I also have learned you can declare methods alongside enums, which is what it looks like he did. Here is the attribute declaration area and the constructor:
public class Playlist implements Comparator<Song>{
private TotalTime aTotalTime;
private String aName;
private ArrayList<Song> playList;
private Comparator<Song> aComparator;
private enum Method{
SortByTitle(new Comparator<Song> () {
#Override
public int compare(Song o1, Song o2) {
// TODO Auto-generated method stub
return o2.getTitle().compareTo(o1.getTitle());
}
}),
SortByArtist(new Comparator<Song>() {
#Override
public int compare(Song o1, Song o2) {
// TODO Auto-generated method stub
return o2.getExpectedTags().getArtist().compareTo(o1.getExpectedTags().getArtist());
}
}),
SortByLength(new Comparator<Song>() {
#Override
public int compare(Song o1, Song o2) {
// TODO Auto-generated method stub
return o2.getTotalSeconds()-o1.getTotalSeconds();
}
});
private Comparator<Song> comparator;
Method(Comparator<Song> pComparator){
comparator = pComparator;
}
public Comparator<Song> getComparator(){
return this.comparator;
}
}
// constructor that initializes the the playlist.
public Playlist(String pName,Method aMethod) {
aName = new String(pName);
playList = new ArrayList<Song>();
this.aComparator = aMethod.getComparator();
}
}
I can vaguely follow what's going on here as such: We start with the constructor, which calls aMethod.getComparator(), with aMethod being the enum instance, and then aMethod.getComparator() returns the this.comparator object, which itself is declared three lines above as a private Comparator<Song> comparator. From my perspective, it looks like ithis will return the private comparator object every time and not actually change the sorting method of the Comparable interface. Any help parsing all of this would be greatly appreciated.

Your analysis is correct. This class seems strange. Some points which stand out:
Why is Playlist a Comparator of Songs? It may make more sense to allow the playlist to be sorted using a Method instead of passing on construction.
The Method provided has no impact on the order of Songs in the Playlist.
The Method enum probably should not be private.
It may be worth revisiting the scope of the components in the project.
What is a Playlist? Is it a different Playlist if the Song order has changed?
Should it be up to the Playlist to decide how to play the songs in the playlist?

Look only at the enum definition.
The enum definition defines 3 actual enums: SortByTitle, SortByLength, and SortByArtist - those are your SPADE, HEARTS, DIAMONDS, CLUBS; of this enum. For each value, they are initialized with a non-zero-length constructor, and the object passed is a custom impl of a comparator, but forget all that for now.
The enumeration (heh) of enum values then ends. Why? Because semicolon.
But the enum definition doesn't end yet; then we get private Comparator<Song> comparator;.
Note that each individual enum value gets its own copy of this field. Each value of an enum is itself an instance of the 'class' that the enum represents. And the key point here is that this field holds different comparators for SortByArtist, SortByLength, etc.
Therefore, Method.SortByArtist.getComparator(); returns the value of that field for the instance of the Method 'class' (enums are basically classes, with highly limited construction; only one instance per value, so 3 instances here, ever). Which is different from the value of that field for the SortByLength instance.
The rest is just anonymous inner classes.
This is valid java, I think it should be fairly obvious to tell, right?
class StringByLengthComparator implements Comparator<String> {
public int compare(String a, String b) {
return a.length() - b.length();
}
}
...
Comparator<String> c = new StringByLengthComparator();
but we can write that with less characters in java, using the concept 'anonymous inner classes'. This works when you make a class and then intent to use this definition exactly once, and then never use it again. Let's say this 'Comparator c = ...;' line is the only place in the entire code base that you're ever going to mention StringByLengthComparator by name. Then:
Comparator<String> c = new Conmparator<String>() {
public int compare(String a, String b) {
return a.length() - b.length();
}
};
Looks funky, but it means the exact same thing. The one difference is that this class, which still exists, is not named StringByLengthComparator, but it gets a random name you needn't worry about and cannot ever use. That's okay though - after all, this was the only place we were ever gonna use this thing.
Java lambdas.
Note that you can make this even shorter, using lambda syntax:
Comparator<String> c = (a, b) -> a.length() - b.length();
still means the same thing (well, the object you get no longer has presence, which only matters if you do very silly things, such as relying on its object identity hashcode, or lock on the comparator itself, which are all crazy things to do. So if you don't do anything crazy, it's the same thing).
That's what the code is doing. It's no different than first defining 3 classes that each implement Comparable<Song>, and then passing new SongsByLengthComparer() as expression as first argument.

Related

How can we instantiate comparator while using it as an argument in sorting?

Why we use new keyword before comparator while using it as a constructor in sorting as comparator is an interface so we cannot instantiate it?
Collections.sort(persons, new Comparator<Person>() {
#Override
public int compare(Person p1, Person p2) {
return p1.getAge() - p2.getAge();
}
});
That's because this code does not instantiate Comparator. As you said, that's not possible.
Instead, it is syntax sugar. It's short for:
// Yes, you can define a class inside a method.
class $AutoGeneratedName implements Comparator<Person> {
#Override public int compare(Person p1, Person p2) {
return p1.getAge() - p2.getAge();
}
}
Collections.sort(persons, new $AutoGeneratedName());
In other words, short for: Define a new class, which implements Comparator. Then, instantiate this class once right away. Resolve this entire expression as a reference to this newly created instance. This construct is called an anonymous inner class.
CAREFUL - this code is bad.
20 years ago, that code was mostly fine, except for one detail: using a - b in comparisons is dangerous for very large numbers, but presumably, given that this is about 'age', not going to be an issue. Still, bad form; return Integer.comparing(p1.getAge(), p2.getAge()) would be much better.
But since then, this is no longer needed. You can write the concept much shorter like so:
Collections.sort(persons, (a, b) -> Integer.compare(a.getAge(), b.getAge());
We can do even that much, much simpler, and more readably by using List#sort with Comparator.comparingInt.
persons.sort(Comparator.comparingInt(Person::getAge));
which does exactly what you think it does when you just read it like its english: It sorts the collection 'persons' by comparing a specific int - which int? The one you get when you invoke getAge() on the person method.
This last snippet is what you should be using instead.

Ordering objects in java *without* using values

I want to create a class of objects to compare to each other, without using values to compare them with. Is there a library in Java which is able to provide this functionality for me? In terms of ordering, the most frequently mentioned library is Comparator, but all the examples I have seen so far use some kinds of value from the objects in order to perform this ordering with.
For example, I want to be able to say that within a class of objects that:
Object A is more important than Object B.
Object B is more important than Object C.
Therefore, I want the library to be able to perform some kind of analysis, and to be able to order the items according to these values, and say to me, that the order of the values above are A, B, C, in that order.
Is there a library which is able to do this in Java?
Are you thinking of something like this?
enum Importance {
High,
Medium,
Low;
}
class Thing implements Comparable<Thing> {
private Importance importance = Importance.Medium;
public Importance getImportance() {
return importance;
}
public void setImportance(Importance importance) {
this.importance = importance;
}
#Override
public int compareTo(Thing o) {
return importance.compareTo(o.importance);
}
}
Alternatively - if you want to control the relativity of each object then record that in a Map. You will need to be careful to tightly control the map to ensure there are no cycles - if there is then your sorting will become unstable.
static Map<Thing, Set<Thing>> moreImportant = new HashMap<>();
class Thing implements Comparable<Thing> {
#Override
public int compareTo(Thing o) {
Set<Thing> more = moreImportant.get(this);
return more == null ? 0 : more.contains(o) ? 1 : -1;
}
}

Non static comparator inner class

I would like to know is it ok to make non static inner class which implements comparator interface.
eg.
public class A {
private Map<String, Integer> scoreMap;
private final class myComparator implements Comparator<MyOtherClass> {
private int calScore(MyOtherClass ob) {
int score = ob.someValue * ob.otherValue;
scoreMap.put(ob.getName(), score);
}
public int compare(MyOtherClass ob1, MyOtherCLass ob2) {
return Integer.compare(calScore(ob1), calScore(ob2));
}
}
}
I would like to use comparator class non static because I want to use non static field "num" and want to modify its value. Is it fine to have non static comparator inner class?
Additional Info
For each object I am calculating score inside compare and sorting accordingly. I need to save those scores in a map which I calculated inside comparator and want to use this map in outer class for further calculation.
Technically nothing is stopping you. Morally however...
As long as you maintain the idempotency (run multiple times and get same result) then it is not incorrect. However it is a sideeffect. (use this comparator, and some class's values change. run another comparator and nothing changes). Side effects aren't necessarily bad but they make a program more complex and harder to understand and debug.
Try seeing it through the eyes of some poor soul that has to maintain your code. Some value changes and they have no idea why. all they did was compare an unaffiliated list.
anyways your example is very abstract so it's hard to judge the context but usually there is no need to do what you want to do. It is generally something to be avoided unless you have a really good reason for it. And that really good reason shouldn't be "because I don't want to loop over the dataset again" in my opinion.
from your edit:
You are trying to save yourself work by not having to do the recalculating again from the sounds of it. You're saving yourself only a small amount of effort really. why not first calculate the scores, store the result, then sort the results (on score field)
like:
public static class ScoredEntry {
private SomeGameData data; //or something
private int score;
// constructor takes data + score, imagine getters, setters, I am lazy ok.
}
public List<ScoredEntry> scoreEntries(List<SomeGameData> gameData) {
List<ScoredEntry> result = new ArrayList<ScoredEntry>();
for (SomeGameData data : gameData) {
int score = calculateScore(data);
result.add(new SCoredEntry(score, data);
}
return result;
}
public void sortBySCore(List<ScoredEntry> list) {
Collections.sort(list, new Comparator<ScoredEntry>() {
public int compare(SCoredEntry a, ScoredEntry b) {
// etcetera.
}
}
}

Compare LinkedList by multiple strings in Java

I have a custom object like this :
Linkedlist<ClassInfo> classes = new LinkedList<ClassInfo>();
Within that, there are accessors for a teacher's name, the class name, the room number, etc. These are all Strings. I have run into a situation where the data in that LinkedList needs to displayed by different parameters (i.e. teacher name, class name, the room number, etc.).
Can anyone supply a quick implementation of how to do this? If I use the Compartor interface, how would I be able tell it which String field to sort the list by? My research also lead me to the Collator, and I was wondering if this would be of use.
Appreciate any help.
Write a different Comparator implementation for each field:
Comparator<ClassInfo> CLASS_NAME_COMPARATOR = new Comparator<ClassInfo>() {
public int compare(ClassInfo class1, ClassInfo class2) {
return class1.getClassName().compareTo(class2.getClassName());
}
};
... // implementations for other fields
...and then sort by whichever comparator is appropriate:
Collections.sort(classes, CLASS_NAME_COMPARATOR);
You will have to provide a custom comparator for every ordering you need to sort your collection to. Eg:
class TeacherComparator implements Comparator<ClassInfo> {
public int compare(ClassInfo c1, ClassInfo c2) {
String teacher1 = c1.getTeacher();
String teacher2 = c2.getTeacher();
return teacher1.compareTo(teacher2);
}
}
class ClassNameComparator implements Comparator<ClassInfo> {
...
}

Comparing two comparator objects in Java

If I do the following
myObject.myMethod(myClass.getComparator());
with
public void myMethod(Comparator<? super myOtherObject> comparator) {
if (comparator.equals(myClass.getComparator()) {
//do sth
}
}
and in myClass
static Comparator<ListItem> getComparator() {
return new Comparator<myOtherObject>() {
public int compare(myOtherObjectitem1, myOtherObjectitem2) {
return (Integer.valueOf(myOtherObject.getRating()).compareTo(Integer.valueOf(myOtherObject.getRating())));
}
};
}
then "//do sth" is not gonna be executed. So the objects I get from getComparator the two times are different. How can that be? Is there a chance to see, which comparator "myMethod" gets?
You're calling the equals method on this line:
if (comparator.equals(myClass.getComparator())
Since you haven't defined this method explicitly on your Comparator class (which is an anonymous inner class), this defaults to the version inherited from Object - which considers two references equal only if they are the exact same object.
And your getComparator() method states return new Comparator() { ... }, so it's calling the constructor and creating a new object each time it's called. Thus the result of one call to getComparator will be a distinct object, and hence will not be considered equal to, the result of another call.
I can think of two possible ways to change your code so that the equality test returns true:
Create the comparator only once, and return this same object from
getComparator. This would involve a change somewhat like the
following in myClass:
private static Comparator<ListItem> cmp = new Comparator<myOtherObject>() {
public int compare(myOtherObjectitem1, myOtherObjectitem2) {
return (Integer.valueOf(myOtherObject.getRating()).compareTo(Integer.valueOf(myOtherObject.getRating())));
}
};
static Comparator<ListItem> getComparator() {
return cmp;
}
Provide an explicit equals() implementation (and thus a hashCode() one too, ideally). You can then control exactly which objects are considered equal to one of your comparators. This might be much easier if you define a concrete class for your comparator rather than it being an anonymous inner class.
At the end of the day, though, I fear your approach might not be right. What does it mean for two comparators to be equal to one another? I feel this is an ambiguous concept for anything other than data classes, and I would be hesitant to use the Object.equals method for this.
(For example, if by equality you mean "they will sort lists in the same order", then I'd add a method to your comparator class called isEquivalentSortOrder or something similar. This way you can specify exactly what you mean without having to rely on the woolly definition of "being the same".)
Why not to create inside myClass static variable of Comparator like:
class myClass{
public static Comparator<ListItem> = new Comparator<myOtherObject>() {
public int compare(myOtherObjectitem1, myOtherObjectitem2) {
...
}
};
}

Categories