3

I need a method, which filter out a collection by checking if the element's field is contained in another collection's element's field. Which way is better:

A method, returning filtered collection:

List method1(List foo, List bar){
    if(bar.isEmpty()) 
        return Collections.emptyList();
    Set<Integer> ids = bar.stream().map(elem->elem.getId).collect(Collectors.toSet());
    return foo.stream().filter(elem->ids.contains(elem.barId));
}
  • easy handling empty conditional collection
  • creating stream and another collection

Or a method, modifying the original collection:

void method2(List foo, List bar){
    if(bar.isEmpty()){ 
         foo.clear();
         return;
    }
    Set<Integer> ids = bar.stream().map(elem->elem.getId).collect(Collectors.toSet());
    foo.removeIf(elem->ids.contains(elem.barId));
}
  • no redundant object
  • clearing original collection instead of just return new
TEXHIK
  • 1,369
  • 1
  • 11
  • 34
  • 3
    Please paste code that at least compiles. Yours, doesn't. At leat the first one as multiple problems. Well the second aint better. – GhostCat Sep 11 '19 at 09:16
  • `removeIf` returns a `boolean`, not a `List`. Other than that the answer is probably: "it depends". – Marvin Sep 11 '19 at 09:16
  • 5
    It depends on whether you still need the original `List` since `removeIf` modifies it. Personally, I'd always use a filtered `Stream` because generally you don't know what other places that list is referenced. – daniu Sep 11 '19 at 09:23
  • @daniu but what about if you have a huge list with heavy objects? This is not my case, but in general - using stream doubles the list(worst case) just for GC if you assigning result to the same list... – TEXHIK Sep 11 '19 at 09:28
  • 3
    **using stream doubles the list** Huh? That isn't true. The whole point of streams is that they present a *view* on a collection. There is no "doubling" – GhostCat Sep 11 '19 at 09:40
  • Maybe an important thing you miss, is that in all your cases you can just use one stream, and this can make deffirence – Youcef LAIDANI Sep 11 '19 at 09:42
  • @GhostCat stream itself not, but it collets result to *another* collection to be returned. yes, it replaces the original one, but there is time, when they both exists. – TEXHIK Sep 11 '19 at 10:36
  • 1
    As @GhostCat said, you should fix the compiler errors. There are still plenty of them. Further, you have to fix the logical errors. A `filter` predicate tells which objects to keep, the `removeIf` predicate tells which to remove. – Holger Sep 11 '19 at 11:36
  • 3
    I think there *could* be an upvote-worthy question buried in these fragments of text and uncompilable code... – Marco13 Sep 11 '19 at 12:01
  • 2
    BTW: There is already special method for this [boolean retainAll(Collection> c)](https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html#retainAll-java.util.Collection-) in case both your lists have same type of the objects and [equals](https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#equals(java.lang.Object)) behave correctly. – Alexander Druzhynin Sep 11 '19 at 19:49

3 Answers3

2

First approach is imho better. Out parameter concept in real life is hard to maintain.

2

Your method1 & method2 will not work as you expected as it does not have a terminal operation in your last stream on foo. It is recommended to use immutable approach whenever possible as it leads to less error-prone code and easy to understand the code behaviour. You should read about this

List method1(List foo, List bar){
    
    if (bar.isEmpty()) 
        return Collections.emptyList();

    final Set<Integer> barIds = getIds(bar);
    return foo.stream()
              .filter(elem->barIds.contains(elem.barId))
              .collect(Collectors.toList());
}

private Set<Integer> getIds(final List bar) {

  return bar.stream()
            .map(elem->elem.getId)
            .collect(Collectors.toSet());
}
Jude Niroshan
  • 4,280
  • 8
  • 40
  • 62
-1

A method that returns a different version of the input, instead of modifying it, is a method which input is passed by value, a strategy compliant to how Java was designed. The other way around is a method which input is passed by reference, the way C works. As you just proved it is possible to do this in Java too, but it is more a "workaround" than a feature. (More info on this)

Not saying you which version is better, but I would use the first one if in doubt.

Hope I helped.

Sterconium
  • 559
  • 4
  • 20