
Despite the fact that Java 8 has been released for a long time, not all programmers use its new features, someone stops the fact that work projects are too difficult to translate from Java 7 or even Java 6, use someone in their GWT projects, Someone is doing projects for Android and does not want or can not use third-party libraries to implement lambdas and Stream Api. However, knowledge of lambdas and Stream Api for a Java programmer is often required at interviews, well, it will just be useful when switching to a project that uses Java 8. I would like to offer you a brief cheat sheet on Stream Api with practical examples of implementing various tasks with a new functional approach. Knowledge of lambda and functional programming is not required (I tried to give examples so that everything was clear), the level of the most basic knowledge of Java and above.
Also, since this is a cheat sheet, the article can be used to quickly recall how a particular feature of the Java Stream Api works. A brief listing of the features of the main functions is given at the beginning of the article.
')
For those who do not know what Stream Api is.Stream API is a new way to work with data structures in a functional style. Most often, using stream in Java 8 works with collections, but in fact this mechanism can be used for a wide variety of data.
Stream Api allows you to write processing of data structures in the style of SQL, if earlier the task to get the sum of all odd numbers from the collection was solved by the following code:
Integer sumOddOld = 0; for(Integer i: collection) { if(i % 2 != 0) { sumOddOld += i; } }
Then with the help of Stream Api you can solve this problem in a functional style:
Integer sumOdd = collection.stream().filter(o -> o % 2 != 0).reduce((s1, s2) -> s1 + s2).orElse(0);
Moreover, Stream Api allows solving the task in parallel only by changing stream () to parallelStream () without any extra code, i.e.
Integer sumOdd = collection.parallelStream().filter(o -> o % 2 != 0).reduce((s1, s2) -> s1 + s2).orElse(0);
Already makes the code parallel, without any semaphores, synchronizations, risks of deadlocks, etc.
General table of contents 'cheat sheets'1. JPA and Hibernate in questions and answers2. Three hundred fifty most popular non-mobile Java opensource projects on github3. Collections in Java (standard, guava, apache, trove, gs-collections, and others4. Java Stream API5. Two hundred and fifty Russian-language teaching videos of lectures and reports on Java.6. List of useful links for Java programmer7 Typical tasks
7.1 Optimum way to convert an InputStream to a string7.2 The most productive way to bypass the Map, count the number of occurrences of the substring8. Libraries for working with Json (Gson, Fastjson, LoganSquare, Jackson, JsonPath and others) Let's start from the beginning, namely the creation of stream objects in Java 8.
I. Ways to create streams
Here are some ways to create a stream.
The way to create stream | Creation Template | Example |
---|
1. Classic: Creating a stream from the collection | collection. stream () | Collection<String> collection = Arrays.asList("a1", "a2", "a3"); Stream<String> streamFromCollection = collection.stream(); |
2. Creating a stream from values | Stream.of ( value1 , ... valueN ) | Stream<String> streamFromValues = Stream.of("a1", "a2", "a3"); |
3. Creating a stream from an array | Arrays.stream ( array ) | String[] array = {"a1","a2","a3"}; Stream<String> streamFromArrays = Arrays.stream(array); |
4. Creating a stream from a file (each line in the file will be a separate element in the stream) | Files.lines ( file_path ) | Stream<String> streamFromFiles = Files.lines(Paths.get("file.txt")) |
5. Creating a stream from a string | "line". chars () | IntStream streamFromString = "123".chars() |
6. Using Stream.builder | Stream. builder (). add (...) .... build () | Stream.builder().add("a1").add("a2").add("a3").build() |
7. Creating a parallel stream | collection. parallelStream () | Stream<String> stream = collection.parallelStream();
|
8. Creating endless streams using Stream.iterate
| Stream.iterate ( start_condition , expression_generation ) | Stream<Integer> streamFromIterate = Stream.iterate(1, n -> n + 1) |
9. Creating Endless Stream with Stream.generate | Stream.generate ( generation_express ) | Stream<String> streamFromGenerate = Stream.generate(() -> "a1") |
In principle, except for the last two ways to create a stream, everything is no different from the usual ways of creating collections. The last two methods are used to generate infinite streams, the initial condition is set in iterate and the expression getting the next value from the previous one, that is, Stream.iterate (1, n -> n + 1) will produce the values ​​1, 2, 3, 4, ... N Stream.generate is used to generate constant and random values, it just gives out the values ​​corresponding to the expression, in this example, it will produce an infinite number of “a1” values.
For those who do not know lambdaThe expression n -> n + 1 is just an analogue of the expression Integer func (Integer n) {return n + 1;}, and the expression () -> "a1" is analogous to the expression String func () {return "a1";} wrapped in anonymous class.
More detailed examplesAlso this example can be found on
github 'e
System.out.println("Test buildStream start");
Ii. Streaming Methods
The Java Stream API offers two kinds of methods:
1. Conveyor - return another stream, that is, they work as a builder,
2. Terminal - return another object, such as a collection, primitives, objects, Optional, etc.
What are the differences between conveyor and terminal methods?The general rule is that a stream can have as many pipeline calls as possible and at the end one terminal, all pipeline methods are lazily executed and until the terminal method is called no action actually occurs, just like creating a Thread or Runnable object , but do not call it start.
In general, this mechanism is similar to constructing SQL queries, there can be any number of nested Selects and only one result in the end. For example, in the expression collection.stream (). Filter ((s) -> s.contains ("1")). Skip (2) .findFirst (), filter and skip are pipelined, and findFirst is terminal, it returns the object Optional and this ends work with stream.
2.1 Brief description of conveyor methods of working with streams
Stream method | Description | Example |
---|
filter | Filters records, returns only records that match the condition. | collection.stream (). filter ("a1" :: equals) .count () |
skip | Lets skip the first N items. | collection.stream (). skip (collection.size () - 1) .findFirst (). orElse ("1") |
distinct | Returns a stream without duplicates (for the equals method) | collection.stream (). distinct (). collect (Collectors.toList ()) |
map | Converts every element of stream | collection.stream (). map ((s) -> s + "_1"). collect (Collectors.toList ()) |
peek | Returns the same stream, but applies a function to each element of the stream. | collection.stream (). map (String :: toUpperCase) .peek ((e) -> System.out.print ("," + e)). collect (Collectors.toList ()) |
limit | Allows you to limit the sample to a certain number of first elements. | collection.stream (). limit (2) .collect (Collectors.toList ()) |
sorted | Allows you to sort the values ​​either in natural order or by specifying the Comparator | collection.stream (). sorted (). collect (Collectors.toList ()) |
mapToInt , mapToDouble , mapToLong | Analogue map, but returns a numeric stream (that is, a stream of numeric primitives) | collection.stream (). mapToInt ((s) -> Integer.parseInt (s)). toArray () |
flatmap flatMapToInt , flatMapToDouble , flatMapToLong | It looks like a map, but it can create several | collection.stream (). flatMap ((p) -> Arrays.asList (p.split (",")). stream ()). toArray (String [] :: new) |
2.2 Brief description of terminal methods of working with streams
Stream method | Description | Example |
---|
findFirst | Returns the first element from the stream (returns Optional) | collection.stream (). findFirst (). orElse ("1") |
findAny | Returns any suitable element from the stream (returns Optional) | collection.stream (). findAny (). orElse ("1") |
collect | Presentation of results in the form of collections and other data structures | collection.stream (). filter ((s) -> s.contains ("1")). collect (Collectors.toList ()) |
count | Returns the number of elements in the stream. | collection.stream (). filter ("a1" :: equals) .count () |
anyMatch | Returns true if the condition is true for at least one element. | collection.stream (). anyMatch ("a1" :: equals) |
noneMatch | Returns true if the condition is not met for one element. | collection.stream (). noneMatch ("a8" :: equals) |
allMatch | Returns true if the condition is true for all items. | collection.stream (). allMatch ((s) -> s.contains ("1")) |
min | Returns the minimum element, uses a comparator as a condition | collection.stream (). min (String :: compareTo) .get () |
max | Returns the maximum element, uses a comparator as a condition | collection.stream (). max (String :: compareTo) .get () |
forEach | Applies a function to each stream object, order when executed in parallel is not guaranteed. | set.stream (). forEach ((p) -> p.append ("_ 1")); |
forEachOrdered | Applies a function to each stream object, preserving the order of the elements guarantees | list.stream (). forEachOrdered ((p) -> p.append ("_ new")); |
toArray | Returns an array of stream values | collection.stream (). map (String :: toUpperCase) .toArray (String [] :: new); |
reduce | Allows you to perform aggregate functions on the entire collection and return a single result. | collection.stream (). reduce ((s1, s2) -> s1 + s2) .orElse (0) |
Pay attention to the methods findFirst, findAny, anyMatch are short-circuiting methods, that is, bypassing streams is organized in such a way as to find a suitable element as quickly as possible, and not to bypass the entire original stream.
2.3 Brief description of additional methods for numeric streams
Stream method | Description | Example |
---|
sum | Returns the sum of all numbers. | collection.stream (). mapToInt ((s) -> Integer.parseInt (s)). sum () |
average | Returns the arithmetic average of all numbers. | collection.stream (). mapToInt ((s) -> Integer.parseInt (s)). average () |
mapToObj | Converts a numeric stream back to an object. | intStream.mapToObj ((id) -> new Key (id)). toArray () |
2.4 Several other useful streaming methods
Stream method | Description |
---|
isParallel | Find out if the stream is parallel |
parallel | Return a parallel stream, if the stream is already parallel, it can return itself |
sequential | Return a consecutive stream, if the stream is already consistent, it can return itself |
With the help of methods parallel and sequential, you can determine which operations can be parallel, and which only sequential. You can also make a parallel stream from any serial stream and vice versa, that is:
collection.stream(). peek(...).
Attention : it is not recommended to use parallel streams for any long operations (retrieving data from the database, network connections), since all parallel streams work with one fork / join pool and such long operations can stop all parallel streams in the JVM from for the lack of available threads in the pool, i.e. parallel streams should be used only for short operations, where the count goes on for milliseconds, but not for those where the count can go on for seconds and minutes.
Iii. Examples of working with stream methods
Consider working with methods on various tasks that are usually required when working with collections.
3.1 Examples of using filter, findFirst, findAny, skip, limit and count
Condition : a collection of Arrays.asList strings is given ("a1", "a2", "a3", "a1"), let's see how it can be processed using the filter, findFirst, findAny, skip and count methods:
Task | Sample code | Result |
---|
Return the number of occurrences of the object "a1" | collection.stream (). filter ("a1" :: equals) .count () | 2 |
Return the first element of the collection, or 0 if the collection is empty | collection.stream (). findFirst (). orElse ("0") | a1 |
Return the last element of the collection or “empty” if the collection is empty. | collection.stream (). skip (collection.size () - 1) .findAny (). orElse ("empty") | a1 |
Find an item in the collection equal to "a3" or throw an error | collection.stream (). filter ("a3" :: equals) .findFirst (). get () | a3 |
Return the third element of the collection in order | collection.stream (). skip (2) .findFirst (). get () | a3 |
Return two items starting from the second | collection.stream (). skip (1) .limit (2) .toArray () | [a2, a3] |
Select all items by pattern | collection.stream (). filter ((s) -> s.contains ("1")). collect (Collectors.toList ()) | [a1, a1] |
Notice that the findFirst and findAny methods return a new type, Optional, introduced in Java 8, in order to avoid a NullPointerException. The filter method is convenient to use to select only a specific set of values, and the skip method allows you to skip a certain number of elements.
If you do not know lambdaThe expression "a3" :: equals is an analogue of boolean func (s) {return "a3" .equals (s);}, and (s) -> s.contains ("1") is an analogue of boolean func (s) {return s.contains ("1");} wrapped in an anonymous class.
Condition : a collection of the People class is given (with the fields name — name, age — age, sex — gender), of the form Arrays.asList (new People (“Vasya”, 16, Sex.MAN), new People (“Petya”, 23, Sex.MAN), new People (“Elena”, 42, Sex.WOMEN), new People (“Ivan Ivanovich”, 69, Sex.MAN)). Let's see examples of how to work with this class:
Task | Sample code | Result |
---|
Select men-conscripts (from 18 to 27 years) | peoples.stream (). filter ((p) -> p.getAge ()> = 18 && p.getAge () <27 && p.getSex () == Sex.MAN) .collect (Collectors.toList ()) | [{name = 'Peter', age = 23, sex = MAN}] |
Find the average age among men | peoples.stream (). filter ((p) -> p.getSex () == Sex.MAN). mapToInt (People :: getAge) .average (). getAsDouble () | 36.0 |
Find the number of potentially working people in the sample (i.e., from 18 years old and considering that women are 55, and men are 60) | peoples.stream (). filter ((p) -> p.getAge ()> = 18) .filter ( (p) -> (p.getSex () == Sex.WOMEN && p.getAge () <55) || (p.getSex () == Sex.MAN && p.getAge () <60)). count () | 2 |
3.2 Examples of using distinct
The distinct method returns stream without duplicates, while for an ordered stream (for example, a list-based collection), the order is stable, for an unordered stream, order is not guaranteed. Consider the results of the collection on the Collection ordered ordered = Arrays.asList ("a1", "a2", "a2", "a3", "a1", "a2", "a2") and Collection nonOrdered = new HashSet <> (ordered ).
Task | Sample code | Result |
---|
Getting a collection without duplicates from an unordered stream | nonOrdered.stream (). distinct (). collect (Collectors.toList ()) | [a1, a2, a3] - order is not guaranteed |
Getting a collection without duplicates from streamlined stream | ordered.stream (). distinct (). collect (Collectors.toList ()); | [a1, a2, a3] - order is guaranteed |
Note:1. If you use distinct with a class that has equals redefined, you must also correctly override hashCode according to the equals / hashCode contract (the most important thing is that hashCode for all equals objects returns the same value), otherwise the distinct may not remove duplicates (similarly like using HashSet / HashMap),
2. If you use parallel streams and you don’t care about the order of elements after removing duplicates - it’s much better for performance to do first stream unordered with unordered (), and only then use distinct (), since maintaining sorting stability while parallel streaming is rather expensive. resources and distinct () on an ordered stream will be executed much longer than with an unordered,
Detailed examplesAlso this example can be found on
github'e
3.3 Examples of using Match functions (anyMatch, allMatch, noneMatch)
Condition : a collection of Arrays.asList strings is given ("a1", "a2", "a3", "a1"), let's see how it can be processed using the Match function
Task | Sample code | Result |
---|
Find whether there is at least one "a1" element in the collection. | collection.stream (). anyMatch ("a1" :: equals) | true |
Find whether there is at least one “a8” item in the collection. | collection.stream (). anyMatch ("a8" :: equals) | false |
Find whether there is a symbol "1" for all elements of the collection. | collection.stream (). allMatch ((s) -> s.contains ("1")) | false |
Check that there are no “a7” items in the collection. | collection.stream (). noneMatch ("a7" :: equals) | true |
Detailed examplesAlso this example can be found on
github'e
3.4 Examples of using Map functions (map, mapToInt, FlatMap, FlatMapToInt)
Condition : two collections, collection1 = Arrays.asList ("a1", "a2", "a3", "a1") and collection2 = Arrays.asList ("1.2.0", "4.5"), are given Let's see how it can be processed using various map functions.Task | Sample code | Result |
---|
Add "_1" to each element of the first collection | collection1.stream (). map ((s) -> s + "_1"). collect (Collectors.toList ()) | [a1_1, a2_1, a3_1, a1_1] |
In the first collection, remove the first character and return an array of numbers (int []) | collection1.stream (). mapToInt ((s) -> Integer.parseInt (s.substring (1))). toArray () | [1, 2, 3, 1] |
From the second collection get all the numbers listed, separated by commas from all the elements. | collection2.stream (). flatMap ((p) -> Arrays.asList (p.split (",")). stream ()). toArray (String [] :: new) | [1, 2, 0, 4, 5] |
From the second collection to get the sum of all the numbers separated by commas | collection2.stream (). flatMapToInt ((p) -> Arrays.asList (p.split (",")). stream (). mapToInt (Integer :: parseInt)). sum () | 12 |
Please note: all map functions can return an object of another type (class), that is, map can work with a stream of lines, and output Stream from Integer values ​​or get the People class, and return the Office class, where these people work, etc. p., flatMap (flatMapToInt, etc.) should return a stream with one, several or no elements for each element of the incoming stream (see the last two examples) for output.Detailed examplesAlso this example can be found on github'e
3.5 Examples of using the Sorted function
Condition : two collections of strings Arrays.asList ("a1", "a4", "a3", "a2", "a1", "a4") and a collection of people of the class People are given (with fields name - name, age - age , sex - gender), of the type of Arrays.asList (new People (“Vasya”, 16, Sex.MAN), new People (“Peter”, 23, Sex.MAN), new People (“Elena”, 42, Sex. WOMEN), new People (“Ivan Ivanovich”, 69, Sex.MAN)). Let's see examples of how to sort them:Task | Sample code | Result |
---|
Sort the string collection alphabetically | collection.stream (). sorted (). collect (Collectors.toList ()) | [a1, a1, a2, a3, a4, a4] |
Sort the string collection alphabetically in reverse order. | collection.stream (). sorted ((o1, o2) -> -o1.compareTo (o2)). collect (Collectors.toList ()) | [a4, a4, a3, a2, a1, a1] |
Sort the collection of strings alphabetically and remove duplicates | collection.stream().sorted().distinct().collect(Collectors.toList()) | [a1, a2, a3, a4] |
| collection.stream().sorted((o1, o2) -> -o1.compareTo(o2)).distinct().collect(Collectors.toList()) | [a4, a3, a2, a1] |
| peoples.stream().sorted((o1,o2) -> -o1.getName().compareTo(o2.getName())).collect(Collectors.toList()) | [{''}, {' '}, {''}, {''}]
|
, | peoples.stream().sorted((o1, o2) -> o1.getSex() != o2.getSex()? o1.getSex(). compareTo(o2.getSex()): o1.getAge().compareTo(o2.getAge())).collect(Collectors.toList()) | [{'Vasya'}, {'Petya'}, {'Ivan Ivanovich'}, {'Elena'}] |
Detailed examplesAlso this example can be found on github'e
3.6 Examples of using Max and Min functions
Condition : a collection of Arrays.asList strings is given ("a1", "a2", "a3", "a1"), and a collection of the Peoples class from past examples of the Sorted and Filter functions.Task | Sample code | Result |
---|
Find the maximum value among the row collection | collection.stream (). max (String :: compareTo) .get () | a3 |
Find the minimum value among the row collection | collection.stream (). min (String :: compareTo) .get () | a1 |
Find a person with a maximum age. | peoples.stream (). max ((p1, p2) -> p1.getAge (). compareTo (p2.getAge ())). get () | {name = 'Ivan Ivanovich', age = 69, sex = MAN} |
Find a person with a minimum age | peoples.stream (). min ((p1, p2) -> p1.getAge (). compareTo (p2.getAge ())). get () | {name = 'Vasya', age = 16, sex = MAN} |
Detailed examplesAlso this example can be found on github'e
3.7 Examples of using ForEach and Peek functions
Both ForEach and Peek essentially do the same thing, change the properties of objects in the stream, the only difference between them is that ForEach is terminal and it finishes working with the stream while Peek is pipelining and work with the stream continues. For example, there is a collection: Collection<StringBuilder> list = Arrays.asList(new StringBuilder("a1"), new StringBuilder("a2"), new StringBuilder("a3"));
And you need to add "_new" to each element, then for ForEach the code will be list.stream().forEachOrdered((p) -> p.append("_new"));
and for peek the code will be List<StringBuilder> newList = list.stream().peek((p) -> p.append("_new")).collect(Collectors.toList());
Detailed examplesAlso this example can be found on github'e
3.8 Examples of using the Reduce function
The reduce method allows you to perform aggregate functions on the entire collection (such as the sum, finding the minimum or maximum value, etc.), it returns one value for the stream, the function takes two arguments — the value obtained in the previous steps and the current value.Condition : Given a collection of numbers Arrays.asList (1, 2, 3, 4, 2) perform several actions on them using reduce.Task | Sample code | Result |
---|
Get the sum of numbers or return 0 | collection.stream (). reduce ((s1, s2) -> s1 + s2) .orElse (0) | 12 |
Return maximum or -1 | collection.stream (). reduce (Integer :: max) .orElse (-1) | four |
Return the sum of odd numbers or 0 | collection.stream (). filter (o -> o% 2! = 0) .reduce ((s1, s2) -> s1 + s2) .orElse (0) | four |
3.9 Examples of using the toArray and collect functions
If everything is simple with toArray, you can either call toArray () to get Object [], or toArray (T [] :: new) - by getting an array of type T, then collect allows you to convert many values ​​into a collection, map or any other type . To do this, use static methods from Collectors, for example, the conversion to List will be stream.collect (Collectors.toList ()).Let's look at static methods from Collectors:Method | Description |
---|
toList, toCollection, toSet | represent stream in the form of a list, collection or set |
toConcurrentMap, toMap | let you convert stream to map |
averagingInt, averagingDouble, averagingLong | return average |
summingInt, summingDouble, summingLong | returns amount |
summarizingInt, summarizingDouble, summarizingLong | return SummaryStatistics with different aggregate values |
partitioningBy | splits the collection into two parts according to the condition and returns them as Map <Boolean, List> |
groupingBy | splits the collection into several parts and returns Map <N, List <T >> |
mapping | additional value conversions for complex collectors |
Now let's look at working with collect and toArray with examples:Condition: Given the collection of numbers Arrays.asList (1, 2, 3, 4), consider the work of collect and toArray with itTask | Sample code | Result |
---|
Get the sum of odd numbers | numbers.stream (). collect (Collectors.summingInt (((p) -> p% 2 == 1? p: 0))) | four |
Subtract 1 from each item and get the average | numbers.stream (). collect (Collectors.averagingInt ((p) -> p - 1)) | 1.5 |
Add to the numbers 3 and get statistics | numbers.stream (). collect (Collectors.summarizingInt ((p) -> p + 3)) | IntSummaryStatistics {count = 4, sum = 22, min = 4, average = 5.5, max = 7} |
Divide numbers into even and odd | numbers.stream (). collect (Collectors.partitioningBy ((p) -> p% 2 == 0)) | {false = [1, 3], true = [2, 4]} |
Condition: Given a collection of Arrays.asList strings ("a1", "b2", "c3", "a1"), consider the work of collect and toArray with itTask | Sample code | Result |
---|
Getting a list without duplicates | strings.stream (). distinct (). collect (Collectors.toList ()) | [a1, b2, c3] |
Get an array of strings without duplicates and in upper case | strings.stream (). distinct (). map (String :: toUpperCase) .toArray (String [] :: new) | {A1, B2, C3} |
Combine all elements in one line through the separator: and wrap tags <b> ... </ b> | strings.stream (). collect (Collectors.joining (":", "<b>", "</ b>")) | <b> a1: b2: c3: a1 </ b> |
map, , | strings.stream().distinct().collect(Collectors.toMap((p) -> p.substring(0, 1), (p) -> p.substring(1, 2))) | {a=1, b=2, c=3} |
map, | strings.stream().collect(Collectors.groupingBy((p) -> p.substring(0, 1))) | {a=[a1, a1], b=[b2], c=[c3]} |
map, : | strings.stream().collect(Collectors.groupingBy((p) -> p.substring(0, 1), Collectors.mapping((p) -> p.substring(1, 2), Collectors.joining(":")))) | {a=1:1, b=2, c=3} |
3.10 Collector'a
In addition to the Collectors already defined in Collectors, you can also create your own Collector. Let's consider an example of how to create it.Method for defining a custom Collector: Collector<_, _, _> ollector = Collector.of( __, ___, ___, [___] );
As can be seen from the code above, for the implementation of your Collector, you need to define three or four methods (method_of the last_processing_accumulator is not necessary). Consider the following code, which we wrote before Java 8, to combine all the rows in the collection: StringBuilder b = new StringBuilder();
And similar code to be written in Java 8 String joinBuilder = strings.stream().collect( Collector.of( StringBuilder::new,
In general, the three methods are easy to understand from the code above, we wrote them practically at every processing of collections, but what is the connection_two_accumulator method? This is the method that is needed for parallel processing of Collector, in this case, when a parallel stream is used, the collection can be divided into two parts (or more parts), each of which will have its own StringBuilder battery and then it will be necessary to combine them, then the code to Java 8 with 2 threads it will be like this: StringBuilder b1 = new StringBuilder();
Let's write your analog Collectors.toList () to work with string stream:
Iv. Conclusion
That's all.
I hope my small cheat sheet for working with stream api was useful for you. All source code is on github , good luck writing good code.PS List of other articles where you can read more about Stream Api:1. Processing Data with Java SE 8 Streams, Part 1 from Oracle,2. Processing Data with Java SE 8 Streams, Part 2 from Oracle,3. Complete Java 8 manual StreamPPS I also advise you to look at my opensource project
useful-java-links - perhaps the most comprehensive collection of useful Java libraries, frameworks and Russian-language instructional videos.
There is also a similar English version of this project and I’m starting the opensource sub-project of Hello world to prepare a collection of simple examples for different Java libraries in one maven project (I will be grateful for any help).General table of contents 'cheat sheets'1. JPA and Hibernate in questions and answers2. Three hundred fifty most popular non-mobile Java opensource projects on github3. Collections in Java (standard, guava, apache, trove, gs-collections, and others4. Java Stream API5. Two hundred and fifty Russian-language teaching videos of lectures and reports on Java.6. List of useful links for Java programmer7 Typical tasks
7.1 Optimum way to convert an InputStream to a string7.2 The most productive way to bypass the Map, count the number of occurrences of the substring8. Libraries for working with Json (Gson, Fastjson, LoganSquare, Jackson, JsonPath and others)