0

Problem with getSize() in AbstractListModel implementation [closed]

asked 2018-01-11 01:06:29 +0800

mhwcat gravatar image mhwcat
103 2

updated 2018-01-11 21:14:26 +0800

I'm implementing custom ListModel for dealing with large amounts of data (over 500k records) with paging and displaying in Grid. Code is similar to example in ZK docs.

Data is displayed correctly, however getSize() method in implementation is called many times (around 2 times total size of data) which I believe creates unnecessary overhead. For over 500k records I see over a million invocations of getSize(). My webapp is refreshing model for a couple of seconds to a minute.

I created simple example in ZK Fiddle, however it seems to be working fine there. My karma is insufficent to publish links so I will post code here (Fiddle id: 3ebc7jb/3-HugeListModelSizeTest)

index.zul

<zk>
<window border="normal" title="hello" apply="org.zkoss.bind.BindComposer" viewModel="@id('vm') @init('pkg$.TestViewModel')">
    <grid mold="paging" model="@bind(vm.items)" pageSize="@load(vm.pageSize)">
        <columns>
            <column label="ID"/>
            <column label="Name"/>
        </columns>
        <rows>
            <template name="model">
                <row>
                    <label value="@load(each.id)"/>
                    <label value="@load(each.name)"/>
                </row>
            </template>
        </rows>
    </grid>
    getSize() invoke counter: <label value="@bind(vm.sizeCounter)"/>
</window>

</zk> TestViewModel.java

public class TestViewModel {
    private ListModel<TestItem> items;
    private int pageSize = 10;

    @AfterCompose
    @NotifyChange("sizeCounter")
    public void afterCompose(@ContextParam(ContextType.VIEW) final Component view) {
        Selectors.wireComponents(view, this, false);

        items = new TestListModel(pageSize);
    }

    public ListModel<TestItem> getItems() {
        return items;
    }

    public int getPageSize() {
        return pageSize;
    }

    public long getSizeCounter() {
        if(items != null)
            return ((TestListModel) items).getSizeCounter();
        else
            return 0;
    }
}

TestListModel.java

public class TestListModel extends AbstractListModel<TestItem> {
    private List<TestItem> testItems;

    private int pageSize;
    private final Map<Integer, TestItem> cache;
    private int size = -1;

    private long sizeCounter;

    public TestListModel(final int pageSize) {
        super();

        generateItems(1000);

        this.pageSize = pageSize;
        this.cache = new HashMap<Integer, TestItem>();
    }

    @Override
    public TestItem getElementAt(final int index) {
        TestItem testItem = cache.get(index);
        if (testItem == null) {
            try {
                final List<TestItem> testItems = getTestItems(index, pageSize);
                int indexKey = index;

                for (final TestItem o : testItems) {
                    cache.put(indexKey, o);
                    indexKey++;
                }
            } catch (final Exception e) {

            } finally {

            }
        } else {
            return testItem;
        }

        testItem = cache.get(index);
        if (testItem == null) {
            throw new RuntimeException("Element at index " + index + " cannot be found in the database.");
        } else {
            return testItem;
        }
    }

    @Override
    public int getSize() {
        sizeCounter++;

        if (size < 0) {
            try {
                size = (int) getFullItemsSize();
            } catch (final Exception e) {

            } finally {

            }
        }

        return size;
    }

    private void generateItems(int size) {
        testItems = new ArrayList(size);

        Random rnd = new Random();

        for(int i = 0; i < size; i++) {
            testItems.add(new TestItem(i, java.util.UUID.randomUUID().toString()));
        }
    }

    private List<TestItem> getTestItems(int index, int pageSize) {
        List<TestItem> chunk = new ArrayList<TestItem>();

        int end;
        if((index + pageSize) > getFullItemsSize())
            end = getFullItemsSize();
        else
            end = index + pageSize;

        chunk = testItems.subList(index, end);

        return chunk;
    }

    private int getFullItemsSize() {
        return testItems.size();
    }

    public long getSizeCounter() {
        return sizeCounter;
    }
}

When runnning fiddle getSize() is invoked only a few times.

When I run this example on my local computer, getSize() is invoked few thousand times. I found other thread (can't post link so... id is 90700) describing similar problem, however it doesn't provide clear solution for this. I have read that ZK Fiddle uses EE version of ZK, but I also tried with *-Eval versions of ZK libs and results were the same.

I tested it with versions 7.0.3 (used in my project) and 8.5.0 (newest) with same results - working fine in ZK Fiddle, not working locally. (Screenshot on imgur)

Thanks for support.

EDIT: I updated fiddle (added "Refresh" button) to better show scenario that I have in my webapp. I see that with ROD enabled I get a lot less getSize() calls, but I cannot reproduce this behaviour locally :(. I'm including -Eval version of ZK libs and using exact same code as in fiddle with ROD enabled and still get a lot (2000+) of getSize() calls. Here is my example project (created from maven archetype with same code as in fiddle).

delete flag offensive retag edit

The question has been closed for the following reason "the question is answered, right answer was accepted" by cor3000
close date 2018-01-15 09:26:52

2 Answers

Sort by ยป oldest newest most voted
1

answered 2018-01-12 10:51:27 +0800

cor3000 gravatar image cor3000
6280 2 7

thanks for providing the source code... this makes it clear what's missing:

in order to activate the ZK EE features you have to add a dependency:

<dependency>
    <groupId>org.zkoss.zk</groupId>
    <artifactId>zkmax</artifactId>
    <version>${zk.version}</version>
</dependency>

After adding this dependency the getSize is called less often when I run your example.

link publish delete flag offensive edit

Comments

Thank you! That solves the problem.

mhwcat ( 2018-01-12 16:17:38 +0800 )edit

you're welcome

cor3000 ( 2018-01-15 09:26:32 +0800 )edit
1

answered 2018-01-11 15:19:32 +0800

cor3000 gravatar image cor3000
6280 2 7

updated 2018-01-11 15:21:16 +0800

Hi,

yes the reason is the same as in the old post: You have to use ZK EE(-Eval) with "Render on Demand" enabled (on ZK fiddle this is enabled by default via zk.xml configuration). Then the number of calls gets reduced:

You can observe the opposite effect: When disabling ROD as in your updated (by me) example the number of calls will increase.

Robert

link publish delete flag offensive edit

Comments

Hello, thanks for your reply - ROD indeed helps but I cannot reproduce it locally. Please see my edit.

mhwcat ( 2018-01-11 21:15:06 +0800 )edit

Question tools

Follow
2 followers

RSS

Stats

Asked: 2018-01-11 01:06:29 +0800

Seen: 22 times

Last updated: Jan 12 '18

Support Options
  • Email Support
  • Training
  • Consulting
  • Outsourcing
Learn More