I have a string arraylist with some null values and some strings. I don't want to sort the arraylist but I should sort the arraylist such that null values comes last. Lets say arraylist is {1,2,null,6,5,null, 3}, I should get null values last {1,2,6,5,3,null,null}.
Solution , I currently have:
Right now, I am constructing new arraylist and If the value is null, I am not pushing it to new list otherwise I am adding it to new arraylist.
Any other better solution?
Thanks for the help.
If you are using Java 8, you can easily build the comparator you need:
Arrays.sort(stringArray, Comparator.nullsLast(Comparator.naturalOrder()));
But if you not using java 8 you can have a comparator like below
public class StringNullComparator implements Comparator<String> {
public int compare(String stringOne, String stringTwo) {
if (stringOne != null && stringTwo != null)
return stringOne.compareTo(stringTwo);
return (stringOne == stringTwo)?0:(stringOne==null? 1 : -1);
}
}
And you can use at stated below
Arrays.sort(stringArray, new StringNullComparator());
Custom Comparator to pass to sort:
public class StringComparator implements Comparator<String> {
public int compare(String s1, String s2) {
if (s1 != null && s2 != null)
return s1.compareTo(s2);
return (s1 == null) ? 1 : -1;
}
}
then:
Collectios.sort(list, new StringComparator());
If you want to avoid explicitly iterating over the whole list you could use ArrayList.indexOf() to find the null values, then remove() them. If you want to keep the values in the list you can then just add a null value to the end of the list. However I would imagine this approach is not great in terms of performance if this is a concern.
You can use NullComparator from apache.
Collections.sort(list, new NullComparator());
what about constructing new arraylist and If it a real value add it to the new list and if it is a null increment a counter.At last add the number of null equal to counter value.
If you want to sort null to the end and keep the order for the non-null elements this Comparator would do that :
class CompareStrings implements Comparator<String> {
#Override
public int compare(String o1, String o2) {
if (o1 == null && o2 != null)
return 1;
if (o2 == null && o1 != null)
return -1;
return 0;
}
}
If both String are null or non-null they will compare equal. If only one is null it will compare as smaller than the non-null one.
How about:
class MyInteger implements Comparator<Integer> {
public int compare(Integer arg0, Integer arg1) {
if(arg1 == null) {
return -1;
}
return 0;
}
}
And we can use it like:
List<Integer> al = new ArrayList<Integer>();
al.add(1);
al.add(2);
al.add(null);
al.add(6);
al.add(5);
al.add(null);
al.add(3);
Collections.sort(al, new MyInteger());
All other solutions involve sorting. As you mentioned, you don't really need sorting. In case time complexity is a concern, you can use the following linear time solution (in-place):
public static <T> void nullsToEndInPlace(List<T> l) {
int i = 0;
int j = l.size() - 1;
while (i < j) {
T left = l.get(i);
T right = l.get(j);
if (left != null) {
i++;
} else if (right == null) {
j--;
} else {
l.set(i, right);
l.set(j, null);
i++;
j--;
}
}
}
Try this.
List<String> list = new ArrayList<>();
list.add("BR64");
list.add("SWG620");
list.add("");
list.add("sw0");
list.add("R124");
list.add("R219");
list.add("TaGh20");
list.add("SW6505");
list.add("");
list.add(null);
list.add("SW_6505");
list.add("swd_157");
list.add("localhost");
list.add("qaGh20_241");
list.add("gen");
list.add(null);
list.add("taGh20");
list.add("zen");
list.add("QWG");
list.add("SWG62_");
list.add("SWG620");
Collections.sort(list, new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
if (o1 != null && o2 != null && o1.length() > 0 && o2.length() > 0) {
return (Character.toLowerCase(o1.charAt(0)) == Character.toLowerCase(o2.charAt(0)))
? o1.compareTo(o2)
: (Character.toLowerCase(o1.charAt(0)) + o1.substring(1))
.compareTo((Character.toLowerCase(o2.charAt(0)) + o2.substring(1)));
} else {
return (o1 == o2) ? 0 : ((o1 == null || o1 == "") ? 1 : -1);
}
}
});
System.out.println(list);
Output-:[BR64, gen, localhost, QWG, qaGh20_241, R124, R219, SW6505, SWG620, SWG620, SWG62_, SW_6505, sw0, swd_157, TaGh20, taGh20, zen, , , null, null]
Both list.sort() and sorted() have a key parameter to specify a function to be called on each list element prior to making comparisons.
For example, here’s a case-insensitive string comparison:
sorted("This is a test string from sohan".split(), key=str.lower)
['a', 'from', 'is', 'sohan', 'string', 'test', 'This']
here, key=str.lower() will convert every string to lower case and then sort the result.
For more info about sorting click here
Related
I have a <display:table> with potential null values in the columns, and I'd like to configure the sorting so that when a given <display:column> is sorted alphabetically ("A" strings at top), the null values are at the bottom of the table (after "Z" strings).
To do this, I've written a Comparator implementation that should put null values at the end of the list:
public class DefaultComparatorNullsLast implements Comparator<Object>{
private final Collator collator;
public DefaultComparatorNullsLast(){
this(Collator.getInstance());
} // DefaultComparatorNullsLast
public DefaultComparatorNullsLast(Collator collatorToUse){
this.collator = collatorToUse;
this.collator.setStrength(Collator.PRIMARY);
} // DefaultComparatorNullsLast
public int compare(final Object obj0, final Object obj1){
//similar to NullComparator.compare()
if(obj0 == obj1) return 0;
else if(obj0 == null) return 1;
else if(obj1 == null) return -1;
//similar to DefaultComparator.compare()
if(obj0 instanceof String && obj1 instanceof String) return this.collator.compare(obj0, obj1);
else if(obj0 instanceof Comparable && obj1 instanceof Comparable) return ((Comparable<Object>) obj0).compareTo(obj1);
else return this.collator.compare(obj0.toString(), obj1.toString());
} // compare
} // DefaultComparatorNullsLast
Strangely, though, the null values are still displaying at the top of the table. When I debug, I see that my DefaultComparatorNullsLast.compare() method is never called on any null values. Digging into the displaytag code, I can see that TableModel.sortRowList() actually places my Comparator implementation into a RowSorter, which is then passed to Collections.sort():
private void sortRowList(List list)
{
if (isSorted())
{
HeaderCell sortedHeaderCell = getSortedColumnHeader();
if (sortedHeaderCell != null)
{
// If it is an explicit value, then sort by that, otherwise sort by the property...
if (sortedHeaderCell.getBeanPropertyName() != null
|| (this.sortedColumn != -1 && this.sortedColumn < this.headerCellList.size()))
{
String sorted = (sortedHeaderCell.getSortProperty() != null)
? sortedHeaderCell.getSortProperty()
: sortedHeaderCell.getBeanPropertyName();
Collections.sort(list, new RowSorter(
this.sortedColumn,
sorted,
getTableDecorator(),
this.sortOrderAscending,
sortedHeaderCell.getComparator()));
}
}
}
}
And when I look into RowSorter, it seems the checkNullsAndCompare() method actually handles null values opposite how I want them to be handled and only calls my DefaultComparatorNullsLast.compare() method if both values are non-null:
private int checkNullsAndCompare(Object object1, Object object2)
{
int returnValue;
if (object1 == null && object2 != null)
{
returnValue = -1;
}
else if (object1 != null && object2 == null)
{
returnValue = 1;
}
else if (object1 == null && object2 == null)
{
// both null
returnValue = 0;
}
else
{
returnValue = comparator.compare(object1, object2);
}
int ascendingInt = this.ascending ? 1 : -1;
return ascendingInt * returnValue;
}
Essentially, I'm wondering if there's any way around this. Of course, I know I can sort the list in my action code before the JSP renders and use sort="external", but I also want this special sorting of null values to take place when the user clicks a column header to sort by that column's property. Is there any way to get the displaytag library to call my custom Comparator implementation instead of RowSorter.checkNullsAndCompare()?
I have a list of objects. The Object looks similar to this one:
class Data {
...
private X somethig;
private Y somethigElse;
public boolean customEquals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Data)) {
return false;
}
Data other = (Data) obj;
if (something == null) {
if (other.something != null) {
return false;
}
} else if (!something.equals(other.something)) {
return false;
}
if (somethigElse == null) {
if (other.somethigElse != null) {
return false;
}
} else if (!somethigElse.equals(other.somethigElse)) {
return false;
}
return true;
}
public boolean equals(Object obj) {
...
}
public int hashCode() {
...
}
getters/setters
}
I need to filter the list to get distinct objects from it.
Note the equals and hashCode methods are implemented (they use another fields) and I can't use equals for this task. So the equality is not defined by equals but by 'something' and 'somethigElse' properties. How can I do that?
I have tried:
final Comparator<Data> comparator = new Comparator<Data>() {
#Override
public int compare(Data o1, Data o2) {
return o1.customEquals(o2) ? 0 : 1;
}
};
Set<Data> set = new TreeSet<Data>(comparator);
set.addAll(list);
System.out.println(set);
But the set still contains some of the objects several times.
That's because you are not providing a proper ordering function.
TreeSet sorting algorithm makes a few assumptions, like:
compare(a, b) > 0 => compare(b, a) < 0
compare(a, b) > 0 && compare(b, c) > 0 => compare(a, c) > 0
and so on.
Implement a proper comparison, not just an 'equals' and it should work.
final Comparator<Data> comparator = new Comparator<Data>() {
#Override
public int compare(Data o1, Data o2) {
int k = o1.getSomething().compareTo(o2.getSomething();
if (k != 0) {
return k;
}
return o1.getSomethingElse() - o2.getSomethingElse();
}
};
That is because TreeSet is a SortedSet, and you are telling that every new element that arrives is greater than others but itself.
Let's make a simplification to have a shorter example of what's going on, suppose that we are using just numbers with your comparator and the storage is an array (and binary search) rather than a tree because it's easier to represent.
We got number 1, it's the only element, so the array is [1] now.
We get a 0 now, but as you are telling the set that it's bigger, we get [1, 0].
Let's add a 3 now for example, we'll get [1, 0, 3].
Add another 1, the bisection will try to compare it with the middle element 0 and see that it's greater, go to the other half, compare with the 3 and it's greater again, so we get [1, 0, 3, 1].
If we add another 1, now it will see that the element is already there and won't add it, but if you add any other number and it happens that its repetitions are not in any of the bisection points, it will always end up at the end of the array.
Probably you need to fix your compare():
public int compare(Data o1, Data o2)
{
int i = o1.getSomething().compareTo(o2.getSomething());
if (i != 0) return i;
return o1.getSomethingElse()- o2.getSomethingElse();
}
I have a need to place null objects at the end of the List. Here is a sample what I have done for this purpose:
public static void main(String... args) {
List<String> strings = Arrays.asList(new String[]{"A", null, "B"});
for(String string : strings) {
System.out.println(string);
}
System.out.println("==================");
List<String> result = new ArrayList<String>();
List<String> nullStrings = new ArrayList<String>();
for(String string : strings) {
if(string != null) {
result.add(string);
} else {
nullStrings.add(string);
}
}
result.addAll(nullStrings);
for(String string : result) {
System.out.println(string);
}
}
I am looking forward to a more efficient and intelligent way to do this. Is it possible to swap inside the actual list so that null node get placed at the end, so that I don't need the other two list(nullStrings, result) and extra iteration.
Update
Sorting will not work for my case. This sample code I made just for testing purpose. Actually I have a different type of Object. Also Sorting will break the position.
Say I have this null, Obj2, Obj1, if do anything like sorting it may happens Obj1, Obj2, null. But I need Obj2, Obj1, null.
You can sort it using Collections.sort and a custom Comparator.
Here the compare code:
#Override
public int compare(String left, String right) {
if (left == right) {
return 0;
}
if (left == null) {
return 1;
}
if (right == null) {
return -1;
}
return 0;
}
Note that elements that have an equal value according to this Comparator won't be re-ordered. From Collections.sort:
This sort is guaranteed to be stable: equal elements will not be reordered as a result of the sort.
Just move non-null elements to the front and fill the rest of the list with null.
int j = 0;
for (int i = 0; i < strings.size(); i++)
if (strings.get(i) != null){
strings.set(j, strings.get(i));
j++;
}
for (; j < strings.size(); j++)
strings.set(j, null);
If you are using LinkedList (or something not a RandomAccess), you'll need ListIterator
ListIterator<String> j = strings.listIterator();
for (ListIterator<String> i = strings.listIterator(); i.hasNext();) {
String s = i.next();
if (s != null) {
j.next();
j.set(s);
}
}
while (j.hasNext()) {
j.next();
j.set(null);
}
The general strategy looks fine to me. I would bring the following changes:
initialize the result list with the appropriate size
don't use a separate list for nulls. At the end of the iteration, you just need to compare the length of the initial list with the length of the result list to know how many nulls you need to add.
Although making a copy of the list will use more memory, it could well be faster than changing the initial list, because removing elements needs to move all the subsequent elements each time. And sorting is N*log(N), whereas copying elements is O(N).
[EDIT owlstead]
public static List<String> moveNullsToEnd(final List<String> strings) {
final List<String> newStrings = new ArrayList<String>(strings.size());
for (String string : strings) {
if (string != null) {
newStrings.add(string);
}
}
for (int i = 0, remaining = strings.size() - newStrings.size(); i < remaining; i++) {
newStrings.add(null);
}
return newStrings;
}
Below code you can use
Remove all the null elements
int noOfNull =0 ,i=0;
for(; i< strings.size() ; i++)
{
if(strings.get(i) == null)
{
noOfNull++;
}
}
strings.removeAll(Collections.singleton(null));
Filling the array with null after the not null elements
for(i =strings.size(); i < strings.size()+noOfNull ; i++)
{
strings.add(null);
}
List<String> list = new ArrayList<>();
list.add("BR64");
list.add("SWG620");
list.add("");
list.add("sw0");
list.add("R124");
list.add("R219");
list.add("TaGh20");
list.add("SW6505");
list.add("");
list.add(null);
list.add("SW_6505");
list.add("swd_157");
list.add("localhost");
list.add("qaGh20_241");
list.add("gen");
list.add(null);
list.add("taGh20");
list.add("zen");
list.add("QWG");
list.add("SWG62_");
list.add("SWG620");
Collections.sort(list, new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
if (o1 != null && o2 != null && o1.length() > 0 && o2.length() > 0) {
return (Character.toLowerCase(o1.charAt(0)) == Character.toLowerCase(o2.charAt(0)))
? o1.compareTo(o2)
: (Character.toLowerCase(o1.charAt(0)) + o1.substring(1))
.compareTo((Character.toLowerCase(o2.charAt(0)) + o2.substring(1)));
} else {
return (o1 == o2) ? 0 : ((o1 == null || o1 == "") ? 1 : -1);
}
}
});
System.out.println(list);
Output-: [BR64, gen, localhost, QWG, qaGh20_241, R124, R219, SW6505, SWG620, SWG620, SWG62_, SW_6505, sw0, swd_157, TaGh20, taGh20, zen, , , null, null]
I have used the below method to Sort a Map first on Object.property1 and then for each Object.property1, sort by Object.property2.
for example,
property1 = TaxIdNumber and
property2 = ProviderName
I was just wondering this can be done in a more shorter and precise manner. Any help or suggestion would be appreciated.
private List<TestObject> sortByValue(final Map m) {
List<TestObject> values = new ArrayList<TestObject>();
values.addAll(m.values());
// First sort the list by Tax ID.
Collections.sort(values, new Comparator<TestObject>() {
public int compare(TestObject r1, TestObject r2) {
Long taxId1 = (r1 == null ? null : r1.getTaxIdNumber());
Long taxId2 = (r2 == null ? null : r2.getTaxIdNumber());
if (taxId1 == null || taxId2 == null) {
return 0;
}
return taxId1.compareTo(taxId2);
}
});
// Then sort the list by Provider name.
Collections.sort(values, new Comparator<TestObject>() {
public int compare(TestObject r1, TestObject r2) {
String name1 = (r1 == null ? null : r1.getProviderName());
String name2 = (r2 == null ? null : r2.getProviderName());
if (name1 == null || name2 == null) {
return 0;
}
if (r1.getTaxIdNumber() == r2.getTaxIdNumber()) {
return name1.compareTo(name2);
} else {
return 0;
}
}
});
return values;
}
You only need one comparator. first compare the taxids. If they are unequal return -1 or 1 as appropriate. If they are equals, then compare the provider name.
something like:
Collections.sort(values, new Comparator<TestObject>() {
public int compare(TestObject r1, TestObject r2) {
Long taxId1 = (r1 == null ? null : r1.getTaxIdNumber());
Long taxId2 = (r2 == null ? null : r2.getTaxIdNumber());
if (taxId1 == null || taxId2 == null) {
return 0;
}
int cmp = taxId1.compareTo(taxId2);
if (cmp != 0)
return cmp;
String name1 = (r1 == null ? null : r1.getProviderName());
String name2 = (r2 == null ? null : r2.getProviderName());
if (name1 == null || name2 == null) {
return 0;
}
return name1.compareTo(name2);
}
});
Your null-handling violates the contract of compare, as you deem null equal to any other value, while the JavaDoc writes:
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.
and in particular:
Finally, the implementor must ensure that compare(x, y)==0 implies that sgn(compare(x, z))==sgn(compare(y, z)) for all z.
which your code fails to accomplish for x = null, y = "a", z = "b".
Therefore, if any objects or properties in the list are null, the list may not be sorted correctly.
That being said, I wonder if the list may really contain null values or properties? If not, I'd remove all null checks and end up with
Collections.sort(list, new Comparator<TestObject>() {
#Override public int compare(TestObject o1, TestObject o2) {
int c = o1.getTaxIdNumber().compareTo(o2.getTaxIdNumber);
if (c != 0) {
return c;
}
return o1.getProviderName().compareTo(o2.getProviderName());
}
}
If the list may contain null objects or properties, you must define whether the null values come first or last, and extend the comparator accordingly:
Collections.sort(list, new Comparator<TestObject>() {
#Override public int compare(TestObject o1, TestObject o2) {
// insert null-checks for o1, o2 here
int c = cmp(getTaxIdNumber(), o2.getTaxIdNumber());
if (c != 0) {
return c;
}
return cmp(o1.getProviderName(), o2.getProviderName());
}
private <T extends Comparable<? super T>> cmp(T o1, T o2) {
if (o1 == o2) {
return 0;
else if (o1 == null) {
return -1;
} else if (o2 == null) {
return 1;
} else {
return o1.compareTo(o2);
}
}
}
Now this is quite a bit of repetitive and tricky code, which is why the folks over at Apache wrote the CompareToBuilder. With that API, you can simply write:
#Override int compare(TestObject r1, TestObject r2) {
// insert null checks for r1 and r2 here - if you really need them
return new CompareToBuilder()
.append(r1.getTaxIdNumber(), r2.getTaxIdNumber())
.append(r1.getProviderName(), r2.getProviderName())
.toComparison();
}
}
I have an array of a custom type that I want to sort by one of its String attributes. For some reason, the following code is producing wrong results. Could you point out where I might have made a mistake?
class PatientLNComparator implements Comparator<Patient>{
#Override
public int compare(Patient p1, Patient p2) {
String p1_LN = (p1 == null) ? null : p1.last_name;
String p2_LN = (p2 == null) ? null : p2.last_name;
if(p2_LN == null)
return -1;
else if(p1_LN == null)
return +1;
else if(p1_LN.equals(p2_LN))
return 0;
else if(p1_LN.compareTo(p2_LN) > 0)
return -1;
else
return +1;
}
}
One problem to start with - your comparator is inconsistent if you give it two patients with null names, or two null patient references. In particular:
Patient p1 = null;
Patient p2 = null;
int x = comparator.compare(p1, p2);
int y = comparator.compare(p2, p1);
The signs of x and y ought to be different - but they'll both be -1.
After that, it depends on how you want to compare the names. I would usually use
return p1_LN.compareTo(p2_LN);
if you want to sort in ascending order. Note that to sort in descending order you shouldn't just return -p1_LN.compareTo(p2_LN), as if the comparison returns the Integer.MIN_VALUE, the negation won't work. Instead you'd want to return p2_LN.compareTo(p1_LN);.
Note that if you're using this scheme, you don't need to call p1_LN.equals(p2_LN) either - that will be handled by the compareTo call.
You want patient to be ordered by alphabetical by last name, null patients and null last names up front?
class PatientLNComparator implements Comparator<Patient>{
#Override
public int compare(Patient p1, Patient p2) {
String p1_LN = (p1 == null) ? null : p1.last_name;
String p2_LN = (p2 == null) ? null : p2.last_name;
if (p1_LN == null && p2_LN == null)
return 0;
else if (p2_LN == null)
return -1;
else if(p1_LN == null)
return +1;
else
return p1_LN.compareTo(p2_LN);
}
}
To be stable, it really should order by some other fields, like first name, when last names are equal.
I'm assuming you want natural string ordering for this.
First of all, as it is, your compareTo branch is giving inversed results. Don't know if that's what you intended or not (as in you're saying p1 is greater than p2 when the p1's string is lower than p2's).
Furthermore, you can ditch the .equals branch of the if. The compareTo already handles this case.
Therefore a simple
if(p2_LN == null && p1_LN == null)
return 0;
else if(p1_LN == null)
return +1;
else if(p2_LN == null)
return -1;
else return p1_LN.compareTo(p2_LN)
would suffice.
I would use Guava's Ordering class for this:
class Patient {
// ...
public static final Function<Patient, String> GET_LAST_NAME =
new Function<Patient, String>() {
public String apply(Patient from) {
if (from == null) return null;
return from.last_name;
}
};
public static final Comparator<Patient> BY_LAST_NAME =
Ordering.natural()
.onResultOf(GET_LAST_NAME)
.nullsFirst();
}
This will resolve the issue with inconsistent comparison of nulls. It also makes it easy to add a secondary order (e.g. first name):
public static final Comparator<Patient> BY_LAST_NAME =
Ordering.natural()
.onResultOf(GET_LAST_NAME)
.compound(Ordering.natural().onResultOf(GET_FIRST_NAME))
.nullsFirst();