31 Ocak 2023 Salı

Stream.peek metodu - Dikkatli Olmak Lazım

Tüm elemenlar için çağrılır.forEach() ile benzer ancak esas amacı debug içindir. Açıklaması şöyle.
This method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline
SonarLint açıklaması şöyle
According to its JavaDocs, the intermediate Stream operation java.util.Stream.peek() “exists mainly to support debugging” purposes.

A key difference with other intermediate Stream operations is that the Stream implementation is free to skip calls to peek() for optimization purpose. This can lead to peek() being unexpectedly called only for some or none of the elements in the Stream.

As a consequence, relying on peek() without careful consideration can lead to error-prone code.
This rule raises an issue for each use of peek() to be sure that it is challenged and validated by the team to be meant for production debugging/logging purposes.
Verilen örnek şöyle
// Noncompliant Code Example
Stream.of("one", "two", "three", "four")
         .filter(e -> e.length() > 3)
         .peek(e -> System.out.println("Filtered value: " + e));

// Compliant Solution
Stream.of("one", "two", "three", "four")
         .filter(e -> e.length() > 3)
         .foreach(e -> System.out.println("Filtered value: " + e));
Debug
Örnek
Şöyle yaparız.
"abcd".chars().peek(e->System.out.print(e + ":"))
Çıktı olarak şunu alırız.
a:b:c:d:
Örnek
Şöyle yaparız.
Stream.of("one", "two", "three", "four")
  .filter(e -> e.length() > 3)
  .peek(e -> System.out.println("Filtered value: " + e))
  .map(String::toUpperCase)
  .peek(e -> System.out.println("Mapped value: " + e))
  .collect(Collectors.toList());
Setter Çağırmak
Açıklaması şöyle. Yani debug amacı dışında kullanılması yasak değil, ama teşvik te edilmiyor. Dolayısıyla tüm sorumluluk kodlayan kişide.
We can see that non-debugging usages are neither forbidden nor discouraged.
Örnek
Elimizde şöyle bir kod olsun. Burada peek() çağrılıyor. 
// The problem is that this behavior is highly deceiving because certain 
// Stream implementations can optimize out peek() calls.

Stream.of(Date.from(Instant.EPOCH))
  .peek(d -> d.setTime(Long.MAX_VALUE))
  .forEach(System.out::println);

// Sun Aug 17 08:12:55 CET 292278994
Ancak çağrılmadığı durumlar da olabilir. Bazı örnekler şöyle
List.of(1, 2, 3)
  .stream()
  .peek(System.out::println)
  .count();
// The result is empty

Stream.iterate(0, i -> i + 1)
  .peek(System.out::println)
  .findFirst();
// The result is empty
Örnek
Şöyle yaparız.
List<Foo> newFoos = foos.stream()
            .filter(Foo::isBlue)
            .peek(foo -> foo.setTitle("Some value"))
            .collect(Collectors.toList());
Hesap Yapmak
Örnek

Kalan yüzdeyi yazdırmak için kullanılabilir. Şöyle yaparız.
Stream<MyData> myStream = readData();
final AtomicInteger loader = new AtomicInteger();
int fivePercent = elementsCount / 20;
MyResult result = myStream
  .map(row -> process(row))
  .peek(stat -> {
    if (loader.incrementAndGet() % fivePercent == 0) {
      System.out.println(loader.get() + " elements on " + elementsCount + " treated");
      System.out.println((5*(loader.get() / fivePercent)) + "%");
    }
  })
  .reduce(MyStat::aggregate);

Hiç yorum yok:

Yorum Gönder