I keep encountering convoluted problems involving tuples, so this was a nice change from the usual trivial examples. Records made the code much clearer and the code blocks shorter. In a case of mutable data, it will be even more helpful.
At 3:15, both the Kata and the JDK 17 solution have a common bug. the filtering will eliminate supplementary characters which may be alphabetic. The use of chars() method is the culprit, using codePoints() would be more correct. The surrogate range 0xd800 0xdfff, will always result in isAlphabetic returning false and will eliminate all the supplementary characters even if they are alphabetic.
I would avoid sorting entry streams, instead I would rather try to use a TreeMap with an appropriate comparator instead. If you need a sorted map, why not use one? Yes, it is more finicky to construct one using collectors etc., but you eliminate a whole step.
Can you paste some Go code here for the 2nd problem - top occurrences (including case of tie as described) ? Will help in comparing. - you can mention a github link.
There is a much simpler solution. 1. Group by letter to get counts Map, sort it by count 2. Get count of the third element in the list, store it into "int boundaryCount" 3. filter(), or even better takeWhile(), with a count >= boundaryCount. It should be no more than 5 lines of code.
Still mind-boggingly verbose and clunky for something that should be simple. And that's for code written by a very experienced, very smart expert, doing everything that can reasonably be done with the latest tools available to distill it down to near-optimum readability. Good thing we have Kotlin and Clojure now, otherwise I would probably have given up on the JVM...
@@brucedsm I can step in here and provide an example. I'm not expert though, so perhaps there are better ways to do it in Kotlin. I haven't watch the whole video yet, so I'm copying the logic he used for the second kata before he started talking about inverting the map. I still need to finish watching that part. The logic should be basically the same as his, so this comparison is mostly so you can compare the readability of Kotlin code with Java code. I personally find it less cluttered and confusing than the Java Streams Apis, but perhaps I'm just more used to Kotlin these days. val result = "This is a test String." .lowercase() .filter { it.isLetter() } .groupingBy { it }.eachCount() .entries.sortedByDescending { it.value } .take(3) The output is: [t=4, s=4, i=3] On a separate note, I love these videos and all the great changes coming to Java. Thanks guys, and keep up the great work!
Perhaps the video should also have shown a basic method using for loops and collections. My guess is that that code would have been a lot shorter and more easy to understand. There are places for streams IMO but the authors of the streams framework probably did not intend to replace all for loops by streams.
@@ErikBrakkee yeah, once you start to use it, you tend to use it everywhere, but many java developers really struggle to adopt to them because you have to learn so much stuff - all the collectors and comparators and streams and predicates and static method references and the processing order in the background, i mean: Comparator.reverseOrder() .. who is looking there for that. It forces people into stackoverflow-programming (which is fine if you know what you are doing., but..)
@@vanivari359 You fail to see that Java has already eliminated 90% of the verbosity. Before Java 8, especially before anonymous classes implementing any of this was particularly verbose. Also the fact that you needed to use procedural patterns, any algorithm would have required 100+ lines of code if you didn't want code smells in the form of overly long functions. Having a separate sort method for reverse order might be handy, but these kind of helper methods increase the size of classes and pollute their namespaces.
Tried below way to get the top 2 or 3 etc most occurring characters, to avoid creation of Records- str.chars() .mapToObj(c -> (char)c) // .map(Character::toLowerCase) .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())) .entrySet() .stream() .collect(Collectors.groupingBy(Map.Entry::getValue,Collectors.mapping(Map.Entry::getKey, Collectors.toList()))) .entrySet() .stream() .sorted(Map.Entry.comparingByKey().reversed()) .limit(2) .forEach(System.out::println); Creating Records for such requirements was causing complexity for me. But have measured the performance of above code.
above code is about 10 times slower than the imperative code written in Java & I will prefer imperative code over functional style till there is a very good reason.