0

I am trying to update a property of bean (if it satisfies a condition) while iterating over list of it. To enhance the performance I am using Lists.transform

private void populate(final WorkFlowDataBean workFlowDataBean, final List<ApplicationDataBean> items) {
        Lists.transform(items, new Function<ApplicationDataBean, ApplicationDataBean>() {
            @Override
            public ApplicationDataBean apply(ApplicationDataBean input) {
                if (String.valueOf(workFlowDataBean.getId().intValue()).equalsIgnoreCase(String.valueOf(input.getWorkflowId().intValue()))) {
                    input.setWorkflowName(workFlowDataBean.getName());
                    input.setWorkflowVersion(workFlowDataBean.getVersion());
                    logger.info("---finally----");
                }
                logger.info(String.valueOf(workFlowDataBean.getId().intValue()) + "===" + String.valueOf(input.getWorkflowId().intValue()));
                return input;
            }
        });

    }

I cannot use foreach loop as it will slow down my application.

Shruti Rawat
  • 687
  • 6
  • 11
  • 24
  • 1
    Does it not work for you? There is no obvious question here. – Mick Mnemonic Mar 26 '15 at 11:44
  • no :( . I am still stuck here – Shruti Rawat Mar 26 '15 at 11:46
  • 2
    Sorry, I don't understand the problem. Lists.transform() needs to iterate over the list in any case. Guava's functional idioms should be used for improving fluency or readability; they don't necessarily (ever?) improve performance when compared to a normal imperative for loop. What error are you getting? – Mick Mnemonic Mar 26 '15 at 11:56
  • the list of items here contains >10000 beans. if i do a foreach loop it takes approx 2-3 min. In our application whenever we can we avoid foreach loop and use iterables/filter etc guava utilities which serve the purpose and save a lot of time. But in this scenario i have just used transform to iterate over a list and avoided using foreach. – Shruti Rawat Mar 26 '15 at 11:58
  • What I am trying to say is that transform() anyways needs to iterate through the whole list, so I cannot see how it would improve performance. The operations in the Lists utility usually involve manipulating the whole collection, whereas using an Iterable, you might not even know or care about the underlying data container as a whole. – Mick Mnemonic Mar 26 '15 at 12:07
  • 2
    Minor correction: `Lists.transform()` will actually return a lazily-computed view of the list, which means that everything that you have in `apply()`, only happens when you start iterating the transformed collection. So you cannot use the method in the way you're using it now, because `apply()` never gets called because you're not using the transformed List for anything. – Mick Mnemonic Mar 26 '15 at 12:35
  • @ShrutiRawat: if you're doing **anything** in the name of improving performance, you need to accurately measure the same thing before and after. You really should read this: [How do I write a correct microbenchmark in Java?](http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java) – Daniel Pryden Mar 26 '15 at 20:16

3 Answers3

3

"I cannot use foreach loop as it will slow down my application" is just wrong here. Trying to use Lists.transform will not help you at all because A) it doesn't do what you seem to think it does, B) it's not the right way to do what you're trying to do (a for-each loop is), and C) you have to iterate over the List returned by Lists.transform, which will not be faster than a for-each loop. It seems like perhaps you think it's faster because your code is not actually doing anything.

If you really want to make this faster, you'll probably want to look into parallelizing the operation.

ColinD
  • 108,630
  • 30
  • 201
  • 202
2

Lists.transform() will actually return a lazily-computed view of the list, which means that everything that you have in apply(), only happens when you start iterating the transformed collection. So you cannot use the method in the way you're using it now, because apply() never gets called if you're not using the transformed List for anything.

For working around the problem you could try creating a new list (which you also discard) based on the transformed collection:

Lists.newArrayList(Lists.transform(
    /*Your transformation code*/
));

A better longer term solution would be to re-think the method overall. That is, you should simply use a for-each loop in the method and not wrap the code into a function. Normal, imperative code will be easier to read and also perform better in this case.

Mick Mnemonic
  • 7,808
  • 2
  • 26
  • 30
  • Thanks... I tried in debug mode of eclipse. Strange thing is happening. When i m debugging it line by line my beans get populated and i am getting desired result. But whhen i run it in one go no population happens.. – Shruti Rawat Mar 27 '15 at 01:21
1

As others have said, the returned List is not the actual transformed list, but a view of the original List that, when iterated later, will apply the transformation to each element.

Regarding the error, you have to return the transformed list. That's why your code is not working:

private List<ApplicationDataBean> populate(final WorkFlowDataBean workFlowDataBean, final List<ApplicationDataBean> items) {
    return Lists.transform(items, new Function<ApplicationDataBean, ApplicationDataBean>() {
        @Override
        public ApplicationDataBean apply(ApplicationDataBean input) {
            if (String.valueOf(workFlowDataBean.getId().intValue()).equalsIgnoreCase(
                String.valueOf(input.getWorkflowId().intValue()))) {
                input.setWorkflowName(workFlowDataBean.getName());
                input.setWorkflowVersion(workFlowDataBean.getVersion());
                logger.info("---finally----");
            }
            logger.info(String.valueOf(workFlowDataBean.getId().intValue()) + "==="
                + String.valueOf(input.getWorkflowId().intValue()));
            return input;
        }
    });
}

Then, invoke this method and iterate over the returned list:

List<ApplicationDataBean> filteredTransformedList = populate(workFlowDataBean, items);

for (ApplicationDataBean bean : filteredTransformedList) {
    // do useful things with every filtered and transformed bean
}

Regarding using imperative vs functional approach, just use whatever you think is the best. This pseudo-functional approach has the advantage that it's applied lazily, i.e. when the returned list is finally iterated. You should note, however, that, as you're modifying the ApplicationDataBean input element directly, elements in the original list will be modified as well.

fps
  • 33,623
  • 8
  • 55
  • 110
  • Thanks... I tried in debug mode of eclipse. Strange thing is happening. When i m debugging it line by line my beans get populated and i am getting desired result. But whhen i run it in one go no population happens.. – Shruti Rawat Mar 27 '15 at 01:21
  • @ShrutiRawat You have to assign the returned list to a variable: `List result = populate(workFlowDataBean, items);` and then iterate over this returned list. – fps Mar 27 '15 at 01:29