I need to check if all values in a map are equal. I have a method to perform this task but would like to use a library or native methods. Limitations: Java 5 + Apache Commons libraries.
public static boolean isUnique(Map<Dboid,?> aMap){
boolean isUnique = true;
Object currValue = null;
int iteration = 0;
Iterator<?> it = aMap.entrySet().iterator();
while(it.hasNext() && isUnique){
iteration++;
Object value = it.next();
if(iteration > 1){
if (value != null && currValue == null ||
value == null && currValue != null ||
value != null && currValue != null & !value.equals(currValue)) {
isUnique = false;
}
}
currValue = value;
}
return isUnique;
}
What about this something like this:
Set<String> values = new HashSet<String>(aMap.values());
boolean isUnique = values.size() == 1;
how about
return (new HashSet(aMap.values()).size() == 1)
I know the original questions asks for solutions in Java 5, but in case someone else searching for an answer to this question is not limited to Java 5 here is a Java 8 approach.
return aMap.values().stream().distinct().limit(2).count() < 2
You could store the values in a Bidirectional Map and always have this property.
public static boolean isUnique(Map<Dboid,?> aMap) {
Set<Object> values = new HashSet<Object>();
for (Map.Entry<Dboid,?> entry : aMap.entrySet()) {
if (!values.isEmpty() && values.add(entry.getValue())) {
return false;
}
}
return true;
}
This solution has the advantage to offer a memory-saving short cut if there are many differences in the map. For the special case of an empty Map you might choose false as return value, change it appropriately for your purpose.
Or even better without a Set (if your Map does not contain null-values):
public static boolean isUnique(Map<Dboid,?> aMap) {
Object value = null;
for (Object entry : aMap.values()) {
if (value == null) {
value = entry;
} else if (!value.equals(entry)) {
return false;
}
}
return true;
}
As my comment above:
//think in a more proper name isAllValuesAreUnique for example
public static boolean isUnique(Map<Dboid,?> aMap){
if(aMap == null)
return true; // or throw IlegalArgumentException()
Collection<?> c = aMap.getValues();
return new HashSet<>(c).size() <= 1;
}
Related
I don't know why find() method does not work for some numbers. Here is the code.
I'm talking about finding element in Double Linked List.
public DLLNode<E> find(E o) {
if (first != null) {
DLLNode<E> tmp = first;
while (tmp.element != o && tmp.succ != null)
tmp = tmp.succ;
if (tmp.element == o) {
return tmp;
} else {
System.out.println("Element does not exist in a list");
}
} else {
System.out.println("List is empty");
}
return first;
}
Most likely, your issue is with :
if (tmp.element == o) {
return tmp;
}
which is comparing objects using reference equality, not value equality. You want to use .equals for that. You mention it works for some numbers, which I'm guessing means you have a DLLNode<Integer> in your test - you're probably just running into the fact that the JVM caches a small subset of Integer objects (I think between -127 and +128) so those appear to work when using ==.
You need to use equals instead of ==
== compares references, ex:
new Double( 2d ) == new Double( 2d ) will be false,
but new Double( 2d ).equals(new Double( 2d )) will be true.
public DLLNode<E> find(E o) {
if (first != null) {
DLLNode<E> tmp = first;
while (!tmp.element.equals(o) && tmp.succ != null)
tmp = tmp.succ;
if (tmp.element.equals(o)) {
return tmp;
} else {
System.out.println("Element does not exist in a list");
}
} else {
System.out.println("List is empty");
}
return first;
}
Here's an excerpt from Java Concurrency in Practice:
public class DelegatingVehicleTracker {
private final ConcurrentMap<String, Point> locations;
private final Map<String, Point> unmodifiableMap;
public DelegatingVehicleTracker(final Map<String, Point> points) {
this.locations = new ConcurrentHashMap<>(points);
this.unmodifiableMap = Collections.unmodifiableMap(this.locations);
}
public Map<String, Point> getLocations() {
return unmodifiableMap;
}
public Point getLocation(final String id) {
return locations.get(id);
}
public void setLocation(final String id, final int x, final int y) {
if (null == locations.replace(id, new Point(x, y))) {
throw new IllegalArgumentException("Invalid vehicle name: " + id);
}
}
}
My question is about the setLocation method which uses ConcurrentMap.replace. The JavaDoc of this method says that it's equivalent to:
if (map.containsKey(key)) {
return map.put(key, value);
} else return null;
except that the action is performed atomically.
What could go wrong if we don't use the atomic version. One possibility is that one thread sees that the map contains a given key and before it puts a new value for that key, another thread removes that key-value pair, but since the class in the example does not allow removals, this cannot happen.
Another possibility is that two threads attempt to replace the same key with different values. In that case one thread may not return the correct previous value but in the example we don't care about the previous value, the method setLocation returns void.
So, it seems as though the method could be rewritten without replace. And that is what prompted my question. In a subsequent version of the same class in the book, which almost identical to the one above, the method setLocation does not use replace, just containsKey and I was wondering if this could compromise thread safety.
the method setLocation does not use replace, just containsKey and I
was wondering if this could compromise thread safety.
It does, you've described it perfectly
What could go wrong if we don't use the atomic version. One
possibility is that one thread sees that the map contains a given key
and before it puts a new value for that key, another thread removes
that key-value pair, but since the class in the example does not allow
removals, this cannot happen.
That's why the implementation of ConcurrentHashMap.replace locks the node it tries to replace
/**
* Implementation for the four public remove/replace methods:
* Replaces node value with v, conditional upon match of cv if
* non-null. If resulting value is null, delete.
*/
final V replaceNode(Object key, V value, Object cv) {
int hash = spread(key.hashCode());
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0 ||
(f = tabAt(tab, i = (n - 1) & hash)) == null)
break;
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
V oldVal = null;
boolean validated = false;
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
validated = true;
for (Node<K,V> e = f, pred = null;;) {
K ek;
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
V ev = e.val;
if (cv == null || cv == ev ||
(ev != null && cv.equals(ev))) {
oldVal = ev;
if (value != null)
e.val = value;
else if (pred != null)
pred.next = e.next;
else
setTabAt(tab, i, e.next);
}
break;
}
pred = e;
if ((e = e.next) == null)
break;
}
}
else if (f instanceof TreeBin) {
validated = true;
TreeBin<K,V> t = (TreeBin<K,V>)f;
TreeNode<K,V> r, p;
if ((r = t.root) != null &&
(p = r.findTreeNode(hash, key, null)) != null) {
V pv = p.val;
if (cv == null || cv == pv ||
(pv != null && cv.equals(pv))) {
oldVal = pv;
if (value != null)
p.val = value;
else if (t.removeTreeNode(p))
setTabAt(tab, i, untreeify(t.first));
}
}
}
}
}
if (validated) {
if (oldVal != null) {
if (value == null)
addCount(-1L, -1);
return oldVal;
}
break;
}
}
}
return null;
}
What could go wrong if we don't use the atomic version
Nothing.
It's stylistic. You could implement the setLocation method whatever way you want, but it just so happens that replace is a nice way to ensure that you only insert if the given location exists in the map.
In a subsequent version of the same class in the book, which almost identical to the one above, the method setLocation does not use replace, just containsKey and I was wondering if this could compromise thread safety.
It does not compromise thread safety. The assertion that the key is contained in the map cannot change in a concurrent write because given that the key is present, it will not suddenly be removed, and given that it is not present, nothing will be done.
The author decided to use contains key because the second example uses a mutable point rather than an immutable point. Recall that JCIP was written for Java 5 and such methods as computeIfPresent did not exist at the time. Therefore, the author must obtain the object it self in order to modify it. Thread safety will therefore be delegated to the mutable point rather than the vehicle tracker itself.
I have tried many possible solution given on the net like to set System property and to convert in double but still getting same error:
java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.ComparableTimSort.mergeHi(ComparableTimSort.java:835)
at java.util.ComparableTimSort.mergeAt(ComparableTimSort.java:453)
at java.util.ComparableTimSort.mergeForceCollapse(ComparableTimSort.java:392)
at java.util.ComparableTimSort.sort(ComparableTimSort.java:191)
at java.util.ComparableTimSort.sort(ComparableTimSort.java:146)
at java.util.Arrays.sort(Arrays.java:472)
at java.util.Collections.sort(Collections.java:155)
Here is my code:
System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
Collections.sort(docs, new Comparator<FeedDocument>() {
public int compare(FeedDocument o1, FeedDocument o2) {
int year1 = 0;
int year2 = 0;
int returnResult = 0;
if (o1.containsKey(FeedConstants.PUBLICATION_YEAR)
&& o2.containsKey(FeedConstants.PUBLICATION_YEAR)
&& o1.get(FeedConstants.PUBLICATION_YEAR) != null
&& (o1.get(FeedConstants.PUBLICATION_YEAR) instanceof String)
&& o2.get(FeedConstants.PUBLICATION_YEAR) != null
&& (o2.get(FeedConstants.PUBLICATION_YEAR) instanceof String)) {
String firstyear = (String) o1.get((FeedConstants.PUBLICATION_YEAR));
String secondyear = (String) o2.get((FeedConstants.PUBLICATION_YEAR));
if (firstyear.equals(secondyear)) {
return 0;
} else if (firstyear != null && !firstyear.isEmpty() && secondyear != null
&& !secondyear.isEmpty()) {
year1 = Integer.parseInt(firstyear.trim());
year2 = Integer.parseInt(secondyear.trim());
// int result = year2 - year1;
// if (result > 0) {
// returnResult = 1;
// } else if (result < 0) {
// returnResult = -1;
// }
return Double.compare(year2, year1);
}
} else {
returnResult = 0;
}
return returnResult;
}
});
Pretty sure I know what's happening here...
Suppse:
o1.get(FeedConstants.PUBLICATION_YEAR) != null
o2.get(FeedConstants.PUBLICATION_YEAR) == null
o3.get(FeedConstants.PUBLICATION_YEAR) != null
Then:
compare (o1, o2); //returns 0
compare (o2, o3); //returns 0
compare (o1, o3); //returns not 0
So you're claiming o1 == o2 == o3 but o1 != o3
Edward Peters' answer is correct in diagnosing your problem, as your compare method doesn't produce consistent (transitive) results.
The best way to resolve this is something like the following in your compare method:
if (o1.get(FeedConstants.PUBLICATION_YEAR) instanceof String) {
if (o2.get(FeedConstants.PUBLICATION_YEAR) instanceof String) {
// Perform the comparison here like you are
} else {
/*
* This could also be 1, the key is to have it consistent
* so the final sorted list clearly separates the FeedDocuments
* with a String PUBLICATION_YEAR and those without one.
*/
return -1;
}
} else if (o2.get(FeedConstants.PUBLICATION_YEAR) instanceof String) {
/*
* In this case, o1 doesn't have a String PUBLICATION_YEAR and o2
* does, so this needs to be the opposite of the return value
* 6 lines up to be consistent.
*/
return 1;
} else {
/*
* Consider all FeedDocuments without a String PUBLICATION_YEAR
* to be equivalent, otherwise you could do some other comparison
* on them here if you wanted.
*/
return 0;
}
The key is, if you only care about a subset of the list being sorted (the FeedDocuments with a String publication year, then you need to first separate them from the rest of the list that you don't care about being sorted (by returning either 1 or -1 when one of the FeedDocuments has a String publication year and the other doesn't). Then, you are free to sort the desired subset without inconsistent results.
Can this code to be simplifed using for?
if ((col[0] == null) && (col[1] == null) && (col[2] == null) && (col[3] == null) && (col[4] == null)){
//statement
}
You can use a Java 8 feature with Stream API:
boolean allNull = Arrays.stream(col).allMatch(Objects::isNull);
Use a boolean flag:
boolean areAllNull = true;
for (int i = 0; i < col.length; i ++) {
if (col[i] != null) {
areAllNull = false;
break;
}
}
if (areAllNull) {
//statement
}
If you want to limit only to certain positions in the array change col.length by a variable or constant marking the limit:
int numberOfPositions = 5;
for (int i = 0; i < numberOfPositions ; i ++)`
To check if X elements in an array are null, you cannot reduce the number of checks (X) unless you can short-circuit them. However, you can have a cleaner "if" statement if you package it in a method:
if (isAllNull(col, 0, 4)){
// do stuff
}
public boolean isAllNull(Object[] col, int start, int end){
for (int index=start;index<=end;index++){
if (col[index] !=null){
return false;
}
}
return true;
}
This will return false immediately when it finds one of the values not null.
I've been working on an algorithm to loop through one ArrayList containing a custom object. I'm now on hour 20 and I've gotten almost nowhere.
ArrayList<TicketItem> all = new ArrayList<>();
// ... 'all' gets filled here ... //
ArrayList<TicketItem> allCopy = new ArrayList<>(all);
for (int i = allCopy.size() - 1; i > 0; i--) {
TicketItem last = allCopy.get(i);
for (int j = 0; j < all.size(); j++) {
TicketItem compare = all.get(j);
if (last.getInt(TicketItem.TICKET_ITEM_ID) != compare.getInt(TicketItem.TICKET_ITEM_ID)) {
if (last.canBeGrouped(compare)) {
last.put(TicketItem.TICKET_ITEM_NUMBER, compare.getInteger(TicketItem.TICKET_ITEM_NUMBER));
allCopy.set(i, last);
break;
}
}
}
}
This works when it wants to and to be honest, it's probably really ugly. I just can't get my head around a better option.
The important method inside TicketItem is this one:
public boolean canBeGrouped(TicketItem other) {
if (other == null)
return false;
if (getBoolean(TicketItem.TICKET_ITEM_VOID))
return false;
if (other.getBoolean(TicketItem.TICKET_ITEM_VOID))
return false;
if (getInteger(TicketItem.MENU_ITEM) == null)
return false;
if (getInteger(TicketItem.MENU_ITEM).equals(other.getInteger(TicketItem.MENU_ITEM))
&& getBigDecimal(TicketItem.TICKET_ITEM_TOTAL).compareTo(
other.getBigDecimal(TicketItem.TICKET_ITEM_TOTAL)) == 0) {
ArrayList<TicketItemModifier> mThis = getModifiers();
ArrayList<TicketItemModifier> mOther = other.getModifiers();
if (mThis == null && mOther == null)
return true;
if (mThis != null && mOther != null) {
if (mThis.size() == mOther.size()) {
for (int i = 0; i < mThis.size(); i++) {
TicketItemModifier m1 = mThis.get(i);
TicketItemModifier m2 = mOther.get(i);
Integer m1MenuModifierId = m1.getInteger(TicketItemModifier.MENU_MODIFIER_ID);
Integer m2MenuModifierId = m2.getInteger(TicketItemModifier.MENU_MODIFIER_ID);
if (!(m1MenuModifierId != null && m2MenuModifierId != null && m1MenuModifierId
.equals(m2MenuModifierId))) {
return false;
}
}
return true;
}
}
}
return false;
}
Again, super ugly especially the for loop in there that works when it wants to. If need be I can modify hashCode and equals methods for both classes TicketItem and TicketItemModifier, however I would like to stay away from those two methods and do something along the lines of Comparable classes because just because they can be grouped does not mean they are equal.
What I want to do basically is go through one ArrayList filled with TicketItem objects and when two can be grouped I need to change the TicketItem object to match it.
I would suggest you create a new property or function like TickeItemCode which should be string concatenation of MENU_ITEM+ "-"+ TICKET_ITEM_TOTAL+ "-" + MENU_MODIFIER_IDs in modifiers list. you can filter the list to remove items where TICKET_ITEM_VOID is true and then sort by new property TickeItemCode and do grouping. This way you can reduce your time from n^2 to nlogn