简体   繁体   中英

How to convert for loop iterating n times to achieve a list using java Streams API with multiple counts changing dynamically

Want to use Streams API and remove current for loop in code snippet below :

public List<MyObject> performSomeAction(){
    List<String> myList= Arrays.asList("abc","xyz","def");
    List<MyObject> resultList=new ArrayList<MyObject>();
    int startIndex=0;
    int totalNumOfRecords=1000; 
    for(int i=0;i<totalNumOfRecords;i++){
        SomeObject o= xService.doSomething();
        String type = o.getType();
        int length = o.getLength();
        if(myList.contains(type)){
            int[] myarray=getMyArray(startIndex+20, startIndex+length);
            String id=o.getSomeId();
            OtherObject obj= xService.performOperation(myarray,"abcd");
            resultList.add(new MyObject(obj,id));
        }
        startIndex+=length;
    }
    return resultList;
}

I tried something like this :

IntStream.range(0,totalNumOfRecords).forEach(i -> xService.doSomething());

but i am stuck and not getting how to proceed further, need to take multiple values from derived object 'o' and use it to perform filter on top of it and again make another method call get some array which will be passed to other method to return other object that i need to create my resultList

This line:

startIndex+=length;

Means that the behaviour of the next iteration depends upon the state after the current iteration.

That basically means that the iteration has to happen serially. As such, there is no benefit from crowbarring Streams in here.

Just stick with your existing code.


I have seen many questions where OP is asking how to rewrite code to use streams. The implicit assumption is that streams are superior to loops , and so streams should always be used in preference to a loop.

This is not true .

In comparison to a loop, a stream is:

  • More restrictive: you cannot use flow control like break , continue ; reassign local variables from the surrounding context; throw checked exceptions; return from the containing method; loop over certain primitive types.
  • More heavyweight: you create lots of objects in constructing the stream
  • Less structured: streams using flat mapping are harder to read than nested loops, IMO
  • Less understood: my experience is that many people see streams in code, and assume that they are hard to understand and a bit magical. This will change over time as they become more comfortable with what is still a relatively-new API; but to an extent, they are correct: the streams API is large, Java-specific, and offers numerous ways to shoot yourself in the foot. (Basic) loops, on the other hand, are effectively the same syntax as in all C-like languages: once you've learned it in one, you immediately "get" what the loop is doing in Java. Even enhanced for loops are basically the same as in C++, Python etc.

However, a stream can be:

  • More concise: sometimes you can express the "loop body" as a lambda or method reference, which can be shorter.
  • Parallelized trivially: although Effective Java 3rd Ed has an item about why this feature should be used only with great care.

Streams are a tool, with limited benefits and numerous drawbacks. Rewriting existing, working code to use them is really not a good idea.

Just putting it here, so there's an example on how to do it


You could use a custom intermediate class with reduction for this:

public List<MyObject> performSomeAction(){
    List<String> myList = Arrays.asList("abc","xyz","def");

    class ReductionHelper {
         int startIndex;
         final List<MyObject> resultList = new LinkedList<>();
    }

    return Stream.generate(xService::doSomething)
        .limit(1000)
        .reduce(new ReductionHelper(), (helper, o) -> {
            int length = o.getLength();
            if(myList.contains(o.getType()){
                int[] myarray=getMyArray(helper.startIndex+20, helperstartIndex+length);
                String id=o.getSomeId();
                OtherObject obj= xService.performOperation(myarray,"abcd");
                helper.resultList.add(new MyObject(obj,id));
            }
            helper.startIndex += length;
            return helper;
        }, (left, right) -> {
            left.resultList.addAll(right.resultList);
            return left;
        })
        .resultList; // return just the result list
}

This just uses the class ReductionHelper to hold the resultList and the startIndex. But it is highly messy

So as you can see, this is in no way more readable than your current solution. Just stick with normal for loops as pointed out by @Andy Turners answer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM