I have a TreeMap with a number of entries.
TreeMap<Long, List<Payment>> myPaymentsForYear;
To remove the first week from the Map, I do
private void removeDaysFromPast() {
for (int i = 0; i < WEEK; i++) {
long key = myPaymentsForYear().firstKey();
myPaymentsForYear.remove(key);
}
System.out.println( "date: " + new Date(myPaymentsForYear.firstKey()).toString());
}
However, the printed statement always shows that the firstKey has not been removed. I fact, non of the seven elements is removed. Does anyone know why?
A TreeMap remove statement will always return the object if it's successful, or a null if the key doesn't exist (given that null is an invalid key), or throw an exception. see reference . Have you verified through debugging that you are actually attempting to remove a key that exists and that your remove statement is executing?
The code I have above is completely correct. It turns out the error was entirely somewhere else.
would be nice if you could share where the error was?
In my case, I wanted the TreeMap with a long as key to be sorted in reverse order and implemented an according Comparator (which I then passed to the TreeMap in the constructor). But as the simple type "long" cannot be used in a Comparator, I had to use the object type "Long" instead:
public class LongComparatorInverted implements Comparator<Long> {
#Override
public int compare(Long lhs, Long rhs) {
return (lhs == rhs) ? 0 : (lhs < rhs) ? 1 : -1;
}
Sure, Android Studio shows you a warning on the "==", but painfully I ignored it...
With the "Long" being an object type, for sure there was no guarantee that I compare the same object when working with the TreeMap.
So the fix for me was - of course - to use equals instead:
return (lhs.equals(rhs)) ? 0 : (lhs < rhs) ? 1 : -1;
Not so easy to find, though...
So if you encounter such an error where "remove" is not working, I´d try to check first whether you use a special Comparator and fully test the Comparator before using it.
Related
I want to use Guava's Maps.difference in java 11 to verify data from Json String which I mapped into a Map.
What I have after a call (with different uid each time) :
{"uid":"31a340bc-e5ed-440c-8726-34c54dea902a","name":"Jean"}
I want to verify that uid is correctly generated and name is "Jean" using a pattern like this :
{"uid":"*","name":"Jean"}
Of course Maps.difference returns a difference in uid value...
Is it possible to specify a wildcard in my verifying pattern so that Maps.difference returns no differences ?
Thanks in advance.
Assuming you mean Guava's Maps.difference: yes, use the difference(left, right, valueEquivalence) overload:
Maps.difference(left, right, new Equivalence<String>() {
#Override public boolean doEquivalent(String a, String b) {
return "*".equals(a) || "*".equals(b) || Objects.equals(a, b);
}
// You have to hash all strings to the same thing because you
// don't know if the current item is a "*", or its corresponding
// item is a "*". But actually it doesn't matter, because I don't
// think it's invoked in difference.
#Override public int doHash(String s) { return 0; }
});
May I know how can I sort a List<Object[]> and order by more than one element ?
below is my code:
I retrieve my result from database using below code
public List<Object[]> readyToPrint() {
Query query = em.createNativeQuery("SELECT
so_order_no,so_bo_suffix,shortname1,ETD......;
List<Object[]> allObject = query.getResultList();
return allObject;
}
As what I know I can only read the result using Object am I right ? So i think i don't have any specific class. Then if the result of element[X] meet certain condition, I want to edit the content of that element, and then only I resorting the result again.
Then i retrieve my result using below code:
`List<Object[]> allList = salesOrderFacade.readyToPrint();
readyToPrintResult = new ArrayList<>();
for (Object[] list : allList) {
if (outgoingFacade.checkReadyToDelivery(list[0].toString(), list[1].toString())) {
readyToPrintResult.add(list);
}
}
I want to sort myreadyToPrintResult` list by element 2 and follow by element 3.
I tried the code below;
Collections.sort(readyToPrintResult, new Comparator<Object[]>() {
#Override
public int compare(Object[] lines1, Object[] lines2) {
return lines1[2].xxxxx;
}
});
but I stuck in the return part. I not able to use "compare" code.
Example:
I have a List of result :
-Apple , 11-01-2017 , Y , N
-Bubble ,11-01-2017 , Y ,N
-Cat , 11-01-2017 , Y ,N
-Dora , 11-01-2017 , N ,Y
-Elephant,11-01-2017, N,Y
Then if Elephant meet some condition, I want to change the list of result of elephant to :
Elephant,11-01-2017, Y,N
Then I would like to sorting the whole list and my expected result is as below:
-Apple , 11-01-2017 , Y , N
-Bubble ,11-01-2017 , Y ,N
-Cat , 11-01-2017 , Y ,N
-Elephant,11-01-2017, Y,N
-Dora , 11-01-2017 , N ,Y
Anyone can help?
Thanks in advance
Replace ActualType with any type you expect that is Comparable (I suppose it most likely would be String):
readyToPrintResult.sort((o1, o2) -> {
int res = ((ActualType) o1[2]).compareTo((ActualType) o2[2]);
return res != 0 ? res : ((ActualType) o1[3]).compareTo((ActualType) o2[3]);
});
By declaring the Object type in the Comparator declaration, you get two Object parameters to compare in the compare() method.
So, you can invoke only Object methods on them and besides Object doesn't implement the Comparable interface.
With the Object declared class, finally,you have not a lot of way to exploit it but a tricky use of the toString().
Whereas the error :
But I got stuck on the return part. I'm not able to use compare code
return lines1[2].xxxxx;
You have two ways :
you work with more specific class than Object[]. For example : Line[] (as you name it in this way).
In this way, you may specify the Line type in the Comparator declaration :
Collections.sort(readyToPrintResult, new Comparator<Line[]>() {
#Override
public int compare(Line[] lines1, Line[] lines2) {
return lines1[2].getValue().compareTo(lines2[2].getValue());
}
});
It is the finer solution.
If you cannot work with a more specific class than Object[] because you manipulate objects with distinct(and not derived) classes , you could check the type of the two parameters and cast them in the compare() method.
It is a really not advised way because you will have to perform a a lot of boiler plate code, casts and handling the cases when the two values doesn't rely on the same type.
The int returned by the compare code is based on your parameters.
If the first one (lines1[2]) is less than your second parameter (lines2[2]) then your compare method should return a negative integer.
If lines1[2] is greater than lines2[2] you return a positive integer.
If lines1[2] and lines2[2] are the same you could return 0 (and quit comparing these objects) or as you want it compare lines1[3] against lines2[3]. Here you have to check again and then return a negative or positive number or 0.
I think I may have found a bug in Java.
I have a TreeMap in which I use a custom comparator. However, it seems when I put(key, value), on a key that already exists, it does not override the key, thus creating duplicate keys. I think I have verified this because I tried:
System.out.println(testMap.firstKey().equals(testMap.lastKey()));
And this prints out true. Anyone know why this is happening?
This is the comparator code:
private class TestComp implements Comparator<String> {
#Override
public int compare(String s1, String s2){
if (s1.equals(s2)) {
return 0;
}
int temp = otherMap.get(s1).compareTo(otherMap.get(s2));
if (temp > 0) {
return 1;
}
return -1;
}
A comparator always needs to return consistent results, and when used in a TreeMap, be consistent with equals.
In this case your comparator violates the first constraint since it does not necessarily give consistent results.
Example: If for instance otherMap maps
"a" -> "someString"
"b" -> "someString"
then both compare("a", "b") and compare("b", "a") will return -1.
Note that if you change the implementation to
if (s1.equals(s2)) {
return 0;
}
return otherMap.get(s1).compareTo(otherMap.get(s2));
you break the other criteria of being consistent with equals, since otherMap.get(s1).compareTo(otherMap.get(s2)) might return 0 even though s1 does not equal s2.
I've elaborated on this in a self-answered follow up question here.
From the comments:
Even if a comparator gives inconsistent results, shouldn't the Java language still not allow duplicate keys?
No, when you insert a key, the TreeMap will use the comparator to search the data structure to see if the key already exists. If the comparator gives inconsistent results, the TreeMap might look in the wrong place and conclude that the key does not exist, leading to undefined behavior.
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 learned about the Comparable interface, for which a class must implement compareTo method. A project I am using that method as:
public class EmployeeAssignmentTotal implements Comparable<EmployeeAssignmentTotal>, Serializable {
private Employee employee;
private int total;
....
public int compareTo(EmployeeAssignmentTotal other) {
return new CompareToBuilder()
.append(employee, other.employee)
.append(total, other.total)
.toComparison();
}
What exacly does CompareToBuilder do here? And how is it interacting with the employee and total attributes?
I did read the javadocs, but I cant make head or tail of what they are doing with the constructor and the multiple appends. Does this question indicate unclear intentions and zero research?
I was trying to figure out how CompareToBuilder works myself and I came across this post, but I wasn't happy with the answer. For instance, the toComparison method should return a negative integer, a positive integer, or zero as the builder has judged the "left-hand" side as less than, greater than, or equal to the "right-hand" side.
So my question was how the order of appending attributes affect the comparison. To answer that, the only thing I could do was check the source code and I found this:
public CompareToBuilder append(int lhs, int rhs) {
if (this.comparison != 0) {
return this;
}
this.comparison = ((lhs > rhs) ? 1 : (lhs < rhs) ? -1 : 0);
return this;
}
So, basically what is happening is that it will compare the attributes based on the order you append them. In your code example, "employee" will be evaluated first. If the left-hand side attributes evaluates as less than or greater then the right-hand side then, total is disregarded.
The attribute "total" will only be evaluated if "employee" evaluates to equal.
This class is meant to assist you in building compareTo()-methods. Imagine you had more than just 2 fields in your class - manually coding your comparison-method could be quite cumbersome.
CompareToBuilder is doing that for you - each append() method is adding a new comparison, and all comparisons are &&ed.
So the code you posted runs equals() on the employee object and total.