I have a user-defined class called User. This class implements comparable, and has the data fields ID number, followers, and following.
I want to sort the elements in the TreeSet so that the User with the most followers is first, and the User with the least followers is last. If both Users have the same number of followers, then sort based on how many people the User is following. If they are following the same number of people, then sort based on the ID number.
class User implements Comparable<User>
{
private int userId;
private ArrayList<User> following;
private ArrayList<User> followers;
Set<User> sortingSet = new TreeSet<User>();
}
I've also implemented a compareTo method
public int compareTo(User other)
{
if(this.followers.size() > other.followers.size())
return -1;
if(this.followers.size() < other.followers.size())
return 1;
if(this.following.size() > other.following.size())
return -1;
if(this.following.size() < other.following.size())
return 1;
if(this.userId < other.userId)
return -1;
if(this.userId > other.userId)
return 1;
return 0;
}
When I add to the TreeSet, it just sorts based on userId.
EDIT: Thanks for the help, I've made some changes to the code so far. I've removed some superfluous if statements. I've also implemented the compareTo method, but the problem still remains the same. I have also already written equals() and hashcode() methods.
EDIT2: Once again, thanks for the help everybody. I've figured out the problem, and it's related to how I initialized the followers and following ArrayLists. I'm blaming that mistake on my lack of sleep.
You have not created a comparator. You have created a thing called Comp which can be compared to users.
A comparator is a thing which compares two things. Comparable is something which can compare itself to something.
Comparable.compareTo takes one argument. Comparator.compare takes two arguments.
Related
So, I have a custom class, and an arraylist of that class type. Now, I want to retrieve the index of an object in this arraylist using only an ID that is available to me among the other attributes that make up an object of that class.
Saw a few examples online, but I'm kinda confused, they're overriding hashCode() and equals(), and in equals() they're checking all the attributes, I just want to check with the ID value, since for every object ID is unique.
public class MyClass {
private String ID;
private String name;
private String userName;
private String position;
// Constructors and getters and setters
}
So, what I want is, for say this piece of code:
List<MyClass> list=new ArrayList<>();
//Values are populated into list
int i=list.indexOf(someObjectsID); //Where someObjectsID is a String and not a MyClass object
int i will have the indexOf of the MyClass object in list that has ID equal to someObjectsID
If you're open to using a third party library, you can use detectIndex from Eclipse Collections.
int index = ListIterate.detectIndex(list, each -> each.getID().equals(someObjectsID));
If the list is of type MutableList, the detectIndex method is available directly on the list.
MutableList<MyClass> list = Lists.mutable.empty();
int index = list.detectIndex(each -> each.getID().equals(someObjectsID));
Note: I am a committer for Eclipse Collections
There is one absolutely guaranteed, efficient solution to this problem. Nothing else will work nearly so simply or efficiently.
That solution is to just write the loop and not try to get fancy.
for(int i = 0; i < list.size(); i++){
if (list.get(i).getId().equals(id)) {
return i;
}
}
return -1;
No need to mess with hashCode or equals. No need to force indexes into streams not designed for them.
Override hashCode & equals in your custom object, then indexOf will Just Work (tm).
I have a class for which equality (as per equals()) must be defined by the object identity, i.e. this == other.
I want to implement Comparable to order such objects (say by some getName() property). To be consistent with equals(), compareTo() must not return 0, even if two objects have the same name.
Is there a way to compare object identities in the sense of compareTo? I could compare System.identityHashCode(o), but that would still return 0 in case of hash collisions.
I think the real answer here is: don't implement Comparable then. Implementing this interface implies that your objects have a natural order. Things that are "equal" should be in the same place when you follow up that thought.
If at all, you should use a custom comparator ... but even that doesn't make much sense. If the thing that defines a < b ... is not allowed to give you a == b (when a and b are "equal" according to your < relation), then the whole approach of comparing is broken for your use case.
In other words: just because you can put code into a class that "somehow" results in what you want ... doesn't make it a good idea to do so.
By definition, by assigning each object a Universally unique identifier (UUID) (or a Globally unique identifier, (GUID)) as it's identity property, the UUID is comparable, and consistent with equals. Java already has a UUID class, and once generated, you can just use the string representation for persistence. The dedicated property will also insure that the identity is stable across versions/threads/machines. You could also just use an incrementing ID if you have a method of insuring everything gets a unique ID, but using a standard UUID implementation will protect you from issues from set merges and parallel systems generating data at the same time.
If you use anything else for the comparable, that means that it is comparable in a way separate from its identity/value. So you will need to define what comparable means for this object, and document that. For example, people are comparable by name, DOB, height, or a combination by order of precedence; most naturally by name as a convention (for easier lookup by humans) which is separate from if two people are the same person. You will also have to accept that compareto and equals are disjoint because they are based on different things.
You could add a second property (say int id or long id) which would be unique for each instance of your class (you can have a static counter variable and use it to initialize the id in your constructor).
Then your compareTo method can first compare the names, and if the names are equal, compare the ids.
Since each instance has a different id, compareTo will never return 0.
While I stick by my original answer that you should use a UUID property for a stable and consistent compare / equality setup, I figured I'd go ahead an answer the question of "how far could you go if you were REALLY paranoid and wanted a guaranteed unique identity for comparable".
Basically, in short if you don't trust UUID uniqueness or identity uniqueness, just use as many UUIDs as it takes to prove god is actively conspiring against you. (Note that while not technically guaranteed not to throw an exception, needing 2 UUID should be overkill in any sane universe.)
import java.time.Instant;
import java.util.ArrayList;
import java.util.UUID;
public class Test implements Comparable<Test>{
private final UUID antiCollisionProp = UUID.randomUUID();
private final ArrayList<UUID> antiuniverseProp = new ArrayList<UUID>();
private UUID getParanoiaLevelId(int i) {
while(antiuniverseProp.size() < i) {
antiuniverseProp.add(UUID.randomUUID());
}
return antiuniverseProp.get(i);
}
#Override
public int compareTo(Test o) {
if(this == o)
return 0;
int temp = System.identityHashCode(this) - System.identityHashCode(o);
if(temp != 0)
return temp;
//If the universe hates you
temp = this.antiCollisionProp.compareTo(o.antiCollisionProp);
if(temp != 0)
return temp;
//If the universe is activly out to get you
temp = System.identityHashCode(this.antiCollisionProp) - System.identityHashCode(o.antiCollisionProp);;
if(temp != 0)
return temp;
for(int i = 0; i < Integer.MAX_VALUE; i++) {
UUID id1 = this.getParanoiaLevelId(i);
UUID id2 = o.getParanoiaLevelId(i);
temp = id1.compareTo(id2);
if(temp != 0)
return temp;
temp = System.identityHashCode(id1) - System.identityHashCode(id2);;
if(temp != 0)
return temp;
}
// If you reach this point, I have no idea what you did to deserve this
throw new IllegalStateException("RAGNAROK HAS COME! THE MIDGARD SERPENT AWAKENS!");
}
}
Assuming that with two objects with same name, if equals() returns false then compareTo() should not return 0. If this is what you want to do then following can help:
Override hashcode() and make sure it doesn't rely solely on name
Implement compareTo() as follows:
public void compareTo(MyObject object) {
this.equals(object) ? this.hashcode() - object.hashcode() : this.getName().compareTo(object.getName());
}
You are having unique objects, but as Eran said you may need an extra counter/rehash code for any collisions.
private static Set<Pair<C, C> collisions = ...;
#Override
public boolean equals(C other) {
return this == other;
}
#Override
public int compareTo(C other) {
...
if (this == other) {
return 0
}
if (super.equals(other)) {
// Some stable order would be fine:
// return either -1 or 1
if (collisions.contains(new Pair(other, this)) {
return 1;
} else if (!collisions.contains(new Pair(this, other)) {
collisions.add(new Par(this, other));
}
return 1;
}
...
}
So go with the answer of Eran or put the requirement as such in question.
One might consider the overhead of non-identical 0 comparisons neglectable.
One might look into ideal hash functions, if at some point of time no longer instances are created. This implies you have a collection of all instances.
There are times (although rare) when it is necessary to implement an identity-based compareTo override. In my case, I was implementing java.util.concurrent.Delayed.
Since the JDK also implements this class, I thought I would share the JDK's solution, which uses an atomically incrementing sequence number. Here is a snippet from ScheduledThreadPoolExecutor (slightly modified for clarity):
/**
* Sequence number to break scheduling ties, and in turn to
* guarantee FIFO order among tied entries.
*/
private static final AtomicLong sequencer = new AtomicLong();
private class ScheduledFutureTask<V>
extends FutureTask<V> implements RunnableScheduledFuture<V> {
/** Sequence number to break ties FIFO */
private final long sequenceNumber = sequencer.getAndIncrement();
}
If the other fields used in compareTo are exhausted, this sequenceNumber value is used to break ties. The range of a 64bit integer (long) is sufficiently large to count on this.
I am making a method using the String comparison method "compareTo".
However I have a few questions pertaining as to how exactly it works.
public int compareTo(Card player2){
int finalRank = this.getRank().compareTo(player2.getRank());
int turn = this.getRank().compareTo(player2.getRank());
// Checks for "War" before anything is processed
if(this.getRank() == player2.getRank())
{
return 0; // results in war
}
//This logic sets Ace as the highest card
//after checking for "WAR"
else if(this.getRank() == Rank.ACE)
{
return 1;
}
else if(player2.getRank() == Rank.ACE)
{
return -1;
}
//If it is not an ace, the winner per turn
//is determined by standard value.
else if(turn > 0)
{
//System.out.println("playerOne wins");
return 1;
}
else
{
//System.out.println("playerTwo wins");
return -1;
}
}//end compareTo
This is my portion of code in the compareTo method.
**So that i can mention, I had received quite a bit of online help for this segment of code. While it was being explained to me, it made sense. Thus if it makes sense to me, i should be able to rewrite this segment of code in another format.
However, i have found myself not being able to do so. Thus i am looking for advice and help.
The function
int turn = this.getRank().compareTo(player2.getRank());
is generally what throws me off.
What exactly is the compareTo method doing?
for a players turn, it is comparing the two cards at play -> following the programs logic.
Is it possible for me to compare cards with out using the compareTo method?
Here is how i have my class constructors set up.
Again, to finalize.
I am having issues understanding how the compareTo method works.
and if anything id like suggestions of another way to compare the players cards at play.
The cards list are coming from 2 seperate classes -> Suit and Rank
private Rank rCard;
private Suit sCard;
/** ************************
* Initializing an object out of class Card
* #param rCard
* #param sCard
*
*************************
*/
public Card(Rank rCard, Suit sCard)
{
this.rCard = rCard;
this.sCard = sCard;
}
First of all, the purpose of the compareTo() method is to define what makes two objects comparable. Not all objects are comparable. Some objects are made comparable by the Java API. For example, String is comparable. So if you were to use a method such as Collections.sort(someStringArray), this task can be accomplished because the objects are comparable. On the other hand, if you wanted to sort() an array of your Card objects, it can't be accomplished until you make the Card object comparable. You do this by using the compareTo() method. You also want to define your Card class as implements Comparable interface, in which you will have to #Override the compareTo() method. This, will make your Cards comparable by rank, if you decide to compare them by rank in your compareTo().
What I believe you're trying to is compare the two objects by rank.
int turn = this.getRank().compareTo(player2.getRank());
is generally what throws me off. What exactly is the compareTo method doing?
You're comparing the rank of this, which I'm assuming is Player1 to the rank of player2. When you use the compareTo() method, you return an int. This int tells you which player's rank is higher... 1 meaning, player1's rank is higher, -1 meaning player2's rank is higher, and 0 meaning the ranks are equal. This int I'm assuming will determine whose turn it is
Also, I believe that this:
int turn = this.getRank().compareTo(player2.getRank());
should be placed outside of the compareTo() method and should be change to this:
int turn = this.compareTo(player2);
You're already comparing the players by their ranks in the compareTo(), so you don't need to use getRank(). I don't know if this will solve your problem completely, because I don't have your complete code, but I hope this gives you a better understanding of how the compareTo() method works.
I am building a class that has a mapping of strings to integers. So if I have 3 apples I would have a mapping of apples to 3.
I need to write a class that sorts the name of the objects by decreasing numbers.
So if I have
(apples, 3)
(oranges, 2)
(bananas, 5)
I will get
(bananas, 5), (apples, 3), (oranges 2)
I was wondering if there's already a class out there that would make my life easier or how I would implement this.
Thanks.
You should be able to put your objects (apples, 3) (oranges, 2) (bananas, 5) into a List and then call Collections.sort(yourlist). You'd then want to make sure the object you declared implements the Comparable interface.
More information is available at http://java.sun.com/docs/books/tutorial/collections/interfaces/order.html
Let's say you declared you object as
public class FruitAndCount implements Comparable<FruitAndCount> {
private final String name;
private final Integer count;
public FruitAndCount(String name, int count) {
this.name = name;
this.count = count;
}
public String name() { return name; }
public int count() { return count; }
public int compareTo(FruitAndCount o) {
return this.count.compareTo(o.count);
}
}
You should then be able to make the following call which will sort your list:
FruitAndCount fruitArray[] = {
new FruitAndCount("Apples", 3),
new FruitAndCount("Oranges", 2),
new FruitAndCount("Bananas", 5)
};
List<FruitAndCount> fruit = Arrays.asList(fruitArray);
Collections.sort(fruit);
You should then have a sorted list of fruit.
It's always nice to be able to make a class implement Comparable, but sometimes you can't, or it is undesirable (for instance, if you need to be able to compare the same type in different ways, based on different attributes).
In this case, it is advisable to use the overloaded Collections.sort() method, which takes a List<T> to sort and a Comparator<T> to determine how the objects should be sorted. This is much cleaner than making new tuples out of your old tuples, and can be more flexible than implementing Comparable (which is also a valid solution).
You really want to take a look at TreeMap.
Assuming the counts are unique, you simply reverse the tuples, storing the count as the key and the name of the fruit as the value. TreeMap then stores the items sorted in ascending order by the key value, and you can read the values back. Since the sorting is done on the insertion the retrieval time is very low.
If you have non-unique counts there's an easy solution here that will let you take advantage of TreeMap.
I have a list. The list can contain multiple items of the same enum type.
Lets say i have an enum : TOY which has values: BALL, DOLL, PLAYSTATION. I want to know how many PLAYSTATION items are in a list with the type TOY. (ie, List<Toy> toys)
What is the best possible solution for this? I don't want to keep iterating through the list everytime.
You can use Apache commons-collections' HashBag. It has a getCount(Object) method which will suit you.
java.util.Collections has a method called frequency(Collection c, Object type).
Usage in my question:
int amountOfPlayStations = Collections.frequency(toys, TOY.PLAYSTATION);
Why don't you create a decorator for the type of list you're using which stores a list of counts for each enum type have been added/removed internally. That way you could use it as a normal list but also add some extra functionality for querying how many of which type are currently contained.
All you'd need to do would be to override the add/remove/addAll etc methods and increment your counters before passing it on to the real list type. The best part about it would be that you could decorate any list type with your new wrapper.
At the very least, a utility method like:
public int count(List<Toy> haystack, Toy needle) {
int result;
for (Toy t : haystack) {
if (t == needle) {
result++;
}
}
return result;
}
Would let you concisely refer to the number of PLAYSTATIONs from elsewhere in the code. Alternatively if you knew the list was unlikely to change, building a Map<Toy, Integer> would let you build up the counts for all items once.
If you don't want to have to iterate over the entire collection each time, another alternative would be to write a ForwardingList implementation. The main benefits of this over the HashBag suggestion are:
it supports generics
it implements the List interface, so you can pass it to any method that expects a List
There is a downside to this approach however, in that you have to write a bit of plumbing code to get it up and running.
Below is a quick example of how you could do it. Note that if you do this you should override all methods that add/delete from the list, otherwise you may end up in an inconsistent state:
import com.google.common.collect.ForwardingList;
public class CountingList<E> extends ForwardingList<E> {
private List<E> backingList = new LinkedList<E>();
private Map<E, Integer> countMap = new HashMap<E, Integer>();
#Override
protected List<E> delegate() {
return backingList;
}
#Override
public boolean add(E element) {
backingList.add(element);
if(countMap.containsKey(element)) {
countMap.put(element, countMap.get(element) + 1);
} else {
countMap.put(element, 1);
}
return true;
}
public int getCount(E element) {
Integer count = countMap.get(element);
return count != null ? count.intValue() : 0;
}
}
Extend java.util.List method and override all mutator methods, i.e. the ones that are used for add or delete elements and also ones used to clear the list. Add a reference to a private java.util.Map which will hold the number of items per type. Add accessor methods which will return current number of elements per type.
The HashBag (by Bozho) seems to be your best bet. But a bit more general would be Googles Collections 2 with an appropriate Predicate:
List<Toy> toys;
List<Toy> playstations = Collections2.filter( toys, new Predicate() {
boolean apply(TOY toy){
return toy == TOY.PLAYSTATION;
}
});
Besides all those solutions (I have a weakness for the Collections.Frequency call), i would recommend you to take a look at google collections, and particularly to [Collections2.transform][2], which could give you a live view on items.
[2]: http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/collect/Collections2.html#transform(java.util.Collection, com.google.common.base.Function)