Sort a list based on two conditions - java

So, I don't know if there is an elegant solution to this but here goes. I want to sort a list but the list contains three types of items. What i want is for type A to be on top sorted alphabetically, type B & C to be on the bottom and sorted alphabetically (type B & C will be combined).
Here is my code:
public int compareTo(Friendship another) {
if(this.getType().equals(TypeA) &&
another.getType().equals(TypeA)){ //if they are both type A, just sort based on user name
return this.getUsername().compareTo(
another.getUsername());
}
else if(this.getType.equals(TypeA)){
return -1;
}
else if(another.getType().equals(TypeA)){
return 1;
}
else{ //this will be hit if they are either Type B or C, then just sort based on username
return this.getUsername().compareTo(
another.getUsername());
}
}
EDIT: sorry, I should have explained this a lot better. The problem is that the above code isn't working. From what I am seeing, the list doesn't seem to be properly ordered.
The TypeA list is for some reason ordered opposite of what I want( Z -> A). And the TypeB & C list is only half sorted. So I am assuming that there is a bug in my code. Let me know if you need more info.
EDIT2: Did some more tests on samples and it looks like the strings aren't being sorted at all. I did both
this.getUsername().compareTo(
another.getUsername());
and
another.getUsername().compareTo(
this.getUsername());
EDIT 3: you guys were right. there was a mistake elsewhere in my code (that was unrelated). Sorry... also don't really know what to do in this case. Who do I give the right answer to?

If i be you, i won't change structure, but only optimize this little bit
public int compareTo(Friendship another) {
if(!this.getType().equals(another.getType()){
//if type are not equal, so we might have at most one A
if(this.getType.equals(TypeA)){ //on left side
return -1;
}
if(another.getType().equals(TypeA)){ //or, on rightside
return 1;
}
}
//or we have on both sides or neither side
return this.getUsername().compareTo(
another.getUsername());
}

I use a similar solution in the same situation, and I think it is good.
But code can be shorter:
public int compareTo(Friendship another) {
boolean thisOnTop = getType().equals(TypeA);
boolean anotherOnTop = another.getType().equals(TypeA);
if (thisOnTop != anotherOnTop) {
return thisOnTop ? -1 : 1;
} else {
return this.getUsername().compareTo(another.getUsername());
}
}

You just have to implement your compareTo in the 3 classes with that logic you said. Something like this:
// TypeA.class
// TypeA class will have priority over the other two, so just sort by whatever you want
public int compareTo(AnotherType anotherType) {
if (this.equals(anotherType)) // TypeA vs TypeA - alphabetically
return this.getUsername().compareTo(anotherType.getUsername());
else // otherwise typeA is greater
return 1; // 1 means greater than
}
// TypeB.class
public int compareTo(AnotherType anotherType) {
if (this.equals(anotherType)) // both typeB, sort alphabetically
return this.getUsername().compareTo(anotherType.getUsername());
else
if(this.equals(typeC)) // TypeB vs TypeC, alphabetically
return this.getUsername().compareTo(typeC.getUsername());
else // TypeB vs TypeA
return -1; // -1 means lesser than
}
//TypeC.class
public int compareTo(AnotherType anotherType) {
if (this.equals(anotherType)) // TypeC vs TypeC - alphabetically
return this.getUsername().compareTo(anotherType.getUsername());
else
if(this.equals(typeB)) // TypeC vs TypeB - alphabetically
return this.getUsername().compareTo(typeB.getUsername());
else
return -1; // -1 means lesser than
}

There is an elegant way to solve this, and it does not involve ugly compareTo trainwrecks.
Pass through your list and make 2 SortedSet, one for A and one for B + C. Add your Friendships based on their Type.
Make a new list and use Collections.addAll() method to appent to the list the 2 arrays you can get from the 2 SortedSet, first the one for A, then the one for B+C.
Since SortedSet will keep the contents in natural order, which is lexicographical for strings, your final list will have Type A first, sorted lexicographically, B and C after, also sorted lexicographically.

Related

intersection of two given LinkedList

Find the intersection of two given LinkedList (where each node has a character).
Return the LinkedList which has character which appears in both LinkedList (same sequence order as LinkedList1).
error:variable temp might not have been initialized
I tried a lot to resolve this error, but I cant step forward in this problem. Please help to resolve my error.
public SchNode func(SchNode head1, SchNode head2)
{
SchNode temp;
for(SchNode ptr=head1;ptr!=null;ptr=ptr.nextNode)
{
for(SchNode ptr2=head2;ptr2!=null;ptr2=ptr2.nextNode)
{
if(ptr.ch==ptr2.ch)
{
temp.ch=ptr2.ch;
temp=temp.nextNode;
}
}
}
return temp ;
}
You can use this method if don't understand it wrong.
measure list1 length, lets call it A
measure list2 length, lets call it B
difference is: C = A - B
if C < 0 then choose list2, else choose list1, lets call chosen list choosenList
answer is choosenList[abs(C)] //i mean absolute value of C
EDIT: what i understand is you have two linked lists that have a common node(pointer)

In Java what is the quickest way to check if list contains items from another list, both list are of same type?

Say I have class called MyClass as follow:
public class MyClass
{
//Identifier is alpha-numeric. If the identifier starts will 'ZZ'
//is special special identifier.
private String identifier = null;
//Date string format YYYY-MM-DD
private String dateString = null;
//Just a flag (not important for this scenario)
private boolean isCoolCat = false;
//Default Constructor and getters/setters implemented
//Overrides the standard Java equals() method.
//This way, when ArrayList calls contains() for MyClass objects
//it will only check the Date (for ZZ identifier)
//and identifier values against each other instead of
//also comparing the isCoolCat indicator value.
#Override
public boolean equals(Object obj)
{
if(this == obj)
{
return true;
}
if(obj == null)
{
return false;
}
if(getClass() != obj.getClass())
{
return false;
}
MyClass other = (MyClass) obj;
if(this.identifier == null)
{
if(other.identifier != null)
{
return false;
}
} else if(!this.identifier.equals(other.identifier)) {
return false;
}
if(other.identifier.startsWith("ZZ"))
{
if(!this.dateString.equals(other.dateString))
{
return false;
}
}
return true;
}
}
In another class I have two List of MyClass type, each contain 100,000 objects. I need to check if items in one list are in the other list and I currently accomplish this as follow:
`
List<MyClass> inList = new ArrayList<MyClass>();
List<MyClass> outList = new ArrayList<MyClass>();
inList = someMethodForIn();
outList = someMethodForOut();
//For loop iterates through inList and check if outList contains
//MyClass object from inList if it doesn't then it adds it.
for(MyClass inObj : inList)
{
if(!outList.contains(inObj))
{
outList.add(inObj);
}
}
My question is: Is this the fastest way to accomplish this? If not can you please show me a better implementation that will give me a performance boost? The list size is not always going to be 100,000. Currently on my platform it takes about 2 minutes for 100,000 size. Say it can vary from 1 to 1,000,000.
You want to use a Set for this. Set has a contains method which can determine if an object is in the set in O(1) time.
A couple things to watch out for when converting from List<MyClass> to Set<MyClass>:
You will lose the ordering of the elements
You will lose the duplicate elements
Your MyClass needs to implement hashcode() and equals(), and they should be consistent.
To convert your List to Set you can just use:
Set<MyObject> s1 = new HashSet<>(inList);
Set<MyObject> s2 = new HashSet<>(outList);
This Java doc explains how to find the union, intersection, and difference of two sets. In particular, it seems like you're interested in the Union:
// transforms s2 into the union of s1 and s2. (The union of two sets
// is the set containing all of the elements contained in either set.)
s2.addAll(s1)
Hashing ! Hashing is always the answer !
Current complexity of this code is, O(nm) where n is the size of inList and m is the size of outList.
You can use a HashSet to reduce your complexity to O(n). Because contains will now take O(1)
This can be done like this,
HashSet<MyClass> outSet = new HashSet<>(outList);
for(MyClass inObj : inList)
{
if(!outSet.contains(inObj))
{
outList.add(inObj);
}
}
Credits and Sources.
returning difference between two lists in java
Time complexity of contains(Object o), in an ArrayList of Objects
HashSet.contains performance
2 minutes comparing 2 very large lists, probably not going to get much time savings here, so depending on your application, can you set a flag so that things dependant on this cannot run until finished and push this into it's own thread and let the user do something else (while also telling them this is on-going.) Or at least put up a progress bar. Letting the user know the app is busy and telling them (ish) how long it will take on something only taking a few minutes in a very complex computation like this is OK and probably better than just shaving a few seconds off the time. users are quite tolerant of delays if they know how long they will be and you tell them there is time to go get a coffee.

Data structure to check for pairs?

Say I have objects A,B,C,D. They can contain references to one another, for example, A might reference B and C, and C might reference A. I want to create segments but dont want to create them twice, so I don't want segment A C and segment C A, just 1 of them. So I want to keep a list of created segments, ex: A C, and check if I already have an A C or C A and skip it if so.
Is there a data structure that can do this?
Thanks
if(list.contains(a,b)
{
//dont add
}
you may introduce something like
class PairKey<T extends Comparable<T>> {
final T fst, snd;
public PairKey(T a, T b) {
if (a.compareTo(b) <=0 ) {
fst = a;
snd = b;
} else {
fst = b;
snd = a;
}
}
#Override
public int hashCode() {
return a.hashCode() & 37 & b.hashCode();
}
#Override
public boolean equals(Object other) {
if (other == this) return true;
if (!(other instanceOf PairKey)) return false;
PairKey<T> obj = (PairKey<T>) other;
return (obj.fst.equals(fst) && obj.snd.equals(snd));
}
}
then you may put edges into HashSet < PairKey < ? extends Comparable> > and then check if the given pair is already there.
You will need to make your vertexes comparable, so it will be possible to treat PairKey(A,B) equal to PairKey(B,A)
And then HashSet will do the rest for you, e.g you will be able to query
pairs.contains(new PairKey(A,B));
and if pairs contain either PairKey(A,B) or PairKey(B,A) - it will return true.
hashCode implementation might be slightly different, may be IDE will generate something more sophisticated.
Hope that helps.
I would use an object called Pair that would look something like this:
class Pair
{
Node start;
Node end;
public Pair(Node start, Node end)
{
this.start=start;
this.end=end;
}
public Pair reverse()
{
return new Pair(end,start);
}
}
Now you can do something like this:
if(pairs.contains(currentPair) || pairs.contains(currentPair.reverse())
{
continue;
} else{
pairs.add(currentPair);
}
As pointed out in the comments, you will need to implement equals and hashcode. However, doing the check in equals to make it match the reversal of the segment is a bad practice in a pure OO since. By implementing equals in the fashion, described within the comments, would bind Pair to your application only and remove the portability of it.
You can use a set of sets of objects.
Set<Set<MyObjectType>> segments = new HashSet<Set<MyObjectType>>();
Then you can add two-element sets representing pairs of MyObject. Since sets are unordered, if segments contains a set with A and B, attempting to add a set containing B and A will treat it as already present in segments.
Set<MyObjectType> segment = new HashSet<MyObjectType>();
segment.add(A); // A and B are instances of MyObjectType
segment.add(B);
segments.add(segment);
segment = new HashSet<MyObjectType>();
segment.add(B);
segment.add(A);
segments.add(segment);
System.out.println("Number of segments: " + segments.size()); // prints 1
Your problem is related with graph theory.
What you can try is to remove that internal list and create a Incidence Martrix, that all you objects share.
The final solution mostly depend of the task goal and available structure. So is hard to choose best solution for you problem with the description you have provided.
Use java.util.Set/ java.util.HashSet and keep adding the references you find e.g.
Set set1 = new HashSet();
set1.add(A), set1.Add(C), set1.Add(C)
You can add this finding in an external set, as finalSet.add(set1)
Set<Set> finalSet = new HashSet<Set>();
finalSet.add(set1);
This will filter out the duplicates automatically and in the end, you will be left with A & C only.

Sort a list of pairs of integers java

I want to sort an arraylist of pairs of integers. So far I've been able to sort them according to the first element, but I get something like (1,2), (1,-2). I want to also sort them according to the second element so I can get a correct sorted arraylist, but I cant seem to make it work.
The code for the first element sorting is:
private class FirstElmComparator implements Comparator<Pair> {
public int compare(Pair pr1, Pair pr2) {
return pr1.compareFirstElms(pr2);
}
}
and compareFirstElms function is the following:
protected int compareFirstElms (Pair p) {
return (new Integer (this.p1)).compareTo(new Integer (p.p1));
}
I can think of making the second element comparator as the following:
private class SecondElmComparator implements Comparator<Pair> {
public int compare(Pair pr1, Pair pr2) {
return pr1.compareSecondElms(pr2);
}
}
protected int compareSecondElms (Pair p) {
return (new Integer (this.p2)).compareTo(new Integer (p.p2));
}
NOTE: p1 and p2 are the first and second element in a pair.
But I think it will override the first element sorting order, or am I mistaken?
Can anybody help me with this.
You create one common comparator that evaluates both elements of the Pair.
public int compare(Pair pr1, Pair pr2) {
int firstResult = pr1.compareFirstElms(pr2);
if (firstResult == 0) { //First comparison returned that both elements are equal
return pr1.compareSecondElms(pr2);
} else {
return firstResult;
}
}
It's very simple, implement it like this:
If the first elements of the pairs to compare are different, then sort on the first element.
Otherwise (if the first elements are equal), sort on the second element.
You wouldn't use two distinct comparators but one (which might in turn call others to do the internal work).
So in pseudocode, the comparison would look like this:
public int compare(Pair pr1, Pair pr2) {
int result = compare(p1.first, p2.first);
if( result == 0 ) {
result = compare(p1.second, p2.second);
}
return result;
}
Well, first off, you need to write an explicit method for that:
public int compare(Pair p) {
int first = compareFirstElms(p);
return first == 0 ? compareSecondElms(p) : first;
}
Secondly, don’t over-engineer. Comparing two ints is as simple as writing this.p1 - p.p1. No need for conversions.
Thirdly, I would choose explicit, concise yet complete names. Don’t arbitrarily abbreviate parts of words, this doesn’t exactly help readability. How about compareByFirst and compareBySecond, respectively?

How to sort arraylist by mutiple level fields in Java

Class Activity{
private int level;
private Activity predecessor;
}
My program creates instances of Activity, puts them in an Arraylist and sorts them by two fields level and predecessor. predecessor is first compared.
For instance, if predecessor is not null, the predecessor Activity of the current activity should be first and it will be second. After satisfying the predecessor condition the following could be ordered by level.
List<Activity> activities = new ArrayList<Activity>();
//Add some activities.
activities.add(Activity);
activities.add(Activity);
Collections.sort(activities,getActivityComparator());
private Comparator<Activity> getActivityComparator() {
return new Comparator<Activity>() {
public int compare(Activity act1, Activity act2) {
if (act1 == null) {
if (act2 == null) {
return 0; // Both activities are null
} else {
return -1; // act1 is NULL, so put act1 in the end of
// the sorted list
}
} else {
if (act2 == null) {
return 1;
}
}
Activity preAct2 = act2.getPredecessor();
if(preAct2!=null&&preAct2==act1)
return -1;
//Adding this by Joeri Hendrickx's suggestion
Activity preAct1 = act1.getPredecessor();
if(preAct1!=null&&preAct1==act2)
return 1;
return act2.getLevel()-act1.getLevel();
}
};
}
I have written the above code and tested a lot, but it outputted inconstant ordering if I putted different ordering into Arrarylist. so definitely this is incorrect method to implement this. Seems the root cause is not every two elements were compared. says Activity act1>Activity act2(by level condition), Activity act2>Activity act3(by level condition), it doesn't mean Activity act1>Activity act3(by predecessor condition).
Here I came up with an new idea that is to use bubble sort instead of using interface sort of Collections. it will compare every two elements that don't need comparator transitive any more. how do you think?
Can anyone help me?
The problem is that your comparator is not describing a correct symmetric and transitive relation.
Consider the following three objects:
act1{predecessor:null, level:1}
act2{predecessor:act1, level:1}
act3{predecessor:null, level:0}
act4{predecessor:act2, level:2}
Now, where comp is your comparator, comp(act1, act2) will return -1, but comp(act2, act1) will return 0. This is not symmetric.
comp(act1, act2) will return -1, comp(act2, act4) will return -1, but comp(act1, act4) will return 1. This is not transitive.
A comparator must describe a correct reflexive, symmetric and transitive relation in order for it to work.
Edited after seeing Axtaxt's post: the problem you describe here cannot be accomplished with a normal sort, because it's always possible to construct a scenario where, by these rules, you get a loop in your sort hierarchy. So it's not possible to make a transitive comparator with only this data.
As far as I understand, you cannot make your comparator transitive at all due to possibility of the following loop (thick line - predcessor, thin line - greater than):
So, you have a partial order and general purpose sorting algorithms are not applicable here.
If you need order by level only among successors of the same activity, you can achieve it using the following simple algorithm:
Build a multimap from Activity to its direct successors
Build a tree with the following properties:
Each node contains an activity, root node has null activity
Subnodes of each node correspond to direct successors of its activity, ordered by levels
It can be created using the following algorithm:
Create a root node with null activity
For each node extract the corresponding successors from the multimap and create subnodes for them, apply it recursively
Traverse that tree
The compare method of comparator is defined as:
"Compares its two arguments for order. Returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second."
Which means if you want act1 to be in the end when its null, you have to return a positive integer -> act1 is greater -> act1 will be at the end.
You have your positive and negative integers mixed.
if (act1 == null) {
if (act2 == null) {
return 0; // Both activities are null
} else {
return 1; // act1 is NULL, so put act1 in the end of
// the sorted list
}
} else {
if (act2 == null) {
return -1;
}
}
Also, for the same reason you would need to use
return act1.getLevel()-act2.getLevel();
imaging a comparator for integers that receives 2 and 3.
You would want it to return a negative int because the first argument is less than the second. -> arg1-arg2 = -1
instead of arg2-arg1 like you did...

Categories