87

I have a JSF page which displays list of Glassfish log files. I use lazy loading for pagination. I keep the list of the log files names into Java List.

private List<directoryListObj> dataList = new ArrayList<>();

dataList = dataList.subList(firstRow, lastRow);

And here is the problem. For example I have 35 files into the directory. When I do this

dataList = dataList.subList(5, 15);

It works fine. But when I do this:

dataList = dataList.subList(30, 38);

I get error wrong index because I want to get index outside of the List. How I can for example return List elements from 30 to 35? I want if I want to get index from 30 to 40 but if there only 35 indexes to get only 5.

user1285928
  • 1,328
  • 29
  • 98
  • 147
  • 14
    `dataList = dataList.subList(30, 38 > dataList.size() ? dataList.size() : 38);` – pb2q Aug 23 '12 at 20:49
  • 20
    Or `dataList.subList(30, Math.min(38, dataList.size())` – Ismail Badawi Aug 23 '12 at 20:50
  • 1
    @pb2q looks like you've lost the chance for an answer. – Luiggi Mendoza Aug 23 '12 at 20:50
  • If you look at the [APIdoc](http://docs.oracle.com/javase/6/docs/api/java/util/AbstractList.html#subList%28int,%20int%29) you'll notice that it throws `IndexOutOfBoundsException - endpoint index value out of range (fromIndex < 0 || toIndex > size)`. So look at the APIdoc for such hints in the future. – zeller Aug 23 '12 at 20:51

6 Answers6

106

Using subList(30, 38); will fail because max index 38 is not available in list, so its not possible.

Only way may be before asking for the sublist, you explicitly determine the max index using list size() method.

for example, check size, which returns 35, so call sublist(30, size());

OR

COPIED FROM pb2q comment

dataList = dataList.subList(30, 38 > dataList.size() ? dataList.size() : 38);
Mikael Auno
  • 8,990
  • 2
  • 21
  • 16
kosa
  • 65,990
  • 13
  • 130
  • 167
52

I've implemented and tested this one; it should cover most bases:

public static <T> List<T> safeSubList(List<T> list, int fromIndex, int toIndex) {
    int size = list.size();
    if (fromIndex >= size || toIndex <= 0 || fromIndex >= toIndex) {
        return Collections.emptyList();
    }

    fromIndex = Math.max(0, fromIndex);
    toIndex = Math.min(size, toIndex);

    return list.subList(fromIndex, toIndex);
}
Haroldo_OK
  • 6,612
  • 3
  • 43
  • 80
  • 5
    This should be the accepted answer, the only one who bothered to test their code. – Ed Randall Jun 17 '16 at 08:50
  • 2
    Very helpful, thanks. Breaking this out into a method makes more sense than trying to wrap up the logic in ternary operators, it just gets too hard to read that way. – Steve Nov 22 '17 at 10:55
43

To get the last element, simply use the size of the list as the second parameter. So for example, if you have 35 files, and you want the last five, you would do:

dataList.subList(30, 35);

A guaranteed safe way to do this is:

dataList.subList(Math.max(0, first), Math.min(dataList.size(), last) );
grbonk
  • 609
  • 6
  • 22
Joe K
  • 18,204
  • 2
  • 36
  • 58
14

You could use streams in Java 8. To always get 10 entries at the most, you could do:

dataList.stream().skip(5).limit(10).collect(Collectors.toList());
dataList.stream().skip(30).limit(10).collect(Collectors.toList());
Stefan Haberl
  • 9,812
  • 7
  • 72
  • 81
3

Although it may be too late, I suggest using stream in Java 8 to avoid handling the out of boundry exception manually.

this code

dataList = dataList.stream().skip(30).limit(8).collect(Collectors.toList());

works same as

dataList = dataList.subList(30, 38);

and in case of out of index error, dataList will be empty list instead of throwing any exception.

hao
  • 639
  • 5
  • 11
-1

For kotlin, you could use: myList.subList(min, max.coerceAtMost(myList.size)

Or in terms of the question above: myList.subList(30, 38.coerceAtMost(myList.size)

Tony
  • 2,242
  • 22
  • 33