I am working with Java and I am looking for a data structure that stores objects and sorts them based on one of the properties(Integer type) of object. For example, if objects have different properties and one of these properties is called number of type Integer. I would like to sort all the objects based on the property number. I am also looking for the time complexities to be as follows:
Insertion in log(n) time.
Deletion in log(n) time.
Get all the objects that have the number property in a certain range in log(n) time. The range has a min value and a max value. I would like to get all the objects that have the number property falling in this range and would like the time complexity to be log(n) in worst case scenario.
I believe TreeSet data structure in Java could be a good candidate to store these objects. I can use to custom comparator to sort the objects based on the number property. But the problem is this number property could be the same for multiple objects and I would like to store all of them. Initially I had a custom comparator like the following. But with this comparator, object with the same number property were getting overwritten.
class Helper implements Comparator<NumberWrapper> {
public int compare(NumberWrapper num1, NumberWrapper num2)
{
if(num1.number == num2.number){
return 0;
}
else if (num1.number > num2.number)
return 1;
else
return -1;
}
}
So, I removed the following code in the comparator so that objects with same number property cannot be overwritten.
if(num1.number == num2.number){
return 0;
}
Now, the overall code looks like the following
import java.util.*;
public class Test {
public static void main(String[] args) {
TreeSet<NumberWrapper> set = new TreeSet<NumberWrapper>(new Helper());
set.add(new NumberWrapper(2));
set.add(new NumberWrapper(3));
set.add(new NumberWrapper(2));
set.add(new NumberWrapper(1));
// Subset to hold all objects which have the "number" property greater than or equal
// to 2 and less than 3. (Basically all 2s)
TreeSet<NumberWrapper> subSet = (TreeSet<NumberWrapper>) set.subSet(new
NumberWrapper(2),new NumberWrapper(3));
// Output here will 2 , 2,
for (NumberWrapper value : subSet){
System.out.print(value.number + " ,");
}
}
}
class NumberWrapper {
Integer number;
NumberWrapper(Integer number){
this.number = number;
}
}
class Helper implements Comparator<NumberWrapper> {
public int compare(NumberWrapper num1, NumberWrapper num2)
{
if (num1.number > num2.number)
return 1;
else
return -1;
}
}
This code seems to be working as expected for me. All the objects with same number property are not getting overwritten. But I have a couple of questions
I am not sure if the compare method I wrote for comparator is not buggy. Will there be a chance that some of the objects with same number property will not be returned when I try to get all the objects that have the number property in a certain range?
Is there any other data structure I should be considering for my needs?
Related
I have a recursive function that generates a list of lists that keeps tracks of valid hand combinations for a card game:
List<List<HandComponent>> validSCompArrangements = new ArrayList<>();
This list is populated by the recursive function successfully but often has duplicate sub-lists (by content but not by not order) that are unavoidable due to the required nature of the function. I wish to remove these duplicate sub-list entries (List<\HandComponent>) so that the above list in the end only features sub-lists that are unique in content, as order does not matter.
Here is the important part of the HandComponent class:
public class HandComponent {
private Type mType;
private Card mCard; // For runs this is the middle card
private Source mSource;
public HandComponent(Type type, Card card, Source source)
{
init(type, card, source);
}
public enum Type {PAIR, TRIPLE, QUAD, RUN}
public enum Source {STOLEN, SECRET, EITHER}
...
}
A sub-list List should only be considered equal to another sub-list if it contains the same exact HandComponents (i.e. the Type, Card, and Source between components of each list must be the same). Card is another enum defined in another file.
So, if two lists in "validSCompArrangements" are
(PAIR,CARD1,STOLEN), (TRIPLE,CARD7,STOLEN), (RUN, CARD8, SECRET)
and
(TRIPLE,CARD7,STOLEN), (RUN, CARD8, SECRET), (PAIR,CARD1, STOLEN)
they should be considered the same since they ultimately contain the same HandComponents even though the order is different and one should be removed so that "validSCompArrangements" only contains that unique list once.
Looking into this I've found bits and pieces on how to solve this problem but nothing that features this combination of a list of lists with custom objects.
One method seems to be to implement a custom Comparator that compares HandComponent instances to use with Collections in order to sort the sub-lists and then another custom Comparator to compare these sorted sub-lists for duplicates, though that seems a tad clunky and I'm not entirely sure how to override the compare method and what kind of return it expects for each comparator I'd need to make. The only other thing I've seen gestured at is that since for my usage the order of both the sub-lists and the main "validSCompArrangements" list itself don't matter, that I should be using Sets and a HashSet to solve this problem instead, I have no idea how to use those to fix this issue, other than that I might need to override the hashCode and equals methods for my HandComponent class, again, not being sure how to do so.
Overall I'm just a bit confused since any example I can manage to find thats remotely related to this usually is talking about just one list of custom objects that contain primatives and not enums, or a list of lists that uses only primatives and no custom objects at all. The fact this is a list of lists of custom objects who's members are enums has me a tad lost on how to go about this.
For example the marked answer in this question: Using collection to remove duplicate Lists, that only handles a portion of my problem, doesn't even seem to work for me despite the OP saying it does. Running that code as is, other than changing
Set<Integer> dedupedCollection = new HashSet<Integer>();
to
Set<List<Integer>> dedupedCollection = new HashSet<>();
as it was clearly meant to be, produces a collection of 3 entries where the second entry of 5, 10, 5 isn't seen as a duplicate and ignored as the OP suggested it should.
EDIT:
So far the closest thing I've found is converting my top-level list to a HashSet using:
Set<List<HandComponent>> handSet = new HashSet<>(validSCompArrangments);
but this only eliminates duplicate lists if their order is the same (which I am guessing is due to the nature of List's default implementation of "equals()"), while I need it to consider lists that are the same in content but different in order as duplicates also. One way around this would be to use Sets for the HandComponent sub-lists as well since they don't care about order innately, but this would prevent those sets from having duplicate HandComponents which I do need to be allowed.
As you said, you just need to implement equals :)
I've provided you how to implement equals method in the HandComponent class and how to use HashSet to getting only the combinations without duplicates.
I've implemented it in Java 8, you can also try to change it using for loop if you want :)
Here is the equals implementation of `HandComponent
public class HandComponent {
public enum Type {PAIR, TRIPLE, QUAD, RUN}
public enum Source {STOLEN, SECRET, EITHER}
public enum Card {ACE, ONE, TWO, TRHEE}
private Type type;
private Card card;
private Source source;
public HandComponent(Type type, Card card, Source source) {
this.type = type;
this.card = card;
this.source = source;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof HandComponent)) {
return false;
}
HandComponent handComponent = (HandComponent) o;
if (type != handComponent.type) {
return false;
}
if (card != handComponent.card) {
return false;
}
if (source != handComponent.source) {
return false;
}
return true;
}
#Override
public String toString() {
return "HandComponent=[" + String.join(", ", Arrays.asList(type.toString(), card.toString(), source.toString())) + "]";
}
}
And below you can see how you can use it
public class Main {
public static void main(String[] args) {
// Creating 2 hand components
HandComponent handComponent1 = new HandComponent(HandComponent.Type.PAIR, HandComponent.Card.ACE, HandComponent.Source.STOLEN);
HandComponent handComponent2 = new HandComponent(HandComponent.Type.QUAD, HandComponent.Card.TRHEE, HandComponent.Source.EITHER);
// 2 combinations with the same card, but different order => they are the same
List<HandComponent> firstCombination = Arrays.asList(handComponent1, handComponent2);
List<HandComponent> secondCombination = Arrays.asList(handComponent2, handComponent1);
// Mixing 2 combinations together
List<List<HandComponent>> combinations = Arrays.asList(firstCombination, secondCombination);
// printing the mix
System.out.println("Before: " + combinations);
// removing duplicates
List<ArrayList<HandComponent>> collect = combinations.stream() // having a stream of list<HandComponent>
.map(HashSet::new) // converting to HashSet, which mean there won't be duplicate in the combinations.
.distinct() // getting only the distinct combinations
.map(ArrayList::new) // reconverting to array list
.collect(Collectors.toList()); // collecting them as list
// result without duplicates
System.out.println("After: " + collect);
// You can now implement it with loop and no java 8 :)
}
}
What ended up working best for me was to implement the "equals()" method for my HandComponent class as suggested by Jiajie Xu, along with the "hashCode()" method automatically generated by Android Studio by using the option in the context menu or Alt + Insert:
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
HandComponent that = (HandComponent) o;
return mType == that.mType &&
mCard == that.mCard &&
mSource == that.mSource;
}
#Override
public int hashCode() {
return Objects.hash(mType, mCard, mSource);
}
I then also made the class implement the Comparable interface for use with the Collections class and specified the sort order priority of HandComponent instances within the "compareTo()" method like so:
#Override
public int compareTo(#NonNull HandComponent other) {
// Check Type first
int compareResult = mType.compareTo(other.mType);
if(compareResult == 0)
{
// Check Card second
compareResult = mCard.compareTo(other.mCard);
if(compareResult == 0)
{
// Check Source last
compareResult = mSource.compareTo(other.mSource);
}
}
return compareResult;
}
Since the default implementation of Comparable for List requires list order to be the same in order to return "true" when comparing two lists, I needed to sort my List of Lists every time I wanted to remove duplicates, which was perfectly fine as I benefited from the organization later on.
Ultimately, this allowed me remove the duplicates from my List of Lists of custom objects by first making sure the sub-lists of HandComponent were all sorted and then creating a HashSet of the top-level list.
List<List<HandComponent>> unsortedList = new ArrayList<>();
... // Populate list
for(int i = 0; i < unsortedList.size(); i++)
{
Collections.sort(unsortedList.get(i));
}
Set<List<HandComponent>> sortedDeDupedSet = new HashSet<>(unsortedList);
// Convert back to list since I need order to matter again later on
List<List<HandComponenet>> sortedDeDupedList = new ArrayList<>(sortedDeDupedSet);
This correctly removes duplicates from the top-level list now that I have properly implemented the "equals()" and "hashCode()" methods, as well as sorted the lists before hand with "compareTo()" by leveraging List's default Comparable implementation. Having to use a for loop to sort the lists themselves since I'm restricted to Java 7 does feel a little bad, but like I said before ultimately it was useful to have the lists sorted for other purposes and a lot of time and code is still saved from using a HashSet, versus the nested for loops that would be required to compare each List entry manually.
There are many other questions on Stack Overflow like this, but this one asks for something different than the others. I want to know how to take all permutations of an int array, without repeats, and put them in another 2D array. For example, the input:
{1,2,3}
the output:
{1,2,3}
{1,3,2}
{2,1,3}
{2,3,1}
{3,1,2}
{3,2,1}
How can I accomplish this? I'd like just a verbal walk through on how to do this, or even better some code. My question differs from this one
because the linked one uses C++ functions to accomplish this. I use Java.
Thanks
Java is an object-oriented language and so I believe it is useful to consider what objects your problem might contain.
One thing that immediately jumps out of your problem domain is the triple-set of integers, so why not define this as an object:
public class Triad {
private final int[] components;
public Triad(int... numbers) {
this.components = numbers;
if (components.length != 3) throw new IllegalArgumentException();
}
#Override public boolean equals(Object ob) {
if (ob == this) return true;
if (!(ob instanceof Triad)) return false;
Triad test = (Triad) ob;
return Arrays.equals(this.components, test.components);
}
#Override public int hashCode() {
return Arrays.hashCode(this.components);
}
}
Note that Triad defines an equals() method and a hashCode() method. This is important for a couple of reasons. Triad is a value class, i.e. instances of Triad represent values rather than something active. Value classes typically:
should be immutable (as we have defined Triad so far, it is immutable)
have well-formed equals() and hashCode() methods
the last property above allows instances to be used without fear with the Java Collections Framework. Now let's put the Collections Framework to use:
public static void main(String[] args) {
Set<Triad> triads = new HashSet<Triad>();
Triad inputTriad;
while (true) {
int[] numbers = ... // read in data from some source
if (numbers == null) break;
inputTriad = new Triad(numbers);
triads.add(inputTriad);
}
// after the loop has completed, the HashSet instance triad will contain
// all your input triads. The contract of HashSet guarantees that there
// will be no duplicates.
:
:
}
If you must have your result in int arrays, it is now a simple matter to iterate through the HashSet instance and assign the component values of each element to your result arrays.
So this was going to be my question, but I actually figured out the problem while I was writing it. Perhaps this will be useful for others (I will remove the question if it's a duplicate or is deemed inappropriate for this site). I know of two possible solutions to my problem, but perhaps someone will come up with a better one than I thought of.
I don't understand why TreeSet isn't removing the first element here. The size of the my TreeSet is supposed to stay bounded, but appears to grow without bound.
Here is what I believe to be the relevant code:
This code resides inside of a double for loop. NUM_GROUPs is a static final int which is set to 100. newGroups is a TreeSet<TeamGroup> object which is initialized (with no elements) before the double for loop (the variables group and team are from the two for-each loops).
final TeamGroup newGroup = new TeamGroup(group, team);
newGroups.add(newGroup);
System.err.println("size of newGroups: " + newGroups.size());
if (newGroups.size() > NUM_GROUPS) {
System.err.println("removing first from newGroups");
newGroups.remove(newGroups.first());
System.err.println("new size of newGroups: "
+ newGroups.size());
}
I included my debugging statements to show that the problem really does appear to happen. I get the following types of output:
size of newGroups: 44011
removing first from newGroups
new size of newGroups: 44011
You see that although the if statement is clearly being entered, the size of the TreeSet<TeamGroup> teamGroups isn't being decremented. It would seem to me that the only way for this to happen is if the remove call doesn't remove anything--but how can it not remove something from a call to first() which should definitely be an element in the TreeSet?
Here is the compareTo method in my TeamGroup class (score is an int which could very reasonably be the same for many different TeamGroup objects hence why I use the R_ID field as a tie-breaker):
public int compareTo(TeamGroup o) {
// sorts low to high so that when you pop off of the TreeSet object, the
// lowest value gets popped off (and keeps the highest values).
if (o.score == this.score)
return this.R_ID - o.R_ID;
return this.score - o.score;
}
Here is the equals method for my TeamGroup class:
#Override
public boolean equals(final Object o) {
return this.R_ID == ((TeamGroup) o).R_ID;
}
...I'm not worried about a ClassCastException here because this is specifically pertaining to my above problem where I never try to compare a TeamGroup object with anything but another TeamGroup object--and this is definitely not the problem (at least not a ClassCastException problem).
The R_ID's are supposed to be unique and I guarantee this by the following:
private static final double WIDTH = (double) Integer.MAX_VALUE
- (double) Integer.MIN_VALUE;
private static final Map<Integer, Integer> MAPPED_IDS =
new HashMap<Integer, Integer>(50000);
...
public final int R_ID = TeamGroup.getNewID();
...
private static int getNewID() {
int randID = randID();
while (MAPPED_IDS.get(randID) != null) {
randID = randID();
}
MAPPED_IDS.put(randID, randID);
return randID;
}
private static int randID() {
return (int) (Integer.MIN_VALUE + Math.random() * WIDTH);
}
The problem is here:
return this.R_ID - o.R_ID;
It should be:
return Integer.compare(this.R_ID, o.R_ID);
Taking the difference of two int or Integer values works if the values are both guaranteed to be non-negative. However, in your example, you are using ID values across the entire range of int / Integer and that means that the subtraction can lead to overflow ... and an incorrect result for compareTo.
The incorrect implementation leads to situations where the compareTo method is not reflexive; i.e. integers I1, I2 and I3 where the compareTo method says that I1 < I2 and I2 < I3, but also I3 < I1. When you plug this into TreeSet, elements get inserted into the tree in the wrong place, and strange behaviours happen. Precisely what is happening is hard to predict - it will depend on the objects that are inserted, and the order they are inserted.
TreeSet.first() should definitely return an object which belongs to the set, right?
Probably ...
So then why can it not remove this object?
Probably because it can't find it ... because of the broken compareTo.
To understand what exactly is going on, you would been to single step through the TreeSet code, etcetera.
I am attempting to sort an ArrayList based on the value of a long present within each object. After following various examples around the internet, I have come up with the following code but it is not sorting as desired (it seems to truncate parts of the object).
public static Comparator<Customer> compareSIN =
new Comparator<Customer>() {
public int compare(Customer cust1, Customer other) {
String sin1 = "" + cust1.sin;
String sin2 = "" + other.sin;
return sin1.compareTo(sin2);
}
};
Please advise me on what I am doing missing in the first snippet of code that is preventing me from sorting the objects properly.
Thanks!
From the title I assume Customer.sin is a long - and the problem is you are trying to compare them as Strings rather then by their numeric value.
(Example: 10000 is lexicographically smaller then 2 - so using Strings here is the fault)
You should use Long.compare() (Assuming java 7):
public static Comparator<Customer> compareSIN =
new Comparator<Customer>() {
public int compare(Customer cust1, Customer other) {
return Long.compare(cust1.sin,other.sin);
}
};
You do not actually need to use a compareTo() method inside your own compareTo() method.
The compare to states that it must return 0 if they are equal and negative or positive numbers for non equality.
For this reason you can compare two longs by returning the one subtracted from the other.
public int compare(Customer cust1, Customer other) {
return cust1.sin - other.sin;
}
This will as you can see, return 0 if they are equal, negative if other.sin is greater than cust1.sin and positive if cust1.sin is greater than other.sin.
You compare Strings instead of longs.
So, imagine you want to compare : "10" and "5", outcome would be "10" < "5" whereas thinking that you're working with long, you expect to get 10 > 5 ...
That can explain your issue.
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.