Improving the performance of merging two ArrayLists - java

I am merging two ArrayLists with the following code. The code is working and giving me the desired result, but I want a more efficient version. Here are the conditions.
Method accepts two lists, and both list have elements in decreasing order (5,4,3,2)
Method accepts an integer to decide the size of the resulting ArrayList.
First input list size is never greater than the size of the resulting ArrayList.
Code:
public ArrayList<Integer> mergeList(ArrayList<Integer> first,ArrayList<Integer> second, int n){
//case 1: when both list are null.
if(first == null && second == null )
return null;
//case 2: when first list is null but second list have elements
else if( first == null && second != null){
return second.size() >=n ? new ArrayList<Integer>(second.subList(0, n)) : second;
}
//case 3: when first list have record and second list is null
else if(first != null && second == null){
return first;
}
//case 4: when both list have elements
else {
first.addAll(second);
Collections.sort(first);
Collections.reverse(first);
return first.size()>=n ? new ArrayList<Integer>(first.subList(0, n)) : first;
}
}
}

It depends on what you mean by "More efficient".
In terms of what? Memory, CPU, readability?
Based on your code above, I'm making the following assumptions:
Readability is more important than pure performance/memory consumption without any profiling measurements/requirements "The First Rule of Program Optimization: Don't do it. The Second Rule of Program Optimization (for experts only!): Don't do it yet." — Michael A. Jackson
Prefer the null object pattern over returning null
Duplicate elements are desirable/required
Use a Comparator to perform a reverse
sort
private List<Integer> mergeList(List<Integer> list1, List<Integer> list2, final int newSize) {
// Enforce null object pattern
if (list1 == null) {
list1 = Collections.emptyList();
}
if (list2 == null) {
list2 = Collections.emptyList();
}
// If duplicates are not desirable, a TreeSet would perform automatic sorting.
List<Integer> result = new ArrayList<Integer>(list1);
result.addAll(list2);
Comparator<Integer> reverseSortComparator = new Comparator<Integer>() {
#Override
public int compare(final Integer o1, final Integer o2) {
return o2.compareTo(o1);
}
};
Collections.sort(result, reverseSortComparator);
if (result.size() > newSize) {
return result.subList(0, newSize);
} else {
return result;
}
}

It looks like you are trying to preserve the contents of first and second. If you are not, then this will do just fine for you and will make your code both faster and more readable:
public ArrayList<Integer> mergeList(ArrayList<Integer> first,ArrayList<Integer> second, int maxLength){
//case 1: when both list are null.
if(first == null && second == null )
return null;
//case 2: when first list is null but second list have elements
else if( first == null && second != null){
return second;
}
//case 3: when first list have record and second list is null
else if(first != null && second == null){
return first;
}
//case 4: when both list have elements
else if(first != null && second != null){
first.addAll(second);
Collections.sort(first); //want to merge these two line into one
Collections.reverse(first);
}
return (ArrayList) first.size() > maxLength ? first.subList(0, n) : first;
}
The reason why this is faster is because with each addAll(), Java must iterate through all the items, copying them into tempList. I preserved the Collections.reverse call because it seems like you need to have your data in reverse sorted order.

Related

How to check if array is null or if the array contents are null

What is the best way to check if array is null value or if the array contents are null in combination in 1 statement in Java 6:
if ((myArray[0] != null) || (myArray[1] != null) || (myArray!= null)) {
...
}
To have it check for any value, I'd use allMatch. It's also important to check for array != null first, otherwise you'll get an Exception if it is.
if (array == null || Arrays.stream(array).allMatch(Objects::isNull))
Note that this won't work with java prior to version 8, OP edited his requirements after I posted the answer
Firstly, check if the array is not null itself. If the array is null, it makes no reason to iterate its elements since Java will throw the NullPointerException upon access to it:
if (myArray != null) {
// ...
}
Then inside the body of the condition iterate through all its elements and check if one of them is null.
boolean hasNull = false;
for (int i=0; i<myArray.length; i++) {
if (myArray[i] == null) {
hasNull = true;
break; // to terminate the iteration since there is no need to iterate more
}
}
This one-line solution (thanks for the warning from #Napstablook). The condition is evaluated as true if the array itself is null or one of its element is null:
if !(myArray != null && myArray[0] != null && myArray[1] != null) { ... }
Be aware that the && operator works that if the left side is evaluated as false, it stops evaluating the rest of the condition because it will not affect the result. The same does || but with true. However, I suggest you avoid this solution since the index might overflow. Better use the for-loop mentioned above.
Check if Array is null:
String array[] = null;
if (array == null) {
System.out.println("array is null");
}
Check if array is Empty:
array = new int[0];
if (array.length == 0) {
System.out.println("array is empty");
}
Check for null at the same time:
int[] array = ...;
if (array.length == 0) { } // no elements in the array
if (array == null || iarray.length == 0) { }
Try this
if (myArray == null || Arrays.stream(myArray).allMatch(element-> element==null)) {}
Edit- For java 6, I really don't see this happening in one line. You can try this if one line is not necessary
boolean isNull = true;
if(myArray==null){
System.out.println("array is null");
}else{
for(Integer element: myArray){
if(element!=null){
System.out.println("array is not null");
isNull=false;
break;
}
}
if(isNull)
System.out.println("Array is null");
}
Instead Itreating Manullay the array, re use the existing collection for this case.
Convert Array Into List
Check Null is present in list or not using contains() method;
Please find the sample code:
public static void main(String[] args) {
Integer[] array = new Integer[3];
array[0] = 1;
array[1] = null;
array[2] = 2;
System.out.println(Arrays.asList(array).contains(null));
}
for example this
boolean isNullOrContainsNull = array == null || Arrays.asList(array).contains(null);
checks in a line whether the array is null or contains null elements
If you want to check whether the array is null or empty or all elements are null take
boolean containsNothingUseful = array == null
|| array.length == 0
|| !new HashSet<String>(Arrays.asList(array))
.retainAll(Arrays.asList((String)null));
(assuming a String[] array)
Uses the Collection#retainAll() method which returns true when there were other values present, i.e. the inverse of "containsOnly"
Using this one liner is actually fairly inefficient and one better uses a method like below which doesn't create lots of temporary objects and mutates collections etc.
public static boolean containsNothingUseful(String[] array) {
if (array == null || array.length == 0)
return true;
for (String element : array) {
if (element != null)
return false;
}
return true;
}
// ...
if (containsNothingUseful(myArray)) { .. }

Sort Users using Insertion Sort

I am trying to sort users in ascending order when I search for the users in our UI. I started the approach with Insertion Sort and I currently have something like this:
if (CollectionUtils.isNotEmpty(userList)) {
//Sort by ascending by display name
for (int i=1; i<userList.size(); i++){
User key = userList.get(i);
int j = i-1;
System.out.println("I: "+key.getFirstName());
System.out.println("J: "+userList.get(j).getFirstName());
while (j>-1 && isAscending(key.getFirstName(), userList.get(j).getFirstName())){
User temp = userList.get(j+1);
System.out.println("TEMP: "+temp.getFirstName());
userList.add(j+1, userList.get(j));
userList.add(j, temp);
j--;
}
userList.add(j+1, key);
}
for (final User user : userList) {
beanList.add(UserBean.getInstance(user));
}
}
Assuming that we have nulls, my isAscending method checks for that and the idea is that the users who's name is null will be placed at the bottom of the list:
private boolean isAscending(String left, String right){
if(left.equals(right)) return false;
if(left == null && right == null) return false;
if((left == null && right != null) || left != null && right == null){
return false;
}
if(left.toLowerCase().compareTo(right.toLowerCase())>0){
return true;
}
return false;
}
With these 2 methods I want to be able to sort users in an ascending order based on their first name. Currently I am running into an infinite loop as after the first iteration, the user at the first index is being compared to himself (aka the user at the first index) over and over again.
Any suggestion would be welcome. Thanks
One of your problems is that you keep adding stuff to the list, but you never remove anything. For example, your inner loop is:
while (j>-1 && isAscending(key.getFirstName(), userList.get(j).getFirstName())){
User temp = userList.get(j+1);
System.out.println("TEMP: "+temp.getFirstName());
userList.add(j+1, userList.get(j));
userList.add(j, temp);
j--;
}
Let's say the list contains [3,2,1]. So the first time through you get:
temp = userList.get(1) // so temp = 2
userList.add(1, 3); // this makes the list contain [3,3,2,1]
userList.add(j, temp) // result: [2,3,3,2,1]
You want to swap the values at locations j and j+1. So write:
temp = userList.get(j+1);
userList.set(j+1, userList.get(j));
userList.set(j, temp);
Also, you probably should fix your isAscending method. Currently, you're calling equals on a potentially null reference. Also, there's no reason to call toLowerCase on the parameters to compareTo when you can easily call compareToIgnoreCase. A cleaner comparison method is:
private boolean isAscending(String left, String right){
// if either parameter is null, then not ascending
if(left == null || right == null) return false;
return (left.compareToIgnoreCase(right) > 0);
}
You might reconsider the null handling logic in your code. Does it really make sense for there to be null values for the fields you're comparing? If not, then you probably should have the comparison method throw an exception.
If you do want to support null values, then you need to modify your isAscending method because the current logic makes any non-null value equal to any null value. So if your list contained [3,null,1], then sorting it would return the same order because calling isAscending(3, null) will return false, and so will isAscending(null, 1). If you want to sort nulls to the front, you want this:
// both values are null, so not ascending
if (left == null && right == null) return false;
// left is null, but right is not
if (left == null) return true;
// right is null, but left is not
if (right == null) return false;
return (left.compareToIgnoreCase(right) > 0);

Check two arguments for null in an elegant way

I am iterating over two collections and check if both collections contain
the same elements. I can't use Java 8.
edit 1 year after:
I created the method in the question to check if two Collections contain the same elements, without thinking about the fact that I am passing two Collection implementations into the method.
But Collection does not determine how elements are sorted. And I am iterating over the collections. Thus, some implementation of Collection could save elements in random order, while containing the same elements.
Both collections contain elements that are comparable and the content
is defined as equal, if all elements return a x.compareTo(y) with 0.
Two values are defined as different, if one of them is null, but not the other.
I want to find an elegant way to compare on nullity and prevent
a null check on the final compareTo().
My current implementation:
public static <T extends Comparable<T>> boolean isSame(#Nullable Collection<T> a, #Nullable Collection<T> b) {
if (a == null || b == null) {
return (a == null && b == null);
}
if (a.size() != b.size()) {
return false;
}
Iterator<T> aIt = a.iterator();
Iterator<T> bIt = b.iterator();
while (aIt.hasNext()) {
T aValue = aIt.next();
T bValue = bIt.next();
if (aValue == null || bValue == null) {
if (aValue == null ^ bValue == null) {
return false;
}
//both null, don't compare, continue looping...
} else if (aValue.compareTo(bValue) != 0) {
return false;
}
}
return true;
}
I want to continue the while loop, if both values are null, because that is
defined as equal.
But I am struggling with this part:
if (aValue == null || bValue == null) {
if (aValue == null ^ bValue == null) {
return false;
}
}
Question:
Is there a more elegant and readable way to compare on nullity, do a further compare if both are not null, return false if only one is null, and continue the loop, if both values are null?
The sequence as follows should work well:
if(aValue == null && bValue == null) continue; // both null; continue
if(aValue == null || bValue == null) return false; // any null; return false
if(aValue.compareTo(bValue) != 0) { // both non-null; compare
return false;
}
In Java8, you can build a Comparator that would replace comparison sequence at cost of creating an extra object (you will need to decide if you care about that):
Comparator<T> cmp = Comparator.nullsLast(Comparator.naturalOrder());
The compararor will take care of null comparison for you (since you assume that two nulls are equal):
while (aIt.hasNext()) {
T aValue = aIt.next();
T bValue = bIt.next();
if (cmp.compare(aValue, bValue) != 0) {
return false;
}
}

String sorting null values

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

Sort ArrayList w.r.t Date in java

I am trying to sort ArrayList w.r.t termDate
ArrayList contains firstName,lastName,email,startDate,termDate
Dates can be either empty or null.
I have to compare accordingly and put all the null/empty date values at the end.
Collections.sort(usersList, new Comparator<User>() {
public int compare(User o1, User o2) {
if(o1.getTermDate() == null && o2.getTermDate() == null)
return 0; //They are both null, both equal
if(o1.getTermDate() == null && o2.getTermDate() != null)
return -1; // The first is null and the second is not, return the first as lower than the second
if(o1.getTermDate() != null && o2.getTermDate() == null)
return 1; //The first is not null and the second is, return the first as higher than the second
else
return o1.getTermDate().compare(o2.getTermDate()); //Return the actual comparison
}
});
It is not compiling compare method.
Please guide.
You should change return o1.getTermDate().compare(o2.getTermDate()); to return o1.getTermDate().compareTo(o2.getTermDate()); Because the class java.util.Date; has not method called compare.

Categories