Fixing a java.lang.UnsupportedOperationException - java

This method is supposed to find the smallest value in a Collection that is greater than the key. I keep getting a java.lang.UnsupportedOperationException, and I can't figure out how to fix it. Thanks in advance for any help.
public static <T> T ceiling(Collection<T> c, T key, Comparator<T> comp) {
T ceiling = null;
if (c == null || comp == null) {
throw new IllegalArgumentException();
}
else if (c.size() == 0) {
throw new NoSuchElementException();
}
else {
Iterator<T> itr = c.iterator();
while (itr.hasNext()) {
if (comp.compare(itr.next(), key) < 0) {
itr.remove();
}
}
}
if (c.size() == 0) {
throw new NoSuchElementException();
}
else {
Iterator<T> itr2 = c.iterator();
ceiling = itr2.next();
while (itr2.hasNext()) {
T temp2 = itr2.next();
if (comp.compare(temp2, ceiling) < 0) {
ceiling = temp2;
}
}
}
return ceiling;
}

Most likely you are trying to modify a collection which is unmodifiable.
I suggest you change the method to not modify the collection. (Also I suggest you read the stack trace to understand what it means)
Something like this
public static <T> T ceiling(Collection<T> c, T key, Comparator<T> comp) {
if (c == null || comp == null)
throw new NullPointerException();
T ret = null;
for (T t : c)
if (comp.compare(t, key)>=0 && (ret==null || comp.compare(t, ret)<0))
ret = t;
return ret;
}

Related

When does java TreeMap makes sorting?

Could anyone say when TeeMap makes sorting - on adding entries via put method or, for example, before we iterate the map? I tried to find in javadoc but with no luck.
It's done during the altering operations.
For exemple, here is the jdk8 implementation of the method put :
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
else {
if (key == null)
throw new NullPointerException();
#SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}

Why doesn't ArrayDeque implement List?

I currently want a data structure that acts like a Deque with indexing.
So, it should have O(1) addition and removal of elements at the front and back, as well as O(1) access of elements based on the index. It is not that hard to imagine a setup that would work for this.
It seems like ArrayDeque would be a natural choice for this. However, ArrayDeque does not implement List. Since the underlying data structure is an array, is there a good reason that it does not allow indexing?
Also, on a more practical note, does anyone know of any library doing the thing I want. Apache Commons does not have one as far as I can see.
(edit, original answer was mostly wrong)
There's no good reason why this class doesn't have an indexing. I checked the source code. It runs exactly like I suggested above. Some of the other items might be harder to add within the List interface. But, simple get wouldn't be one of them.
You take an array of a given size depending on your usage. Then set the start position halfway into it. Then you track a variable for the head and the tail. As you iterate items you move the head back and tail forward expanding outward. If you reach need a value outside the range you modulo that value so .length is equal to 0, and -1 is equal to (.length -1) and you keep adding values until ((tail - head) > .length) at which point you build another array copying all the pieces to be coherent usually about twice the size. Then to index it, you take head +index to get the real index.
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/ArrayDeque.java
Pretty sure the get is trivially akin to:
#Override
public E get(int index) {
int i = (head + index) & (elements.length - 1);
return elements[i];
}
Which seems like they should almost certainly have that. (See source code link for licensing info for the following).
import java.io.*;
import java.util.AbstractCollection;
import java.util.Arrays;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
public class ArrayDequeList<E> extends AbstractCollection<E> implements Deque<E>, Cloneable, Serializable, List<E>
{
private transient E[] elements;
private transient int head;
private transient int tail;
private static final int MIN_INITIAL_CAPACITY = 8;
private void allocateElements(int numElements) {
int initialCapacity = MIN_INITIAL_CAPACITY;
// Find the best power of two to hold elements.
// Tests "<=" because arrays aren't kept full.
if (numElements >= initialCapacity) {
initialCapacity = numElements;
initialCapacity |= (initialCapacity >>> 1);
initialCapacity |= (initialCapacity >>> 2);
initialCapacity |= (initialCapacity >>> 4);
initialCapacity |= (initialCapacity >>> 8);
initialCapacity |= (initialCapacity >>> 16);
initialCapacity++;
if (initialCapacity < 0) // Too many elements, must back off
initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements
}
elements = (E[]) new Object[initialCapacity];
}
#Override
public boolean addAll(int index, #NonNull Collection<? extends E> c) {
return false;
}
#Override
public E get(int index) {
int i = (head + index) & (elements.length - 1);
return elements[i];
}
#Override
public E set(int index, E element) {
int i = (head + index) & (elements.length - 1);
E old = elements[i];
elements[i] = element;
return old;
}
#Override
public void add(int index, E element) {
throw new IllegalStateException("This one is hard to do.");
}
#Override
public E remove(int index) {
throw new IllegalStateException("This one is hard to do.");
}
#Override
public int indexOf(Object o) {
throw new IllegalStateException("This one's not that hard but pass..");
}
#Override
public int lastIndexOf(Object o) {
throw new IllegalStateException("This one's not that hard but pass..");
}
#Override
public ListIterator<E> listIterator() {
throw new IllegalStateException("Needs to write a new iterator..");
}
#NonNull
#Override
public ListIterator<E> listIterator(int index) {
throw new IllegalStateException("Needs to write a new iterator..");
}
#NonNull
#Override
public List<E> subList(int fromIndex, int toIndex) {
throw new IllegalStateException("Hm, not sure how this would work.");
}
private void doubleCapacity() {
assert head == tail;
int p = head;
int n = elements.length;
int r = n - p; // number of elements to the right of p
int newCapacity = n << 1;
if (newCapacity < 0)
throw new IllegalStateException("Sorry, deque too big");
Object[] a = new Object[newCapacity];
System.arraycopy(elements, p, a, 0, r);
System.arraycopy(elements, 0, a, r, p);
elements = (E[])a;
head = 0;
tail = n;
}
private <T> T[] copyElements(T[] a) {
if (head < tail) {
System.arraycopy(elements, head, a, 0, size());
} else if (head > tail) {
int headPortionLen = elements.length - head;
System.arraycopy(elements, head, a, 0, headPortionLen);
System.arraycopy(elements, 0, a, headPortionLen, tail);
}
return a;
}
public ArrayDequeList() {
elements = (E[]) new Object[16];
}
public ArrayDequeList(int numElements) {
allocateElements(numElements);
}
public ArrayDequeList(Collection<? extends E> c) {
allocateElements(c.size());
addAll(c);
}
public void addFirst(E e) {
if (e == null)
throw new NullPointerException();
elements[head = (head - 1) & (elements.length - 1)] = e;
if (head == tail)
doubleCapacity();
}
public void addLast(E e) {
if (e == null)
throw new NullPointerException();
elements[tail] = e;
if ( (tail = (tail + 1) & (elements.length - 1)) == head)
doubleCapacity();
}
public boolean offerFirst(E e) {
addFirst(e);
return true;
}
public boolean offerLast(E e) {
addLast(e);
return true;
}
public E removeFirst() {
E x = pollFirst();
if (x == null)
throw new NoSuchElementException();
return x;
}
public E removeLast() {
E x = pollLast();
if (x == null)
throw new NoSuchElementException();
return x;
}
public E pollFirst() {
int h = head;
E result = elements[h]; // Element is null if deque empty
if (result == null)
return null;
elements[h] = null; // Must null out slot
head = (h + 1) & (elements.length - 1);
return result;
}
public E pollLast() {
int t = (tail - 1) & (elements.length - 1);
E result = elements[t];
if (result == null)
return null;
elements[t] = null;
tail = t;
return result;
}
public E getFirst() {
E x = elements[head];
if (x == null)
throw new NoSuchElementException();
return x;
}
public E getLast() {
E x = elements[(tail - 1) & (elements.length - 1)];
if (x == null)
throw new NoSuchElementException();
return x;
}
public E peekFirst() {
return elements[head]; // elements[head] is null if deque empty
}
public E peekLast() {
return elements[(tail - 1) & (elements.length - 1)];
}
public boolean removeFirstOccurrence(Object o) {
if (o == null)
return false;
int mask = elements.length - 1;
int i = head;
E x;
while ( (x = elements[i]) != null) {
if (o.equals(x)) {
delete(i);
return true;
}
i = (i + 1) & mask;
}
return false;
}
public boolean removeLastOccurrence(Object o) {
if (o == null)
return false;
int mask = elements.length - 1;
int i = (tail - 1) & mask;
E x;
while ( (x = elements[i]) != null) {
if (o.equals(x)) {
delete(i);
return true;
}
i = (i - 1) & mask;
}
return false;
}
public boolean add(E e) {
addLast(e);
return true;
}
public boolean offer(E e) {
return offerLast(e);
}
public E remove() {
return removeFirst();
}
public E poll() {
return pollFirst();
}
public E element() {
return getFirst();
}
public E peek() {
return peekFirst();
}
public void push(E e) {
addFirst(e);
}
public E pop() {
return removeFirst();
}
private void checkInvariants() {
assert elements[tail] == null;
assert head == tail ? elements[head] == null :
(elements[head] != null &&
elements[(tail - 1) & (elements.length - 1)] != null);
assert elements[(head - 1) & (elements.length - 1)] == null;
}
private boolean delete(int i) {
checkInvariants();
final E[] elements = this.elements;
final int mask = elements.length - 1;
final int h = head;
final int t = tail;
final int front = (i - h) & mask;
final int back = (t - i) & mask;
// Invariant: head <= i < tail mod circularity
if (front >= ((t - h) & mask))
throw new ConcurrentModificationException();
// Optimize for least element motion
if (front < back) {
if (h <= i) {
System.arraycopy(elements, h, elements, h + 1, front);
} else { // Wrap around
System.arraycopy(elements, 0, elements, 1, i);
elements[0] = elements[mask];
System.arraycopy(elements, h, elements, h + 1, mask - h);
}
elements[h] = null;
head = (h + 1) & mask;
return false;
} else {
if (i < t) { // Copy the null tail as well
System.arraycopy(elements, i + 1, elements, i, back);
tail = t - 1;
} else { // Wrap around
System.arraycopy(elements, i + 1, elements, i, mask - i);
elements[mask] = elements[0];
System.arraycopy(elements, 1, elements, 0, t);
tail = (t - 1) & mask;
}
return true;
}
}
public int size() {
return (tail - head) & (elements.length - 1);
}
public boolean isEmpty() {
return head == tail;
}
public Iterator<E> iterator() {
return new DeqIterator();
}
public Iterator<E> descendingIterator() {
return new DescendingIterator();
}
private class DeqIterator implements Iterator<E> {
private int cursor = head;
private int fence = tail;
private int lastRet = -1;
public boolean hasNext() {
return cursor != fence;
}
public E next() {
if (cursor == fence)
throw new NoSuchElementException();
E result = elements[cursor];
// This check doesn't catch all possible comodifications,
// but does catch the ones that corrupt traversal
if (tail != fence || result == null)
throw new ConcurrentModificationException();
lastRet = cursor;
cursor = (cursor + 1) & (elements.length - 1);
return result;
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
if (delete(lastRet)) { // if left-shifted, undo increment in next()
cursor = (cursor - 1) & (elements.length - 1);
fence = tail;
}
lastRet = -1;
}
}
private class DescendingIterator implements Iterator<E> {
private int cursor = tail;
private int fence = head;
private int lastRet = -1;
public boolean hasNext() {
return cursor != fence;
}
public E next() {
if (cursor == fence)
throw new NoSuchElementException();
cursor = (cursor - 1) & (elements.length - 1);
E result = elements[cursor];
if (head != fence || result == null)
throw new ConcurrentModificationException();
lastRet = cursor;
return result;
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
if (!delete(lastRet)) {
cursor = (cursor + 1) & (elements.length - 1);
fence = head;
}
lastRet = -1;
}
}
public boolean contains(Object o) {
if (o == null)
return false;
int mask = elements.length - 1;
int i = head;
E x;
while ( (x = elements[i]) != null) {
if (o.equals(x))
return true;
i = (i + 1) & mask;
}
return false;
}
public boolean remove(Object o) {
return removeFirstOccurrence(o);
}
public void clear() {
int h = head;
int t = tail;
if (h != t) { // clear all cells
head = tail = 0;
int i = h;
int mask = elements.length - 1;
do {
elements[i] = null;
i = (i + 1) & mask;
} while (i != t);
}
}
public Object[] toArray() {
return copyElements(new Object[size()]);
}
public <T> T[] toArray(T[] a) {
int size = size();
if (a.length < size)
a = (T[])java.lang.reflect.Array.newInstance(
a.getClass().getComponentType(), size);
copyElements(a);
if (a.length > size)
a[size] = null;
return a;
}
public ArrayDequeList<E> clone() {
try {
ArrayDequeList<E> result = (ArrayDequeList<E>) super.clone();
result.elements = Arrays.copyOf(elements, elements.length);
return result;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
private static final long serialVersionUID = 2340785798034038923L;
private void writeObject(ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
// Write out size
s.writeInt(size());
// Write out elements in order.
int mask = elements.length - 1;
for (int i = head; i != tail; i = (i + 1) & mask)
s.writeObject(elements[i]);
}
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject();
// Read in size and allocate array
int size = s.readInt();
allocateElements(size);
head = 0;
tail = size;
// Read in all elements in the proper order.
for (int i = 0; i < size; i++)
elements[i] = (E)s.readObject();
}
}

java.lang.ClassCastException when Implementation of HashMap in Java

When I use MapEntry[] entry = (MapEntry[]) new Object[capacity],
it tells me java.lang.ClassCastException.
How can this happen? I am confused about this. (Since it seems I should do the casting because it's generic)
I found some tutorial and they were using like this:
table = new Entry[capacity];
(http://www.javamadesoeasy.com/2015/02/hashmap-custom-implementation.html)
it did not even do the casting.
My codes are below.
public class MyHashMap<K, V> {
private class MapEntry {
K key;
V value;
MapEntry next;
MapEntry(K key, V value) {
this.key = key;
this.value = value;
}
}
private int size = 0;
private int capacity;
MapEntry[] entry;
#SuppressWarnings("unchecked")
MyHashMap() {
capacity = 10;
entry = (MapEntry[]) new Object[capacity];
}
#SuppressWarnings("unchecked")
MyHashMap(int capacity) {
entry = (MapEntry[]) new Object[capacity];
}
public void put(K key, V value) {
int hash = hashCode(key);
MapEntry newNode = new MapEntry(key, value);
if (entry[hash % capacity] == null) {
entry[hash % capacity] = newNode;
} else {
if (key == entry[hash % capacity].key) {
entry[hash % capacity].value = value;
} else {
MapEntry nextNode = entry[hash % capacity].next;
while (nextNode != null) {
if (key == nextNode.key) {
nextNode.value = value;
return;
}
nextNode = nextNode.next;
}
nextNode = newNode;
}
}
}
public V get(K key) {
int hash = hashCode(key);
MapEntry node = entry[hash % capacity];
if (node == null) {
return null;
}
if (node.key == key) {
return node.value;
}
while (key != node.key) {
node = node.next;
if (node.key == key) {
return node.value;
}
}
return null;
}
public boolean contains(K key) {
return get(key) != null;
}
public int size() {
return size;
}
public void remove(K key) {
int hash = hashCode(key);
MapEntry node = entry[hash % capacity];
if (node == null) return;
if (key == node.key) {
entry[hash % capacity] = node.next;
}
MapEntry pre = node;
while (key != node.key) {
node = node.next;
if (key == node.key) {
pre.next = node.next;
return;
}
pre = pre.next;
}
}
private int hashCode(K key) {
return Math.abs(key.hashCode());
}
public void display(){
for(int i = 0; i < capacity; i++){
if(entry[i] != null){
MapEntry node = entry[i];
while(node != null){
System.out.print("{" + node.key + "=" + node.value + "}" + " ");
node = node.next;
}
}
}
}
public static void main(String[] args) {
MyHashMap<Integer, Integer> hashMapCustom = new MyHashMap<Integer, Integer>();
hashMapCustom.put(21, 12);
hashMapCustom.put(25, 121);
hashMapCustom.put(30, 151);
hashMapCustom.put(33, 15);
hashMapCustom.put(35, 89);
System.out.println("value corresponding to key 21="
+ hashMapCustom.get(21));
System.out.println("value corresponding to key 51="
+ hashMapCustom.get(51));
System.out.print("Displaying : ");
hashMapCustom.display();
System.out.print("Displaying : ");
hashMapCustom.display();
}
}
You can't convert a class of an array by just casting that's yhe reason you get ClassCastException. You should use
`Arrays.copyof ().`
CustomType[]ca=Arrays.copyOf(array,array.length,CustomType[].class);
I have figured out how this work.
(Creation of array whose component type is either a type parameter, a concrete parameterized type or a bounded wildcard parameterized type, is type-unsafe.)
entry = (MapEntry[]) Array.newInstance(MapEntry.class, capacity);
In this way, there can be no errors.
There is another question with good solution.
How to create a generic array in Java?

Treemap get throws NullPointerException

Follwing is my java class TestEntry.java
private void initializemapTest()
{
eventMap = new TreeMap<String,String>();
//Put some value into eventMap
mapTest = new TreeMap<String, String>( new Comparator<String>()
{
public int compare( String key1, String key2 )
{
if( key1 == null )
{
if( key2 == null )
{
return 0;
}
else
{
return 1;
}
}
else
{
if( key2 == null )
{
return -1;
}
else
{
return key1.compareTo( key2 );
}
}
}
} );
for( String s : eventMap.keySet() )
{
mapTest.put( eventMap.get( s ), s ); //Error at this line
}
}
As per my knowledge eventMap doesnot allow null values, hence keyset of eventMap does not have any null values,
if value of any key in eventMap is null, while i try to put it in mapTest, it shoukd not throw any null pointer exception, because its respective comparator allows null values
But why am i getting this exception
java.lang.NullPointerException
at java.util.TreeMap.cmp(TreeMap.java:1911)
at java.util.TreeMap.get(TreeMap.java:1835)
at kidiho.sa.client.reports.ReportEntry.initializemapTest(TestEntry.java:22)
It will throw NullPointerException because in TreeMap api get() method is throwing NullPointerException deliberately if that is null.
final Entry<K,V> getEntry(Object key) {
// Offload comparator-based version for sake of performance
if (comparator != null)
return getEntryUsingComparator(key);
if (key == null)
throw new NullPointerException();
Comparable<? super K> k = (Comparable<? super K>) key;
Entry<K,V> p = root;
while (p != null) {
int cmp = k.compareTo(p.key);
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
return null;
}
From TreeMap:
final Entry<K,V> getEntry(Object key) {
// Offload comparator-based version for sake of performance
if (comparator != null)
return getEntryUsingComparator(key);
if (key == null)
throw new NullPointerException();
Comparable<? super K> k = (Comparable<? super K>) key;
Entry<K,V> p = root;
while (p != null) {
int cmp = k.compareTo(p.key);
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
return null;
}
That is: TreeMap doesn't allow null keys, so you cannot do:
tm.put(null, something)
And subsequently, you cannot do
tm.get(null)
As according to the TreeMap behaviour, those operations actually don't make sense
As other said, you can't use a null value as a TreeMap key, it will throw a NullPointerException.
You're not getting the NullPointerException from the same place probably because your first map has a registered comparator and the second has none.

Why does my compare methd throw IllegalArgumentException sometimes?

I am having this problem for some time, have searched lots of StackOverflow questions but couldn't solve my problem.
I also asked a similar question before and got the suggestion to use,
System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
It didn't solve my problem. I never got this exception on any of my test devices, but some of my users have been reporting it regularly. I am really clueless how to solve it.
The Exception
This is the exception that I am getting,
java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.TimSort.mergeLo(TimSort.java:743)
at java.util.TimSort.mergeAt(TimSort.java:479)
at java.util.TimSort.mergeCollapse(TimSort.java:404)
at java.util.TimSort.sort(TimSort.java:210)
at java.util.TimSort.sort(TimSort.java:169)
at java.util.Arrays.sort(Arrays.java:2023)
at java.util.Collections.sort(Collections.java:1883)
or sometimes this,
java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.TimSort.mergeHi(TimSort.java:864)
at java.util.TimSort.mergeAt(TimSort.java:481)
at java.util.TimSort.mergeCollapse(TimSort.java:406)
at java.util.TimSort.sort(TimSort.java:210)
at java.util.TimSort.sort(TimSort.java:169)
at java.util.Arrays.sort(Arrays.java:2010)
at java.util.Collections.sort(Collections.java:1883)
What I Have Done
enum FileItemComparator implements Comparator<FileItem> {
//Using ENUM
NAME_SORT {
public int compare(FileItem o1, FileItem o2) {
int result = 0;
if (o1 != null && o2 != null) {
String n1 = o1.getFileName();
String n2 = o2.getFileName();
if (n1 != null && n2 != null)
result = n1.compareTo(n2);
}
return result;
}
},
DATE_SORT {
public int compare(FileItem o1, FileItem o2) {
int result = 0;
if (o1 != null && o2 != null) {
String d1 = o1.getFileDate();
String d2 = o2.getFileDate();
if (d1 != null && d2 != null) {
Long l1 = Long.valueOf(d1);
Long l2 = Long.valueOf(d2);
if (l1 != null && l2 != null) {
result = l1.compareTo(l2);
}
}
}
return result;
}
},
SIZE_SORT {
public int compare(FileItem o1, FileItem o2) {
int result = 0;
if (o1 != null && o2 != null) {
File f1 = o1.getItem();
File f2 = o2.getItem();
if (f1 != null && f2 != null) {
result = Long.valueOf(f1.length()).compareTo(Long.valueOf(f2.length()));
}
}
return result;
}
};
public static Comparator<FileItem> descending(final Comparator<FileItem> other) {
return new Comparator<FileItem>() {
public int compare(FileItem o1, FileItem o2) {
return -1 * other.compare(o1, o2);
}
};
}
public static Comparator<FileItem> getComparator(final FileItemComparator... multipleOptions) {
return new Comparator<FileItem>() {
public int compare(FileItem o1, FileItem o2) {
for (FileItemComparator option : multipleOptions) {
int result = option.compare(o1, o2);
if (result != 0) {
return result;
}
}
return 0;
}
};
}
}
This is how I am sorting,
Collections.sort(dirs, FileItemComparator.getComparator(FileItemComparator.NAME_SORT));
The Problem
I am sure there is something wrong in the compare method with transitive dependencies. I have tried a lot and can't seem to fix it. Actually, I never got this problem in any of my test devices, but my users are reporting it constantly.
I hope anyone here will be able to catch the problem and help me solve it once and for all.
Updated Code (Thanks to #Eran)
I thought it would be best to help others by posting the complete updated code. It will help a lot of people facing the same problem.
enum FileItemComparator implements Comparator<FileItem> {
//Using ENUM
NAME_SORT {
public int compare(FileItem o1, FileItem o2) {
if (o1 == null) {
if (o2 == null) {
return 0;
} else {
return 1; // this will put null in the end
}
} else if (o2 == null) {
return -1;
}
String n1 = o1.getFileName();
String n2 = o2.getFileName();
if (n1 == null) {
if (n2 == null) {
return 0;
} else {
return 1; // this will put null names after non null names
}
} else if (n2 == null) {
return -1;
}
return n1.compareTo(n2);
}
},
DATE_SORT {
public int compare(FileItem o1, FileItem o2) {
if (o1 == null) {
if (o2 == null) {
return 0;
} else {
return 1; // this will put null in the end
}
} else if (o2 == null) {
return -1;
}
String d1 = o1.getFileDate();
String d2 = o2.getFileDate();
if (d1 == null) {
if (d2 == null) {
return 0;
} else {
return 1; // this will put null names after non null names
}
} else if (d2 == null) {
return -1;
}
Long l1 = Long.valueOf(d1);
Long l2 = Long.valueOf(d2);
if (l1 == null) {
if (l2 == null) {
return 0;
} else {
return 1; // this will put null names after non null names
}
} else if (l2 == null) {
return -1;
}
return l1.compareTo(l2);
}
},
SIZE_SORT {
public int compare(FileItem o1, FileItem o2) {
if (o1 == null) {
if (o2 == null) {
return 0;
} else {
return 1; // this will put null in the end
}
} else if (o2 == null) {
return -1;
}
File f1 = o1.getItem();
File f2 = o2.getItem();
if (f1 == null) {
if (f2 == null) {
return 0;
} else {
return 1; // this will put null in the end
}
} else if (f2 == null) {
return -1;
}
Long l1 = Long.valueOf(f1.length());
Long l2 = Long.valueOf(f2.length());
if (l1 == null) {
if (l2 == null) {
return 0;
} else {
return 1; // this will put null names after non null names
}
} else if (l2 == null) {
return -1;
}
return l1.compareTo(l2);
}
};
public static Comparator<FileItem> descending(final Comparator<FileItem> other) {
return new Comparator<FileItem>() {
public int compare(FileItem o1, FileItem o2) {
return -1 * other.compare(o1, o2);
}
};
}
public static Comparator<FileItem> getComparator(final FileItemComparator... multipleOptions) {
return new Comparator<FileItem>() {
public int compare(FileItem o1, FileItem o2) {
for (FileItemComparator option : multipleOptions) {
int result = option.compare(o1, o2);
if (result != 0) {
return result;
}
}
return 0;
}
};
}
}
Let's look at your first compare method :
public int compare(FileItem o1, FileItem o2) {
int result = 0;
if (o1 != null && o2 != null) {
String n1 = o1.getFileName();
String n2 = o2.getFileName();
if (n1 != null && n2 != null)
result = n1.compareTo(n2);
}
return result;
}
Suppose you are comparing two FileItems (let's call them o1 and o2), one with a file name and the other without (i.e. null file name). Your method will return 0.
Now if you compare o2 with another FileItem (o3) for which the file name is not null, you return 0 again.
But if you compare o1 to o3, since both of them have non null file name, the comparison returns -1 or 1 (assuming the file names are different).
Therefore your comparison is inconsistent since it's not transitive.
If one element lacks a property required for the comparison and the other doesn't, you shouldn't return 0. You should decide whether to return 1 or -1 (depending whether, for example, the FileItems with null names should be ordered before or after the FileItems with non null names).
For example :
public int compare(FileItem o1, FileItem o2)
{
if (o1 == null) {
if (o2 == null) {
return 0;
} else {
return 1; // this will put null in the end
}
} else if (o2 == null) {
return -1;
}
String n1 = o1.getFileName();
String n2 = o2.getFileName();
if (n1 == null) {
if (n2 == null) {
return 0;
} else {
return 1; // this will put null names after non null names
}
} else if (n2 == null) {
return -1;
}
return n1.compareTo(n2);
}
This is a common mistake with comparators - you are not handling null consistently. The usual pattern would look like this:
public int compare(FileItem o1, FileItem o2) {
// null == null
if (o1 == null && o2 == null) {
return 0;
}
// null < not null
if (o1 == null || o2 == null) {
return -1;
}
// Neither can be null now so this is safe.
String n1 = o1.getFileName();
String n2 = o2.getFileName();
// Same logic again.
if (n1 == null && n2 == null) {
return 0;
}
if (n1 == null || n2 == null) {
return -1;
}
return n1.compareTo(n2);
}
Added
Note that this implementation also a common mistake as I am allowing compare(null,not_null) to equal compare(not_null,null) which also violates the contract - please use #Eran's solution or something like this.
public int compare(FileItem o1, FileItem o2) {
// null == null
if (o1 == null && o2 == null) {
return 0;
}
// null != not null
if (o1 == null || o2 == null) {
// Swap these around if you want 'null' at the other end.
return o1 == null ? -1: 1;
}
// Neither can be null now so this is safe.
String n1 = o1.getFileName();
String n2 = o2.getFileName();
// Same logic again.
if (n1 == null && n2 == null) {
return 0;
}
if (n1 == null || n2 == null) {
// Swap these around if you want 'null' at the other end.
return n1 == null ? -1: 1;
}
return n1.compareTo(n2);
}

Categories