I have a list of objects that I want to modify with one of the setters is it bad to call it in anonymous map and what are the possible side effects:
.stream().map(foo -> { foo.setDate(date);return foo;})
.collect(Collectors.toList()));
Intellij is telling me to switch it to peek
.stream().peek(foo -> foo.setDate(date).collect(Collectors.toList()));
But I read that peek should be used for debugging only. Should I avoid both ways?
Why don't you use foreach ?
.forEach(foo -> { foo.setDate(date);})
You don't even need to stream the collection.
You will save yourself the cost of creating a new collection as well.
The first way is more than OK in this case, what matters is that you don't change the source of the stream structurally, meaning adding/removing elements to it while you stream.
And indeed IntelliJ is wrong about this, a map is a lot more suited than a peek (it is only for debugging).
Related
I see removeif() on ArrayList, but when I do stream() it there is no option of it. Is it because removeif() change the size of collection and stream needs a fix size to work upon ?
to remove a element from a stream you can use Stream::filter. Example:
.filter(e -> e.getId() == 4)
Is it because removeif() change the size of collection and stream needs a fix size to work upon ?
No, in fact stream can even work with a infinite number of objects
Stream doesn't change source collection, stream take elements from sorce (it could be collection, infinite generator) then pass they through chain (transforming, filtering) on each step of mapping it will be a new objects, and then collect what passed in result (it could be collection, joined string or Integer) and return result in variable. It is provide declarative style and immutability, that can do great work in multithread calculations without side effect.
Is it possible to convert code like this
for (Contacto contact : contactos) {
fix.printf("%s\t%s\t%s\n",
contact.getName(), contact.getNumb(), contact.getDateOfBirth());
}
into a java stream?
Sure you can - the below should do the trick, using the forEach method of Stream:
contactos.stream()
.forEach(c -> fix.printf("%s\t%s\t%s\n", c.getName(), c.getNumb(), c.getDoB()));
Bear in mind this can impair readability - most people I know would say the loop is easier to read than this.
Also bear in mind that reaaaaally, for this, you should use forEachOrdered instead of forEach - forEach doesn't guarantee the order in which the items are processed, and for parallel streams this matters. forEachOrdered guarantees that items are processed one-by-one.
You can omit the call to Stream, depending on your container - if it implements Iterable (most standard containers do), then you should use forEach on that rather than the stream (so contactos.forEach(...). It's tidier and avoids the overhead of creating a Stream.
You also avoid the forEachOrdered issue above, as the items are processed "in iteration order".
I have a piece of code that looks something like this.
I have read two contradicting(?) "rules" regarding this.
That .map should not have side effects
That .foreach should not
update a mutable variable (so if I refactor to use foreach and
populate a result list, then that breaks that) as mentioned in http://files.zeroturnaround.com/pdf/zt_java8_streams_cheat_sheet.pdf
How can I solve it so I use streams and still returns a list, or should I simply skip streams?
#Transactional
public Collection<Thing> save(Collection<Thing> things) {
return things.stream().map(this::save).collect(Collectors.toList());
}
#Transactional
public Thing save(Thing thing) {
// org.springframework.data.repository.CrudRepository.save
// Saves a given entity. Use the returned instance for further operations as the save operation might have changed the entity instance completely.
Thing saved = thingRepo.save(thing);
return saved;
}
Doesn't that paper say shared mutable state? In your case if you declare the list inside the method and then use forEach, everything is fine. The second answer here mentions exactly what you are trying to do.
There is little to no reason to collect a entirely new List if you don't mutate it at all. Besides that your use case is basically iterating over every element in a collection and save that which could simply be achieved by using a for-each.
If for some reason thingRepo.save(thing) mutates the object you can still return the same collection, but at this point it's a hidden mutation which is not clearly visible at all since thingRepo.save(thing) does not suggest that.
I'm storing several Things in a Collection. The individual Things are unique, but their types aren't. The order in which they are stored also doesn't matter.
I want to use Java 8's Stream API to search it for a specific type with this code:
Collection<Thing> things = ...;
// ... populate things ...
Stream<Thing> filtered = things.stream.filter(thing -> thing.type.equals(searchType));
Is there a particular Collection that would make the filter() more efficient?
I'm inclined to think no, because the filter has to iterate through the entire collection.
On the other hand, if the collection is some sort of tree that is indexed by the Thing.type then the filter() might be able to take advantage of that fact. Is there any way to achieve this?
The stream operations like filter are not that specialized to take an advantage in special cases. For example, IntStream.range(0, 1_000_000_000).filter(x -> x > 999_999_000) will actually iterate all the input numbers, it cannot just "skip" the first 999_999_000. So your question is reduced to find the collection with the most efficient iteration.
The iteration is usually performed in Spliterator.forEachRemaining method (for non-short-circuiting stream) and in Spliterator.tryAdvance method (for short-circuiting stream), so you can take a look into the corresponding spliterator implementation and check how efficient it is. To my opinion the most efficient is an array (either bare or wrapped into list with Arrays.asList): it has minimal overhead. ArrayList is also quite fast, but for short-circuiting operation it will check the modCount (to detect concurrent modification) on every iteration which would add very slight overhead. Other types like HashSet or LinkedList are comparably slower, though in most of applications this difference is practically insignificant.
Note that parallel streams should be used with care. For example, the splitting of LinkedList is quite poor and you may experience worse performance than in sequential case.
The most important thing to understand, regarding this question, is that when you pass a lambda expression to a particular library like the Stream API, all the library receives is an implementation of a functional interface, e.g. an instance of Predicate. It has no knowledge about what that implementation will do and therefore has no way to exploit scenarios like filtering sorted data via comparison. The stream library simply doesn’t know that the Predicate is doing a comparison.
An implementation doing such an optimization would need an interaction of the JVM, which knows and understands the code, and the library, which knows the semantics. Such thing does not happen in current implementation and is currently far away, at least as I can see it.
If the source is a tree or sorted list and you want to benefit from that for filtering, you have to do it using APIs operating on the source, before creating the stream. E.g. suppose, we have a TreeSet and want to filter it to get items within a particular range, like
// our made-up source
TreeSet<Integer> tree=IntStream.range(0, 100).boxed()
.collect(Collectors.toCollection(TreeSet::new));
// the naive implementation
tree.stream().filter(i -> i>=65 && i<91).forEach(i->System.out.print((char)i.intValue()));
We can do instead:
tree.tailSet(65).headSet(91).stream().forEach(i->System.out.print((char)i.intValue()));
which will utilize the sorted/tree nature. When we have a sorted list instead, say
List<Integer> list=new ArrayList<>(tree);
utilizing the sorted nature is more complex as the collection itself doesn’t know that it’s sorted and doesn’t offer operations utilizing that directly:
int ix=Collections.binarySearch(list, 65);
if(ix<0) ix=~ix;
if(ix>0) list=list.subList(ix, list.size());
ix=Collections.binarySearch(list, 91);
if(ix<0) ix=~ix;
if(ix<list.size()) list=list.subList(0, ix);
list.stream().forEach(i->System.out.print((char)i.intValue()));
Of course, the stream operations here are only exemplary and you don’t need a stream at all, when all you do then is forEach…
As far as I am aware, there's no such differenciation for normal streaming.
However, you might be better off when you use parallel streaming when you use a collection which is easily devideable, like ArrayList over LinkedList or any type of Set.
I know that once we create the stream off of a collection we should not modify collection while consuming the stream, However not sure if following approach works where I am modifying the entry inside the map and not adding or removing anything from it. Also, is this the right way to stream the map always and consume it ?
creepyCategoryStatuses.entrySet().stream().forEach(entry -> {
String creepyBlockResponse = (String) getBlockResponse(entry.getKey());
if (creepyBlockResponse.equalsIgnoreCase("block")){
entry.setValue(true);
//OR creepyCategoryStatuses(entry.getKey(), true);
}
});
This kind of modification is no problem as setting the value of an entry is not a structural modification. However, it’s worth noting that there are more Java 8 features than the Stream API or the forEach method:
creepyCategoryStatuses.replaceAll((key,value)->
((String)getBlockResponse(key)).equalsIgnoreCase("block")||value
);
does the job much easier. Note that ||value will retain the old value if the condition evaluates to false as in your original code.
Yes, such usage of Map seems to be legit. Note that in this case you don't need the stream. You can simply use
creepyCategoryStatuses.entrySet().forEach(entry -> {...});
According to your use-case, you could use replaceAll here:
creepyCategoryStatuses.replaceAll((k, v) -> ((String) getBlockResponse(k)).equalsIgnoreCase("block") ? true : v);
I always favor functional paradigms, so I would collect the new data into a new map, but that's a matter of taste.