3

Reading through this article, I can filter the collection like this:

Set<Status> statusClone = cloner.deepClone(statusList).stream().filter(s -> !(s instanceof FileMetadata)).collect(Collectors.toSet());

However, I will also need to set the properties simultaneously filtering them. After filtering them, I currently iterate through each one and then set properties:

for (Iterator<Status> iterator = statusClone.iterator(); iterator.hasNext();)
        {
            Status s = iterator.next();
//          if (s instanceof FileMetadata)
//          {
//              iterator.remove();
//              continue;
//          }
            s.setStatus(JobStatus.UNINITIATED);
            s.setLastAccessedTime(0);
            s.setOffSet(null);
            s.setStreamNo(null);
        }
        statusList.addAll(statusClone);

Is this possible in Java8 without using foreach?

EDIT: From the comments, I agree that I can clone inside the filter. Thanks.

dmachop
  • 824
  • 1
  • 21
  • 39
  • 2
    Problem is that your code isn't very functional (you do not have an immutable Status for one) so it's awkward to write it using Stream. – Tunaki Oct 02 '15 at 15:48
  • If you are going to set the same values again and again for different objects, put that in a new method and call the method from the stream. – We are Borg Oct 02 '15 at 15:50
  • Potential duplicate: http://stackoverflow.com/questions/16635398/java-8-iterable-foreach-vs-foreach-loop – Phylogenesis Oct 02 '15 at 15:52
  • Thanks but I wanted to know to use without foreach on the collection. I'm iterating through the collect again. If this is the case, then I could avoid lambdas and use a conventional for loop using an iterator. – dmachop Oct 02 '15 at 15:53
  • 2
    You are on the right track when you say that you could avoid using lambas and use a conventional for loop, however, you don’t need to deal with an `Iterator`. Use `for(Status s: statusClone) { /* manipulate s */ }`. You can use this since Java 5… – Holger Oct 02 '15 at 16:36

2 Answers2

6

You could use peek:

private static void initStatus(Status s) {
  s.setStatus(JobStatus.UNINITIATED);
  s.setLastAccessedTime(0);
  s.setOffSet(null);
  s.setStreamNo(null);
}

Set<Status> statusClone = cloner.deepClone(statusList).stream()
                  .filter(s -> !(s instanceof FileMetadata))
                  .peek(MyClass::initStatus)
                  .collect(Collectors.toSet());

Note the javadoc warning that it could create issues if you use a parallel stream because you are modifying state.


It would be better to initialise the status directly when cloning the original list - something like:

private static Status cloneAndInitStatus(Status s) {
  Status clone = new Status(s);
  clone.setStatus(JobStatus.UNINITIATED);
  clone.setLastAccessedTime(0);
  clone.setOffSet(null);
  clone.setStreamNo(null);
  return clone;
}

Set<Status> statusClone = statusList.stream()
                  .filter(s -> !(s instanceof FileMetadata))
                  .map(MyClass::cloneAndInitStatus)
                  .collect(Collectors.toSet());

That way you avoid state modifications during the stream.

assylias
  • 321,522
  • 82
  • 660
  • 783
1

You could write:

Set<Status> statusClone = cloner.deepClone(statusList)
                            .stream() 
                            .filter(s -> !(s instanceof FileMetadata))
                            .peek(s -> {
                                s.setStatus(JobStatus.UNINITIATED);
                                s.setLastAccessedTime(0);
                                s.setOffSet(null);
                                s.setStreamNo(null);
                            })
                            .collect(Collectors.toSet());

But it's a bit awkward since we're directly mutating an Object inside the peek method.

It would be better to write a copy constructor of Status and then use map instead of peek to map each status to a new status with this constructor.

Tunaki
  • 132,869
  • 46
  • 340
  • 423