Cannot retain value in Java recursive call? - java

I have the following method in my Java app:
public List<UUID> recursiveMethod(UUID productUuid) {
Set<UUID> productUuidSet = new HashSet<>();
productUuidSet.add(productUuid);
List<Product> subProducts = productRepo.getSubProducts(productUuid);
for (Product subProduct : subProducts) {
final UUID subProductUuid = subProduct.getProductUuid();
productUuidSet.add(subProductUuid);
productUuidSet.addAll(recursiveMethod(subProductUuid));
}
return new ArrayList<>(productUuidSet);
}
As far as I know, recursive method keep each call result recursively. And, when calling the recursive method via productUuidSet.addAll(recursiveMethod(subProductUuid));, it creates a new HashSet, but as it returns the value inside the HashSet, I would be able to keep it in productUuidSet after recursive call.
So, what is wrong with this method? If there is a problem, how can I retain productUuidSet value by adding result after each recursive call?

If I understand your question, you want to keep the set rather than creating new set each time?
This should work:
public List<UUID> recursiveMethod(UUID productUuid) {
Set<UUID> productUuidSet = new HashSet<>(); // or LinkedHashSet if order matters
recursiveMethod0(productUuidSet, productUuid);
return new ArrayList<>(productUuidSet);
}
private void recursiveMethod0(Set<UUID> productUuidSet, UUID productUuid) {
productUuidSet.add(productUuid);
List<Product> subProducts = productRepo.getSubProducts(productUuid);
for (Product subProduct : subProducts) {
recursiveMethod0(productUuidSet, subProduct.getProductUuid());
}
}
TL;DR: simply pass the set instead of recreating it each time.
I simplified your insertion code: productUuidSet.add(subProductUuid); is already done by the recursive function.

Related

Java List removing items without any action

I'm facing a weird behavior in my Java code using List.
The code is very simple, I have a List of Object called AccessRequest which comes from a database and I'm using this first List to create a new one but with a filter to select only a few objects.
Here is the code :
private void updateCommentIfNeeded() {
List<AccessRequest> accessRequestList = getAllRequest();
List<AccessRequest> commentsList = getCommentsListProcessedManually(accessRequestList);
}
public List<AccessRequest> getCommentsListProcessedManually(List<AccessRequest> accessRequests) {
accessRequests.removeIf(ar -> !ar.getComment().equals("To be processed manually"));
if (accessRequests.size() != 0) {
SQLServerConnection sqlServerConnection = new SQLServerConnection(sqlServerUrl);
accessRequests.removeIf(ar -> !sqlServerConnection.emailExists(ar.getEmail()));
}
return accessRequests;
}
I'm supposed to get a second List only containing the objects that has their comments to To be processed manually, which I do. But the weird part is that the first List also takes the value of the second as if I wrote accessRequestList = commentsList but there is no such thing and I'm using local variable.
Ex :
I have 3 objects in my first List, but only one containing the required comment
Both list ends with containing the only objects containing the comment
I'm kind of lost here if anyone has an idea !
Your method getCommentsListProcessedManually modifies the list you're passing. I believe you're operating under the assumption that passing the list as a parameter somehow creates a copy of the list, whereas what is actually happening is that a reference to the list is passed by value.
There are several ways to solve this, but the easiest is to simply create a copy of your input list at the start of your method:
public List<AccessRequest> getCommentsListProcessedManually(List<AccessRequest> input) {
List<AccessRequest> accessRequests = new ArrayList<>(input);
accessRequests.removeIf(ar -> !ar.getComment().equals("To be processed manually"));
if (accessRequests.size() != 0) {
SQLServerConnection sqlServerConnection = new SQLServerConnection(sqlServerUrl);
accessRequests.removeIf(ar -> !sqlServerConnection.emailExists(ar.getEmail()));
}
return accessRequests;
}
You could also use the Stream API for this (using the filter operation), but that's quite a bit trickier in this situation.
You are passing a reference of the list to the method getCommentsListProcessedManually.
So accessRequestList and the one passed as a parameter are the same, hence any operation done to the list is done to the same list.
You can create a copy of the list before passing it as a parameter:
List<AccessRequest> newList = new ArrayList<AccessRequest>(accessRequestList);

Update a list reference inside a method

In Java we can not reassign a reference inside a method.
So the following does not work:
class SomeClass {
List<PaidOrders> paidOrders;
List<PendingOrders> pendingOrders;
List<CancelledOrders> cancelledOrders;
private void process(List<OrderStatus> data, List<Orders> currentOrderlist) {
List<Order> newOrders = fromOrderStatus(data);
currentOrderlist = newOrders;
}
}
But the following does work:
class SomeClass {
private void process(List<OrderStatus> data, List<Orders> currentOrderlist) {
List<Order> newOrders = fromOrderStatus(data);
currentOrderlist.clear();
currentOrderlist.addAll(newOrders); // <- extra linear loop
}
}
The problem is that the second example does an extra linear loop to copy from one list to the other.
Question:
I was wondering, is there some design approach so that I could neatly just replace the references instead? I.e. somehow make the first snippet work with some change in the parameters or something?
Update
After the comments I would like to clarify that the currentOrderList can be any of the paidOrders, pendingOrders, cancelledOrders.
The code for process is the same for all types.
Hm. I see two possibilities here. Either you use some wrapper object such as AtomicReference (might be a bit overpowered because of the multi-threading issues) as the argument and then just set it there or you use a consumer.
In the second case your method would look like this:
public void process(List<OrderStatus> data, Consumer<List<Orders>> target) {
List<Person> newOrders = fromOrderStatus(data);
target.accept(newOrders);
}
Then on the calling side you would implement it like this:
process(data, e-> <<targetList>> = e);
If your list will be wrapped by a different object (for example - AtomicReference), then you will be able to change it.
public static void doSomething(AtomicReference<List<Integer>> listAtomicReference){
List<Integer> newIntegers = new ArrayList<>();
listAtomicReference.set(newIntegers);
}
public static void main(String[] args) {
AtomicReference<List<Integer>> listAtomicReference = new AtomicReference<>(Arrays.asList(4));
doSomething(listAtomicReference);
System.out.println(listAtomicReference.get());
}
Output:
[]
Making a public member variable in a class.
With that being said, I wouldn't recommend walking this path.
Is premature optimization really the root of all evil?

arraylist remove eliminates following objects

I'm getting a very strange action in my code. I have an ArrayList of the following class.
class mySocket
{
public String name;
public Socket sck;
public mySocket(String n,Socket s)
{
this.name=n;
this.sck=s;
}
}
I declare the object like this
ArrayList<mySocket> handler = new ArrayList<>();
Now the problem is that when I try to remove an item using this method:
public void removeByName(String name)
{
synchronized(this)
{
mySocket t;
int i;
for(i=0;i<handler.size();i++)
{
t=handler.get(i);
if((t.name.equals(name)))
{
handler.remove(i);
break;
}
}
}
}
The remove function clears everything that follows the index.
For Example:
if this ArrayList has 3 elements and I call handler.remove(1) it removes not only 1 but also the object on line 2.
I think your issue is that you are using an indexed for loop and removing by index. In your example, if your list has 3 elements and you remove index 1, the object that was at index 2 is still there. It's just now at index 1.
A better way to do what you're attempting is to use an iterator or for-each loop.
//code outside for loop the same
for( mySocket socket : handler ) {
if((socket.name.equals(name)))
{
handler.remove(socket);
break;
}
}
Is the ordering of your mySocket objects important? If not, storing them in a Map keyed by name would save you some trouble. Then you would just call handler.remove(name). This operation is safe, even if name doesn't exist in the map. Also, for current uses of the collection handler that don't care aobut the name, you can retrieve the unordered Set of mySockets by calling map.values(). You would then be able to iterate over that Set using an iterator or for-each as above.
You CAN NOT remove items in a Collection while looping through them, the result, as you have seen, is undefined.
You either have to build a list of items to be removed and use
originalList.removeAll(itemsToBeRemoved);
Or you build your loop using an iterator.
Iterator<mySocket> handlerIterator = handler.iterator();
while (handlerIterator.hasNext()) {
mySocket t = handlerIterator.next();
if (t.name.equals(name)) {
handlerIterator.remove();
}
}

JMockit | trying to define different return values based on parameters but getting unexpected results

I've got a class like the following:
class A
{
public method doSomething()
{
//....
DAO dataAccessor = new DAO();
List<Object> result1 = dataAccessor.getData(dataAccessor.getSql1());
List<Object> result2 = dataAccessor.getData(dataAccessor.getSql2());
//.. do some stuff with the results
}
Now, I use jMockit for testing the above function, by mocking the DAO class.
This is how my test class looks like:
class A_Test
{
private A myAClass;
#Mocked DAO mockedDAO;
List<Object> resultToSql1, resultToSql2;
// ... Some initializations...
#Test
public void testDoSomething()
{
new NonStrictExpectations()
{
mockedDAO.getSql1(); result = "SQL1";
mockedDAO.getData(withEqual("SQL1")); result = resultToSql1;
mockedDAO.getSql2(); result = "SQL2";
mockedDAO.getData(withEqual("SQL2")); result = resultToSql2;
};
myAClass.doSomething();
}
}
Now, it seems that the second expectation regarding getData() masks the first one, i.e. the mock object behaves as if I never declared the first lines in the expectation (the ones that handle sql1):
The first call to getData() returns empty collection, instead of the values with which I initialized resultToSql1. The second call returns resultToSql2, as expected.
If I comment the following line:
mockedDAO.getData(withEqual("SQL2")); result = resultToSql2;
the first call is returning what I defined - resultToSql1, and the second returns empty collection.
This makes sense.
So, what am I doing wrong? ?How can I define two different return values from getData() based on the parameters of the call?
Any help would be appreciated.
Thanks!
So, After digging more deeply inside the manual, I found that:
...But what if a test needs to decide the result of a recorded invocation based on the arguments it will receive at replay time? We can do it through a mockit.Delegate instance ...
So, in order to solve the above problem, the expectations block should look like this:
new NonStrictExpectations()
{
mockedDAO.getSql1(); result = "SQL1";
mockedDAO.getSql2(); result = "SQL2";
mockedDAO.getData(anyString);
result = new mockit.Delegate()
{
List<Object> getData(String sql)
{
if (sql.equals("SQL1"))
return resultToSql1;
if (sql.equals("SQL2"))
return resultToSql2;
return null;
}
}
};

Google Collections Suppliers and Find

I'm looking for a Google Collections method that returns the first result of a sequence of Suppliers that doesn't return null.
I was looking at using Iterables.find() but in my Predicate I would have to call my supplier to compare the result against null, and then have to call it again once the find method returned the supplier.
Given your comment to Calm Storm's answer (the desire not to call Supplier.get() twice), then what about:
private static final Function<Supplier<X>, X> SUPPLY = new Function<....>() {
public X apply(Supplier<X> in) {
// If you will never have a null Supplier, you can skip the test;
// otherwise, null Supplier will be treated same as one that returns null
// from get(), i.e. skipped
return (in == null) ? null : in.get();
}
}
then
Iterable<Supplier<X>> suppliers = ... wherever this comes from ...
Iterable<X> supplied = Iterables.transform(suppliers, SUPPLY);
X first = Iterables.find(supplied, Predicates.notNull());
note that the Iterable that comes out of Iterables.transform() is lazily-evaluated, therefore as Iterables.find() loops over it, you only evaluate as far as the first non-null-returning one, and that only once.
You asked for how to do this using Google Collections, but here's how you would do it without using Google Collections. Compare it to Cowan's answer (which is a good answer) -- which is easier to understand?
private static Thing findThing(List<Supplier<Thing>> thingSuppliers) {
for (Supplier<Thing> supplier : thingSuppliers) {
Thing thing = supplier.get();
if (thing != null) {
return thing;
}
}
// throw exception or return null
}
In place of the comment -- if this was the fault of the caller of your class, throw IllegalArgumentException or IllegalStateException as appropriate; if this shouldn't have ever happened, use AssertionError; if it's a normal occurrence your code that invokes this expects to have to check for, you might return null.
What is wrong with this?
List<Supplier> supplierList = //somehow get the list
Supplier s = Iterables.find(supplierList, new Predicate<Supplier>(){
boolean apply(Supplier supplier) {
return supplier.isSomeMethodCall() == null;
}
boolean equals(Object o) {
return false;
}
});
Are you trying to save some lines? The only optimisation I can think is to static import the find so you can get rid of "Iterables". Also the predicate is an anonymous inner class, if you need it in more than one place you can create a class and it would look as,
List<Supplier> supplierList = //somehow get the list
Supplier s = find(supplierList, new SupplierPredicateFinder());
Where SupplierPredicateFinder is another class.
UPDATE : In that case find is the wrong method. You actually need a custom function like this which can return two values. If you are using commons-collections then you can use a DefaultMapEntry or you can simply return an Object[2] or a Map.Entry.
public static DefaultMapEntry getSupplier(List<Supplier> list) {
for(Supplier s : list) {
Object heavyObject = s.invokeCostlyMethod();
if(heavyObject != null) {
return new DefaultMapEntry(s, heavyObject);
}
}
}
Replace the DefaultMapEntry with a List of size 2 or a hashmap of size 1 or an array of length 2 :)

Categories

Resources