Recursion: Finding number of elements - java

I have following recursive method that returns the number of element in a nested Collection. A Collection contains child Collections plus Elements.
Is there a faster algorithm to achieve this?
int elementCount = 0;
#Override
public int getElementCount(CollectionDTO collectionDTO){
if(collectionDTO == null){
return elementCount;
}
if (collectionDTO.getChildCollectionDTOs() != null
&& collectionDTO.getChildCollectionDTOs().size() > 0) {
for (CollectionDTO collection : collectionDTO.getChildCollectionDTOs())
getElementCount(collection);
}
if(collectionDTO.elements != null && collectionDTO.elements.size() > 0)
elementCount +=collectionDTO.elements.size();
return elementCount;
}

In the worst case you are calling collectionDTO.getChildCollectionDTOs() three times so you should consider to call it just once, store the result in a variable and reuse it.
If another caller of this method which has the same reference to this object comes into play the usage of that class level variable elementCount will have side effects and won't return the correct result.
You should always use braces {} although they are optional for single lined if statements or for loops. This will just make your code less error prone.
Applying these points will lead to
#Override
public int getElementCount(CollectionDTO collectionDTO){
if(collectionDTO == null){
return 0;
}
int elementCount = 0;
if(collectionDTO.elements != null && collectionDTO.elements.size() > 0) {
elementCount +=collectionDTO.elements.size();
}
List<CollectionDTO> children = collectionDTO.getChildCollectionDTOs();
if (children == null){
return elementCount;
}
for (CollectionDTO collection : children)
elementCount += getElementCount(collection);
}
return elementCount;
}

Related

How can I check if two lists are equals?

I have to solve one problem, I don't know the reason why my code doesn't work.
I have to check if two lists I created are completely equals so they have the same value at the same position.
I'm allowed to use loops as well, even by I prefer the recursive mode.
Thank you so much for your help and time!
public static boolean checkEquality(Node n, Node m) {
if(n != null && m != null) {
boolean res = false;
while(n!=null) {
if(n.getElem()==m.getElem()) {
n = n.getNext();
m = m.getNext();
res = true;
}
else
{
res = false;
}
}
return res;
}
else
{
System.out.println("Lists empty!");
return true;
}
}
There are a couple of weak spots, so I give the solid solution:
public static boolean checkEquality(Node n, Node m) {
while (n != null && m != null) {
//if (!Objects.equals(n.getElem(), m.getElem())) {
if (n.getElem() != m.getElem()) {
return false;
}
n = n.getNext();
m = m.getNext();
}
return n == null && m == null;
}
Comparing can only be done while both n and m are not null. Your code only checks n.
== is not valid for instance for String. Instead of .equals one might also use Objects.equals which also tests for null.
getNext in every loop step.
two empty lists are also equal. Both lists should end at the same time.
The tst fails as soon as two compared nodes are not equal. So one should start with assuming a true result. And as soon as the comparison fails, one should no longer loop and certainly not overwrite res from false to true.
it would help if you elaborate what type of list u are comparing,
linkedlist or arrays. based on your function, it seems that you are planning to compare a linkedlist.
linkedlist documentation
arrays documentation
// sample comparison
boolean areIdentical(Node a_head, Node b_head) {
Node a = a_head, b = b_head;
while (a != null && b != null) {
if (a.data != b.data)
return false;
/* If we reach here, then a and b are not null
and their data is same, so move to next nodes
in both lists */
a = a.next;
b = b.next;
}
// If linked lists are identical, then 'a' and 'b'
// must be null at this point.
return (a == null && b == null);
}

Java TreeSet not adding object

I am trying to add objects into a Treeset but the objects not all are getting added.
class Fruits
{
String name ;
int weight;
int price;
Fruits(String n, int w, int p)
{
this.name=n;
this.weight=w;
this.price =p;
}
#Override
public int hashCode() {
System.out.println("hashcode called");
int prime =31;
int result =1;
result = prime*result +(this.name.hashCode()+this.price+this.weight);
return result;
}
#Override
public boolean equals(Object obj) {
System.out.println("Equals called");
if(null!=obj)
{
Fruits f= (Fruits) obj;
if(this.name.equals(f.name) && this.price==f.price && this.weight == f.price)
{
return true;
}
}
return false;
}
}
class FruitsComparator implements Comparator<Fruits>
{
//Order by Name, then quanity and then Price
#Override
public int compare(Fruits f1, Fruits f2)
{
if(f1.name.equals(f2.name) && f1.weight == f2.weight && f1.price == f2.price)
{
System.out.println(1);
return 0;
}
else if(f1.name.equals(f2.name) && f1.weight==f2.weight && f1.price < f2.price)
{
System.out.println(2);
return -1;
}
else if (f1.name.equals(f2.name) && f1.weight==f2.weight && f1.price > f2.price)
{
System.out.println(3);
return 1;
}
else if (f1.name.equals(f2.name) && f1.weight<f2.weight && f1.price == f2.price)
{
System.out.println(4);
return -1;
}
else if (f1.name.equals(f2.name) && f1.weight>f2.weight && f1.price == f2.price)
{
System.out.println(5);
return 1;
}
else if (f1.name.compareTo(f2.name) <1 && f1.weight==f2.weight && f1.price == f2.price)
{
System.out.println(6);
return -1;
}
else if (f1.name.compareTo(f2.name) >1 && f1.weight==f2.weight && f1.price == f2.price)
{
System.out.println(7);
return 1;
}
return 0;
}
}
From public static void main of another class.
Fruits f1= new Fruits("Apple",1,3);
Fruits f2= new Fruits("Apple",10,1);
Fruits f3= new Fruits("Apple",15,2);
Set<Fruits> sf = new TreeSet<Fruits>(new FruitsComparator());
sf.add(f1);
sf.add(f2);
sf.add(f3);
System.out.println("--Fruits Example--");
for( Fruits f: sf)
{
System.out.println(f.name+"-"+f.weight+"-"+f.price);
}
The output I get is :
--Fruits Example--
Apple-1-3
But when I have fruits objs as below i get the all the objects
just keeping everything same but the third element.
Fruits f1= new Fruits("Apple",1,3);
Fruits f2= new Fruits("Apple",1,1);
Fruits f3= new Fruits("Apple",1,2);
The output get for this is
--Fruits Example--
Apple-1-1
Apple-1-2
Apple-1-3
So somehow my objects are treated as same when I keep different elements on weight and price. I couldn't figure out as why the objects are treated as same. Please help.
The primary issue is, you are always checking two fields to be equal and only one to be different.
At the final else, that happens if at least 2 fields are different, you return 0 which means they should be treated as equal, and that is the reason you have this issue.
Since the order you want is to first sort by name, then by quantity and then by price, remove the && f1.price == f2.price from the 4th condition onwards, and remove && f1.weight==f2.weight on the last two.
You can avoid this issue completely if you use Java 8 style.
Set<Fruits> sf = new TreeSet<Fruits>(Comparator.comparing(Fruits::getName)
.thenComparing(Fruits::getWeight)
.thenComparing(Fruits::getPrice)
);
I have added the working code in codiva - online java compiler ide. I have also included a slightly cleaner implementation in FruitsComparator.java file.
Tree related collections don't use equals() or hashCode(). Those come into play for Map.
Your conditions in the compare result in a 0, hence the fruit isn't inserted.
First Apple goes in as the tree is empty. The 2nd & 3rd Apple result in false in all the if conditions, thus returning the final 0. Put a System.out.println() before the final return to confirm.
If you want to sort the fruits first by name, then by weight & then finally by price, here's a more compact way doing it:
#Override
public int compare(Fruits f1, Fruits f2) {
if (f1.name.equals(f2.name)) {
if (f1.weight < f2.weight) {
return -1;
} else if (f1.weight > f2.weight) {
return 1;
} else {
if (f1.price < f2.price) {
return -1;
} else if (f1.price > f2.price) {
return 1;
} else {
return 0;
}
}
} else {
return f1.name.compareTo(f2.name);
}
}
TreeSet, when used with a Comparator, the elements' equality is decided by the compare method of the Comparator, otherwise would use the compareTo method of its element since they are required to implement the Comparable interface. The hashcode and equals methods will only be used by the Set interface itself (such as method contains uses equals method to check if the elements are presented). And hashcode is not something that a TreeSet to use while it is used by HashSet which is totally another way to implement Set interface. Thus, in your code, since the compare method you've overridden of the Comparator treats these elements equal, so they cannot be inserted for multiple times. One guideline that the Java Tutorial points out is, the compare method should comply with the equals methods, which is, the elements should be treated equal in the compare method if and only if the equals method do.
And in your equals method, you did use this.weight == f.price to compare two fruits, which I don't think is what you intended to do. This makes your equals methods not consistent with the compare method.
For your reference, see Java Object Ordering tutorial, and as well as a question I asked two days ago.
You have an error in your equals method in class Fruits:
if(this.name.equals(f.name) && this.price==f.price && this.weight == f.price)
should have been:
if(this.name.equals(f.name) && this.price==f.price && this.weight == f.weight)
(note the last part).

Compare one ArrayList and combine common elements

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

Linked List Null Error

I have written the following code to compute the sum of all the even entries in a LinkedList. However, I keep getting a NullPointerException because of the line where I use (n.getNext).getNext().
Could any of guys tell me as to why this is happening?
Here is the piece of code I'm referring to:
public int sumEven() {
return sumEven(head);
}
// private sumEven helper
private int sumEven(IntListNode n) {
int nodeNumber=1;
int count=0;
if(n.getNext() == null && nodeNumber%2 == 0) {
return n.getValue();
} else if((n.getNext()).getNext() == null && nodeNumber%2 == 0) {
return n.getValue();
} else {
nodeNumber++;
if(nodeNumber%2 == 0) {
count+=n.getValue();
return count+ sumEven(n.getNext());
} else {
return count + sumEven(n.getNext());
}
}
}
Because what if: In the first if statement n.getNext() is null but nodeNumber%2 is not equal to 0
So you go to the next if statement which says n.getNext().getNext() == null where already the first getNext() is null since it passed the first part of the previous if statement but failed the if statement because of the nodeNumber%2 == 0 part.
Ex: nodeNumber = 1 and n.getNext() = null
First if(true && false) => false
Second if(ERROR) Cuz the previous if statement has the first part true which says n.getNext() is null
There are many problems in the code. How about rethink the strategy? If you want to make it recursive, you can assume that the private method sumEven() will always be called with an even node. Then all that private method does is add up whatever that it is called with.
The following code is not tested but shows what I mean.
public int sumEven() {
return sumEven(head,0);
}
private int sumEven(IntListNode n, int total) {
if(n==null) return total;
total += n.getValue(); // sums up running total
if(n.getNext()==null) return total;
return sumEven(n.getNext().getNext(),total); // calls next even node with current running total
}

How do I search for a String in an array of Strings using binarySearch or another method?

Using binarySearch never returns the right index
int j = Arrays.binarySearch(keys,key);
where keys is type String[] and key is type String
I read something about needing to sort the Array, but how do I even do that if that is the case?
Given all this I really just need to know:
How do you search for a String in an array of Strings (less than 1000) then?
From Wikipedia:
"In computer science, a binary search is an algorithm for locating the position of an element in a sorted list by checking the middle, eliminating half of the list from consideration, and then performing the search on the remaining half.[1][2] If the middle element is equal to the sought value, then the position has been found; otherwise, the upper half or lower half is chosen for search based on whether the element is greater than or less than the middle element."
So the prerequisite for binary search is that the data is sorted. It has to be sorted because it cuts the array in half and looks at the middle element. If the middle element is what it is looking for it is done. If the middle element is larger it takes the lower half of the array. If the middle element is smaller it the upper half of the array. Then the process is repeated (look in the middle etc...) until the element is found (or not).
If the data isn't sorted the algorithm cannot work.
So you would do something like:
final String[] data;
final int index;
data = new String[] { /* init the elements here or however you want to do it */ };
Collections.sort(data);
index = Arrays.binarySearch(data, value);
or, if you do not want to sort it do a linear search:
int index = -1; // not found
for(int i = 0; i < data.length; i++)
{
if(data[i].equals(value))
{
index = i;
break; // stop looking
}
}
And for completeness here are some variations with the full method:
// strict one - disallow nulls for everything
public <T> static int linearSearch(final T[] data, final T value)
{
int index;
if(data == null)
{
throw new IllegalArgumentException("data cannot be null");
}
if(value == null)
{
throw new IllegalArgumentException("value cannot be null");
}
index = -1;
for(int i = 0; i < data.length; i++)
{
if(data[i] == null)
{
throw new IllegalArgumentException("data[" + i + "] cannot be null");
}
if(data[i].equals(value))
{
index = i;
break; // stop looking
}
}
return (index);
}
// allow null for everything
public static <T> int linearSearch(final T[] data, final T value)
{
int index;
index = -1;
if(data != null)
{
for(int i = 0; i < data.length; i++)
{
if(value == null)
{
if(data[i] == null)
{
index = i;
break;
}
}
else
{
if(value.equals(data[i]))
{
index = i;
break; // stop looking
}
}
}
}
return (index);
}
You can fill in the other variations, like not allowing a null data array, or not allowing null in the value, or not allowing null in the array. :-)
Based on the comments this is also the same as the permissive one, and since you are not writing most of the code it would be better than the version above. If you want it to be paranoid and not allow null for anything you are stuck with the paranoid version above (and this version is basically as fast as the other version since the overhead of the method call (asList) probably goes away at runtime).
public static <T> int linearSearch(final T[] data, final T value)
{
final int index;
if(data == null)
{
index = -1;
}
else
{
final List<T> list;
list = Arrays.asList(data);
index = list.indexOf(value);
}
return (index);
}
java.util.Arrays.sort(myArray);
That's how binarySearch is designed to work - it assumes sorting so that it can find faster.
If you just want to find something in a list in O(n) time, don't use BinarySearch, use indexOf. All other implementations of this algorithm posted on this page are wrong because they fail when the array contains nulls, or when the item is not present.
public static int indexOf(final Object[] array, final Object objectToFind, int startIndex) {
if (array == null) {
return -1;
}
if (startIndex < 0) {
startIndex = 0;
}
if (objectToFind == null) {
for (int i = startIndex; i < array.length; i++) {
if (array[i] == null) {
return i;
}
}
} else {
for (int i = startIndex; i < array.length; i++) {
if (objectToFind.equals(array[i])) {
return i;
}
}
}
return -1;
}
To respond correctly to you question as you have put it. Use brute force
I hope it will help
public int find(String first[], int start, int end, String searchString){
int mid = start + (end-start)/2;
// start = 0;
if(first[mid].compareTo(searchString)==0){
return mid;
}
if(first[mid].compareTo(searchString)> 0){
return find(first, start, mid-1, searchString);
}else if(first[mid].compareTo(searchString)< 0){
return find(first, mid+1, end, searchString);
}
return -1;
}
Of all the overloaded versions of binarySearch in Java, there is no such a version which takes an argument of String. However, there are three types of binarySearch that might be helpful to your situation:
static int binarySearch(char[] a, char key);
static int binarySearch(Object[] a, Object key);
static int binarySearch(T[] a, T key, Comparator c)

Categories

Resources