1

See comments for question. Remove N elements selectively (Condition is that list element matches 'remove')

List<String> mylist = new ArrayList<>();
mylist.add("remove");
mylist.add("all");
mylist.add("remove");
mylist.add("remove");
mylist.add("good");
mylist.add("remove");

//  Remove first X "remove".
//  if X is 2, then result list should be "all, remove, good, remove"
//  Use java 8 features only, possibly single line code.
//  Please don't answer with looping, iterating, if conditions etc.
//  Answer should use JDK 8 new features.
4
  • 3
    If you really really want to do it in one line, IntStream.range(0,x).forEach(i -> mylist.remove("remove"));, but it's going to be unnecessarily O(N*x) and just a bad idea. Commented Jun 13, 2016 at 5:00
  • 2
    Or Collections.nCopies(x, "remove").forEach(mylist::remove);, but @Misha’s comment applies to this as well. Commented Jun 13, 2016 at 9:52
  • IntStream.range(0,x).forEach(i -> mylist.remove("remove")); Condition could be anything. ex: Remove if string contains "remov" Commented Jun 14, 2016 at 4:46
  • 1
    Sometimes a good old for-loop is much more readable than a stream. This seems like a homework assignment question, but IRL in such cases, for loops are better. Commented Jun 14, 2016 at 7:02

3 Answers 3

6

How about this:

List<String> filter(List<String> mylist, int x){
    AtomicInteger index = new AtomicInteger(0);
    mylist.removeIf(p -> p.equals("remove") && index.getAndIncrement() < x);
    return myList;
}

With x=0, it prints:

[remove, all, remove, remove, good, remove]

With x=1, it prints:

[all, remove, remove, good, remove]

With x=2, it prints:

[all, remove, good, remove]

With x=3, it prints:

[all, good, remove]

Sign up to request clarification or add additional context in comments.

4 Comments

A good example of how to replace a good old for loop with something more complicated, less readable and less efficient at the same time…
I think, “more complicated” and “less efficient” are categories, which work independently of familiarity.
Works for me. Because I can add any condition (For ex: String contains instead of equals) I tried same approach with type "int" and didnt work. Got compiler error which stated that type int should be final. Still some fine tuning is needed. because removeIf will run on all elements in list unnecessarily. Ex: If list has 100 elements and I need to remove 2, then I guess it will go through all 100 elements unnecessarily.
@J J: that’s why I said “less efficient”. It processes all elements and repeatedly accesses a heap variable with thread synchronization semantics. While it is possible that the HotSpot optimizer sorts this out, it’s still inferior to a simple for loop wich uses a local variable and short circuits in the first place.
1

try this:

List<String> mylist = new ArrayList<>();
    mylist.add("remove");
    mylist.add("all");
    mylist.add("remove");
    mylist.add("remove");
    mylist.add("good");
    mylist.add("remove");
    IntStream.range(0,2).forEach(a-> mylist.remove("remove"));

    System.out.println(mylist);

Comments

1

A not entirely serious solution with streams which avoids state (updated):

        IntStream.range(0, mylist.size()) //1
             .mapToObj(x -> new AbstractMap.SimpleEntry<>(x, mylist.get(x))) //2
             .collect(Collectors.partitioningBy(x->x.getValue().equals("remove"))) //3
             .entrySet()
             .stream()
             .flatMap( x->x.getKey()?x.getValue().stream().skip(2):x.getValue().stream()) //4
             .sorted(Map.Entry.comparingByKey()) //5
             .map(Map.Entry::getValue) //6
             .collect(Collectors.toList());
  1. An IntStream is used to preserve the original sorting of the list.
  2. Tuples are created
  3. The list is partitionized depending on if the string equals "remove"
  4. only elements from the list containing "remove" are skipped
  5. the original sorting if restored
  6. finally, the orignal Strings are restored from the Tuple

Since the list is wrapped and unwrapped two times, partitionized and sorted, it is unfortunately not a very efficient solution. So probably, a simple for-loop is more efficient than a stream-based solution.

2 Comments

Note that besides the construction of the entry instances, you can use Map.Entry instead of AbstractMap.SimpleEntry, e.g. .map(Map.Entry::getValue) which is shorter and generalizes the solution. Also, you can use .sorted(Map.Entry.comparingByKey()). Starting with Java 9, you will likely be able to use Map.entry(x, mylist.get(x)) instead of new AbstractMap.SimpleEntry<>(x, mylist.get(x)), assuming that all follow-up operation refer to Map.Entry instead of AbstractMap.SimpleEntry.
You got my +1 for the creativity. While this is obvious overkill for this specific task, it allows to learn a lot about the Stream API and how you could solve certain kind of problems.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.