As pointed out in the comments, the correct (as in "more readable", "more idiomatic" and "less error-prone") way to do this is to use removeAll, as in
list1.removeAll(list2);
Note that this modifies the original list.
If you insist on using streams (or simply you don't want to modify the original list) you can filter out values that appear in both lists (i.e. letting only values from list1 that are not contained in list2 past the filter)
var list3 = list1.stream()
.filter(Predicate.not(list2::contains))
.collect(Collectors.toList());
Note: I used Predicate::not since you tagged the question with java-11. It's not available in Java 8. If you are stuck with Java 8 (or 9, or 10) the process is similar if a little more verbose
var list3 = list1.stream()
.filter(s -> !list2.contains(s))
.collect(Collectors.toList());
list1.removeAll(list2)instead of loops or streams?