Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to populate an ArrayList until you reach a specified max number of items with Java 8 Streams?

I'm new to Java 8 and I'm studying streams. I'm trying to populate an ArrayList of Messages that has a date after a specified date. I need that this new ArrayList has a maximum of 16 items. I tried the following:

private static final int MAX_MESSAGES_NUM = 16;

public ArrayList<Messages> filterMessagesByData(Calendar filterDate, ArrayList<Messages> messagesList) {
    ArrayList<Messages> filteredMessages = new ArrayList<Messages>();
    int msgCount = 0;
    messagesList.stream().filter(message -> {

        Calendar msgDate = new GregorianCalendar();
        try {
            msgDate.setTime(new SimpleDateFormat("dd/MM/yy").parse(message.getDate()));
            msgCount ++;
        } catch (ParseException e) {
            e.printStackTrace();
            throw new RuntimeException();
        }
        return (msgDate.compareTo(filterDate) >= 0) && msgCount < MAX_MESSAGES_NUM;
    }).forEach(filteredMessages::add);

    return filteredMessages;
}

but it gives me an error at line msgCount++:

Local variable msgCount defined in an enclosing scope must be final or effectively final.

I suspect that external variables can't be modified in a lambda expression.

Is there a way that it can accomplished using streams and filters?

like image 445
Fabio Lapozyk Avatar asked Feb 08 '23 23:02

Fabio Lapozyk


1 Answers

You can use Stream.limit(maxSize) to limit the number of elements in the Stream:

public List<Messages> filterMessagesByData(Calendar filterDate, ArrayList<Messages> messagesList) {
    return messagesList.stream().filter(message -> {
        Calendar msgDate = Calendar.getInstance();
        try {
            msgDate.setTime(new SimpleDateFormat("dd/MM/yy").parse(message.getDate()));
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
        return msgDate.compareTo(filterDate) >= 0;
    }).limit(MAX_MESSAGES_NUM).collect(Collectors.toList());
}

I changed a couple of things in your initial code:

  • new GregorianCalendar() was replaced with Calendar.getInstance().
  • Instead of using forEach, you can directly collect all the elements in a list using Collectors.toList()
  • cleaned up exception handling so the original exception is saved as a cause, making writing the stacktrace to stderr unnecessary.
like image 199
Tunaki Avatar answered Feb 11 '23 11:02

Tunaki