Reduce the complexity from code - java

I am using below code and used two continue statement depending on some logic but sonar list showing this issue Reduce the total number of break and continue statements in this loop to use at most one.
How to resolve this issue?
for (HashMap<String, String> objRequestIdVO : pObjTicketId) {
List<TicketDetailsDO> objTicketDetailslist = storeManagerDao.getTicketDetailsWithTicketId(objRequestIdVO.get("requestId"));
if (null == objTicketDetailslist || objTicketDetailslist.isEmpty()) {
continue;
}
Integer iDesiredDsicount = objTicketDetailslist.get(0).getDesiredDiscount();
String iSubDept = objTicketDetailslist.get(0).getSubdeptTicket().getSubDeptId();
List<MCouponDO> objMCounponList = storeManagerDao.getMcouponData(iDesiredDsicount, iSubDept);
if (null == objMCounponList || objMCounponList.isEmpty()) {
continue;
}
String strHeader = objMCounponList.get(0).getHeader();
objHeaderVO = new HeaderVO();
objHeaderVO.setHeader(strHeader);
objHeaderVO.setRequestId(objRequestIdVO.get("requestId"));
objHeaderVOList.add(objHeaderVO);
}

Change the null check continue, to not null check and proceed. The code will be executed only if the not null check passes, which is same as saying continue if null.
for (HashMap<String, String> objRequestIdVO : pObjTicketId) {
List<TicketDetailsDO> objTicketDetailslist = storeManagerDao.getTicketDetailsWithTicketId(objRequestIdVO.get("requestId"));
if (!(null == objTicketDetailslist || objTicketDetailslist.isEmpty())) {
Integer iDesiredDsicount = objTicketDetailslist.get(0).getDesiredDiscount();
String iSubDept = objTicketDetailslist.get(0).getSubdeptTicket().getSubDeptId();
List<MCouponDO> objMCounponList = storeManagerDao.getMcouponData(iDesiredDsicount, iSubDept);
if (!(null == objMCounponList || objMCounponList.isEmpty()) {
String strHeader = objMCounponList.get(0).getHeader();
objHeaderVO = new HeaderVO();
objHeaderVO.setHeader(strHeader);
objHeaderVO.setRequestId(objRequestIdVO.get("requestId"));
objHeaderVOList.add(objHeaderVO);
}
}
}

You could use streams replacing the continues with filters.
pObjTicketId.stream()
.map(m-> m.get("requestId"))
.map(reqId ->
Optional.ofNullable(storeManagerDao.getTicketDetailsWithTicketId(reqId))
.filter(l->!l.isEmpty())
.map(l->l.get(0))
.map(ticketDetails->
storeManagerDao.getMcouponData(ticketDetails.getDesiredDiscount(),
ticketDetails.getSubdeptTicket().getSubDeptId())
)
.filter(Objects::nonNull)
.filter(l->!l.isEmpty())
.map(l->l.get(0))
.map(couponDo-> {
HeaderVO headerVO = new HeaderVO();
headerVO.setHeader(couponDo.getHeader());
headerVO.setRequestId(oreqId);
return headerVO;
})
)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());

You have a bigger problem than fixing Sonar warning if calls - storeManagerDao.getTicketDetailsWithTicketId(objRequestIdVO.get("requestId")) & storeManagerDao.getMcouponData(iDesiredDsicount, iSubDept) are about making DB calls.This is a big performance point and one should - Never ever make DB calls from within a loop, this is far more dangerous than multiple continue & break statements
So I would first restructure your DAO call - storeManagerDao.getTicketDetailsWithTicketId to run IN sql query for a bunch of objRequestIdVO.get("requestId") in one go , outside your main loop & produce a Map<String,List<TicketDetailsDO>> ...that will automatically get rid of your first if .
Next you repeat same process for constructing a Map<String,List<MCouponDO> objMCounponList> by iterating previous map Map<String,List<TicketDetailsDO>> where key of this map is something like - iDesiredDsicount|iSubDept .
This way you will have two disconnected loops and only two DB calls & your Sonar warning gets automatically solved on the way.

Related

Optimize use of Streams

boolean isRoleOld,isRoleNew;
for (Relations relation : listOfRelations)
{
if (Constants.ROLE_OLD.equalsIgnoreCase(relation.getRole()))
{
isRoleOld = true;
}
if (Constants.ROLE_NEW.equalsIgnoreCase(relation.getRole()))
{
isRoleNew = true;
}
}
if (isRoleOld && isRoleNew)
{
“Success”
}else{
throw Exception();
}
What i have done yet is
if (listOfRelations.stream()
.anyMatch(relation -> Constants.ROLE_OLD.equalsIgnoreCase(relation.getRole()))
&&
listOfRelations.stream()
.anyMatch(relation -> Constants.ROLE_NEW.equalsIgnoreCase(relation.getRole())))
{
System.out.println("Success");
}
How to use streams from Java8 to optimize this code. Using a anymatch twice is not the point.
Any help will be appreciated.
You could use a stream to map to each Role, and filter to identify old/new role match, then count the distinct matches.
long count = listOfRelations.stream().map(Relations::getRole)
.filter(role -> Constants.ROLE_OLD.equalsIgnoreCase(role)
|| Constants.ROLE_NEW.equalsIgnoreCase(role))
.map(String::toLowerCase)
.distinct().count();
if (count != 2) {
throw new Exception();
}
System.out.println("Success");
However although this does only one pass through the stream it does not exit early once matched both roles so won't be ideal for large data-sets.
Actually, the solution with two .anyMatch checks is better because of short-circuiting when the required value is matched and therefore these checks may usually complete "sooner" than a full single run over the entire stream. In case ROLE_OLD value is missing, && short-circuiting occurs and no check for ROLE_NEW is executed. The worst case is when ROLE_OLD is located at the end of the input listOfRelations, and no ROLE_NEW is there.
Similar loop-based solution would use break as soon as both ROLE_OLD and ROLE_NEW have been detected; in the worst case, the collection is fully iterated one time.

How to cleanly process java 8 stream "findFirst()" result even if empty

One area that I often finding confusing with java 8 streams is when an intermediate result can be empty, and you need to take alternate paths if it's empty or not empty.
For instance, if I have code like this:
String pymtRef = defaultValue;
Optional<PaymentTender> paymentTender = paymentTenders.stream()
.filter(pt -> (pt.getFlag() == Flag.N || pt.getFlag() == null)).findFirst();
if (paymentTender.isPresent()) {
pymtRef = paymentTender.get().getId();
}
return pymtRef;
I would like to figure out how to remove the conditional block and do this in a single stream.
If I simply call ".map" on the filter result, that can work if it found a matching entry. If not, I get a NoSuchElementException.
I might instead use "ifPresent()", but the return type of that is "void".
Is there any way to make this cleaner?
Update:
The solution using "orElse()" works fine.
The entire method now looks something like this:
public String getPaymentReference(OrderContext orderContext) {
List<PaymentTender> paymentTenders = getPaymentTenders(orderContext);
if (paymentTenders.size() == 1) {
return paymentTenders.get(0).getId();
}
return paymentTenders.stream()
.filter(pt -> (pt.getAutoBill() == AutoBill.N || pt.getAutoBill() == null))
.findFirst().map(pt -> pt.getId()).orElse(DEFAULT_VALUE);
}
Can you think of a way to include the first conditional in the stream without making it more complex?
Calling get() straight after map will yield an exception if the Optional has an empty state, instead call orElse after map and provide a default value:
paymentTenders.stream()
.filter(pt -> (pt.getFlag() == Flag.N || pt.getFlag() == null))
.findFirst()
.map(PaymentTender::getId)
.orElse(someDefaultValue);
Edit:
As for:
Can you think of a way to include the first conditional in the stream
without making it more complex?
No, this is better the way you've done it. it's more readable and easier to follow.
introducing any type of logic to make it into one pipeline (if possible) will just end of being complex and hence harder to follow and understand.
You can do it in one statement via
public String getPaymentReference(OrderContext orderContext) {
List<PaymentTender> paymentTenders = getPaymentTenders(orderContext);
return paymentTenders.stream()
.filter(paymentTenders.size() == 1? pt -> true:
pt -> pt.getAutoBill() == AutoBill.N || pt.getAutoBill() == null)
.findFirst().map(PaymentTender::getId).orElse(DEFAULT_VALUE);
}
Note that this will not repeat the evaluation of the paymentTenders.size() == 1 for every element, but use a different function, depending on the state. When the condition is fulfilled, pt -> true will accept any element, which will result in the sole element being accepted as intended. Otherwise, the ordinary predicate, pt -> pt.getAutoBill() == AutoBill.N || pt.getAutoBill() == null is used.

Issue in loop getting the value

I am having an issue in getting the data in the below loop ,even though the size is not zero i am getting null in the sysout 'data is'.What is wrong there ?
List<Long> dd = domainItemMapper.getIsSearchable(34372);
System.out.println("the test is-" + dd.size());
for (int i = 0; i < dd.size(); i++) {
Long isSearch = dd.get(i);
System.out.println("data is"+dd.get(i));
if (isSearch.equals(0)) {
isSearchValue = false;
} else
isSearchValue = true;
}
The call to database is a mybatis call as below
interface
List<Long> getIsSearchable(#Param("parentFieldId") long parentFieldId);
impl
<mapper namespace="com.ge.dbt.common.persistence.IFormValidatorMapper">
<select id="getIsSearchable" statementType="CALLABLE"
resultType="Long">
select is_searchable from t_field where parent_field_id=#{parentFieldId}
</select>
</mapper>
I guess your whole code can be converted in to two lines.
if(dd!= null && !dd.isEmpty())
return dd.contains(0);//Contains Guard you in such cases because equals check
//happen on passed element ie *0*
Default value of Long in java is null. So you will need additional check for null in your case.
Enclose your isSearch.equals check in a null check
if(isSearch != null)
{
if (isSearch.equals(0))
{
isSearchValue = false;
}
else
{
isSearchValue = true;
}
}
However it'll be better to modify code for domainItemMapper.getIsSearchable(34372); method so that it doesn't fill the list with null at all.
Seems to be your list contains null literals. And List Supports null as a value.
based on your this comment
The data has null,0 and 1 data.I want to return data only for 0 and 1.
You need to fix your query like this
select is_searchable from t_field where parent_field_id=#{parentFieldId} and is_searchable is not NULL;

LinkedList Iteration Exception

I am struggling with this error. I feel its really simple but cannot understand why am getting the error.
I keep getting a NullPointerException when I iterate through values in my linked list.
Code Snippet:
private void updateBuyBook(LimitOrder im) {
LimitOrder lm = null;
Iterator itr = buyBook.entrySet().iterator();
boolean modify = false;
while (itr.hasNext() && !modify) {
Map.Entry pairs = (Map.Entry) itr.next();
if ((((LinkedList<IMessage>) pairs.getValue()).size() > 0)) {
LinkedList<ILimitOrder> orders = (LinkedList<ILimitOrder>) pairs
.getValue();
ListIterator listIterator = orders.listIterator();
while (listIterator.hasNext() && !modify) {
LimitOrder order = (LimitOrder) listIterator.next();
if (order.getOrderID().equalsIgnoreCase(im.getOrderID())) { // error at this line
lm = order;
addToBuyMap(im);
modify = true;
break;
}
}
if (modify = true) {
orders.remove(lm);
break;
}
}
}
}
Error is at this line:
Exception in thread "main" java.lang.NullPointerException
order.getOrderID().equalsIgnoreCase(im.getOrderID()));
Please help. Is my assignment wrong in any way???
Please help!!!
Thanks
Changing your code a bit will make it longer, but much easier to find the error... instead of doing:
if (order.getOrderID().equalsIgnoreCase(im.getOrderID())) {
Change it to:
String orderID = order.getOrderID();
String imOrderID = im.getOrderID();
if(orderID.equals(imOrderID()) {
Then you will know if order or im is null. If neither of those is null then the things that could be null are orderID and imOrderID. It is now a simple matter of finding out which one of those is null.
If it is order or im then the program will crash on the order.getOrderID() or im.getOrderID() lines.
If, instead it is orderID or imOrderID that is null, then it will crash on if(orderID.equals(imOrderID()) {. You can then use System.out.println (or something better, like a debugger) do easily find out what is wrong.
If neither of those should be null then I suggest adding something like:
if(orderID == null) { throw new IllegalStateException("orderID cannot be null"); }
if(imOrderID == null) { throw new IllegalStateException("imOrderID cannot be null"); }
and then track down how it got set to null to begin with.
My guess would be that you're passing in the null, into im. I'll need to see more of the code to be sure.
You never check to see if im is null. I suspect it is.
One first look, where is im instantiated? You can also try to debug in your IDE so as to see whats going on?
Either im is null or order.getOrderID() is returning null.
Doesn't look like im is ever declared / assigned. So
im.getOrderID()
is probably where the null pointer exception is generated.
-- Dan
Edit:
Missed that im is passed in as an argument. So that leaves a few possibilities (in order of likelihood):
im is null (ie. user called function with null parameter)
order.getOrderID() is returning null
order is null (ie. the list has nulls in it)
Edit2:
Your line
if (modify = true)
Is fundamentally wrong and will always evaluate to true (single equal is for assignment, == is for comparison.)
When simply checking if a flag boolean is true or false, it is best to use:
boolean flag = true;
if(flag)
{
// True block
}
else
{
// False block
}
It will be good if you could add a debug point before that line and see which variable is null. looking at the code the answer is 1. order is null 2. order.getOrderID() is null or 3. im is null

Extract Method with continue

We're refactoring a long method; it contains a long for loop with many continue statements. I'd like to just use the Extract Method refactoring, but Eclipse's automated one doesn't know how to handle the conditional branching. I don't, either.
Our current strategy is to introduce a keepGoing flag (an instance variable since we're going to want to extract method), set it to false at the top of the loop, and replace every continue with setting the flag to true, then wrapping all the following stuff (at different nesting levels) inside an if (keepGoing) clause. Then perform the various extractions, then replace the keepGoing assignments with early returns from the extracted methods, then get rid of the flag.
Is there a better way?
Update: In response to comments - I can't share the code, but here's an anonymized excerpt:
private static void foo(C1 a, C2 b, C3 c, List<C2> list, boolean flag1) throws Exception {
for (int i = 0; i < 1; i++) {
C4 d = null;
Integer e = null;
boolean flag2 = false;
boolean flag3 = findFlag3(a, c);
blahblahblah();
if (e == null) {
if (flag1) {
if (test1(c)) {
if (test2(a, c)) {
Integer f = getF1(b, c);
if (f != null)
e = getE1(a, f);
if (e == null) {
if (d == null) {
list.add(b);
continue;
}
e = findE(d);
}
} else {
Integer f = getF2(b, c);
if (f != null)
e = getE2(a, f);
if (e == null) {
if (d == null) {
list.add(b);
continue;
}
e = findE(d);
}
flag2 = true;
}
} else {
if (test3(a, c)) {
Integer f = getF2(b, c);
if (f != null)
e = getE2(a, f);
if (e == null) {
if (d == null) {
list.add(b);
continue;
}
e = findE(d);
}
flag2 = true;
} else {
if (d == null) {
list.add(b);
continue;
}
e = findE(d);
flag2 = true;
}
}
}
if (!flag1) {
if (d == null) {
list.add(b);
continue;
}
e = findE(d);
}
}
if (e == null) {
list.add(b);
continue;
}
List<C2> list2 = blahblahblah(b, list, flag1);
if (list2.size() != 0 && flag1) {
blahblahblah();
if (!otherTest()) {
if (yetAnotherTest()) {
list.add(b);
continue;
}
blahblahblah();
}
}
}
}
This is one of those fun ones where no single pattern will get you there.
I would work at it iteratively.
First I'd try to see if I couldn't use an early continue to remove one of those levels of ifs. It's much clearer code to check for a condition and return early (or in your case continue) than to have deeply nested ifs.
Next I think I'd take some of the inner chunks and see if they couldn't be extracted into a separate method. It looks like the first two big blocks (within the "if (test2(a, c)) {" and its else statement) are very similar. There is cut and paste logic that should be the same.
Finally after that stuff is cleared up, you can start looking at your actual problem--you need more classes. This entire statement is probably a three line polymorphic method in 3-5 sibling classes.
It's very close to throw-away and rewrite code, once you identify your actual classes, this entire method will vanish and be replaced with something so simple it hurts. Just the fact that it's a static utility method should be telling you something--you don't want one of those in this type of code.
Edit (After looking a little more):
There is so much here it would be really fun to go through. Remember that when you are done you want no code duplication--and I'm pretty sure this entire thing could be written without a single if--I think all your ifs are cases that could/should easily be handled by polymorphism.
Oh, and as an answer to your question of eclipse not wanting to do it--don't even TRY automatic refactoring with this one, just do it by hand. The stuff inside that first if() needs to be pulled out into a method because it's virtually identical to the clause in its else()!
When I do something like this, I usually create a new method, move the code from the if into the new method (leaving just a call to the new method inside the if), then run a test and make sure you didn't break anything.
then go line by line and check to ensure there is no difference between the if and its else code. If there is, compensate for it by passing the difference as a new variable to the method. After you're sure everything is identical, replace the else clause with a call. Test again. Chances are at this point a few additional optimizations will become obvious, you'll most likely lose the entire if by combining it's logic with the variable you passed to differentiate the two calls.
Just keep doing stuff like that and iterating. The trick with refactoring is to use Very Small Steps and test between each step to ensure nothing changed.
continue is basically an analogue of an early return, right?
for (...) {
doSomething(...);
}
private void doSomething(...) {
...
if (...)
return; // was "continue;"
...
if (!doSomethingElse(...))
return;
...
}
private boolean doSomethingElse(...) {
...
if (...)
return false; // was a continue from a nested operation
...
return true;
}
Now I must admit that I didn't quite follow your current strategy, so I might have just repeated what you said. If so, then my answer is that I can't think of a better way.
If I were faced with your situation I would look at using other refactoring techniques such as "replace conditional with polymorphism". That said you should always do one thing at a time, so if you first want to extract method you have two options:
Add the "keepGoing" flag
Throw an exception from the method
Of these two options, I think the keepGoing flag is better. I wouldn't stop refactoring after you extract the method. I am sure once you have a smaller method you will find a way to remove this flag and have cleaner logic.
I'm going to summarize the answers here, while accepting Bill K's answer as the most complete. But everyone had something good to offer, and I might use any of these approaches next time I'm faced with this sort of situation.
mmyers: Cut out the loop body, paste it into a new method and replace all the continues with returns. This worked very nicely, although it would have trouble if there were other control flow statements, like break and return, inside the loop.
Bill K: Tease it apart iteratively; look for duplication and eliminate it. Take advantage of polymorphic classes to replace the conditional behavior. Use Very Small Steps. Yes; this is all good advice, with broader applicability than just this specific case.
Aaron: Either use the keepGoing flag to replace the continue or throw an Exception. I didn't try this, but I think the Exception option is a very nice alternative, and one I hadn't considered.

Categories

Resources