Java core technology Volume II notes
Chapter 1: Java 8 stream Library
A collection of processes introduced in Java 8 that are "what to do, not how to do"
1.1 operation from iteration to flow
package com.package1; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.util.List; public class Test { public static void main(String[] args) throws IOException { var contents = new String(Files.readAllBytes(Paths.get("alice.txt")),StandardCharsets.UTF_8); List<String> words = List.of(contents.split("\\PL+")); //Inside is a regex regular expression, which represents non letters. Split means split. This method will return a String array with the following regex int count = 0; for(String w:words){ if(w.length() > 12){ count++; } } //Use stream long count2 = words.stream().filter(w -> w.length() > 12).count(); //Changing the serial stream to parallel stream allows the stream library to perform filtering and counting in parallel long count3 = words.parallelStream().filter(w -> w.length() > 12).count(); } }
There are significant differences between flow and set:
- Streams do not store elements.
- The operation of a stream does not modify its data source
- Stream operations are performed as lazily as possible. This means that an infinite stream can be manipulated
Flow follows the principle of "what to do rather than how to do", describes what needs to be done, and gives it to Java itself for implementation, giving Java optimization space. It is more convenient and faster than specifying the operation process by itself
- Create stream
- Conversion flow
- The application terminates the operation, resulting in a result
1.2 creation of flow
You can use the stream method of the Collection interface to convert any Collection into a stream. You can also use static stream Of method
Stream<String> words= Stream.of(contains.split("\\PL+")); //Of has a variable length and can build a stream with any number of arguments Stream<String> song = Stream.of("gently","down","the","stream"); //Using array Stream (array, from, to) can use some elements of the array to create a stream //Create a stream that does not contain any elements Stream<String> silence = Stream.empty();
When operating a stream, there is no set behind the stream to operate Therefore, it is wrong and meaningless to delete the argument of the stream
package com.package1; import java.io.IOException; import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Iterator; import java.util.List; import java.util.Spliterator; import java.util.Spliterators; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; public class Test { public static void main(String[] args) throws IOException { //The following shows most of the operations to create a stream Path path = Paths.get("../gutenberg/alice30.txt"); var contents = new String(Files.readAllBytes(path),StandardCharsets.UTF_8); Stream<String> words = Stream.of(contents.split("\\PL+")); //The of method produces a stream whose element is the given value //static <T> Stream<T> of(T... values); show("words",words); Stream<String> song = Stream.of("gently","down","the","stream"); show("song",song); Stream<String> silence = Stream.empty(); show("silence",silence); Stream<String> echos= Stream.generate(()->"Echo"); //The generate method generates an infinite stream, which is constructed by continuously calling function s //static <T> Stream<T> generate(Supplier<T> s) show("echos",echos); Stream<Double> randoms = Stream.generate(Math::random); //You can also directly use the method reference of the closure given by Java show("randoms",randoms); Stream<BigInteger> integers = Stream.iterate(BigInteger.ONE,n-> n.add(BigInteger.ONE)); //An infinite stream is generated. Its original includes seed. The value generated by f is called in seed and the value generated by f is called in the previous element //You can add a hasNext predicate to specify termination // static <T> Stream<T> iterate(T seed,UnaryOPerator<T> f) // static <T> Stream<T> iterate(T seed,Predicate<? super T> hasNext,UnaryOPerator<T> f) show("integers",integers); //A large number of methods in the Java API can generate streams. For example, the following method will divide the string in contents into words Stream<String> wordAnotherWay = Pattern.compile("\\PL+").splitAsStream(contents); show("wordAnotherWay",wordAnotherWay); //Files. The lines method returns a Stream containing all the lines in the file try(Stream<String> lines = Files.lines(path,StandardCharsets.UTF_8)){ show("lines",lines); } Iterable<Path> iterable = FileSystems.getDefault().getRootDirectories(); //If the iterable object held is not a collection, it can be converted into a stream through the following call Stream<Path> rootDirectories = StreamSupport.stream(iterable.spliterator(),false); show("rootDirections",rootDirectories); Iterator<Path> iterator = Paths.get("/usr/share/dict/words").iterator(); //If it is an Iterator object, you can convert it into a stream using the following statement Stream<Path> pathComponents = StreamSupport.stream(Spliterators.spliteratorUnknownSize( iterator, Spliterator.ORDERED),false); show("pathComponents",pathComponents); } public static <T> void show(String title, Stream<T> stream){ final int SIZE= 10; List<T> firstElements = stream.limit(SIZE+1).collect(Collectors.toList()); System.out.print(title+": "); for(int i = 0; i < firstElements.size(); i++){ if(i > 0){ System.out.print(", "); } if(i < SIZE){ System.out.print(firstElements.get(i)); } else{ System.out.print("..."); } } System.out.println(); } }
1.3 filter,map and flatMap methods
Stream<T> filter(Predicate<? super T> predicate); //Generates a stream that contains all elements in the current stream that meet the predicate conditions <R> Stream<R> map(Function<? super T,? extends R> mapper); //Generates a stream that contains the results of applying mapper to all elements in the current stream <R> Stream<R> flatmap(Function<? super T, ? extends Stream<? extends R>> mapper); //Generate a stream, which not only implements the function of map, but also connects the results together (note that each result here is a stream, that is, stream connection)
1.4 extract sub streams and aggregate streams
Stream<T> limit(long maxSize); //Generates a stream containing the first maxSize elements in the current stream Stream<T> skip(long n); //Discard the first n Stream<T> takeWhile(Predicate<? super T> predicate); //Generate a stream that meets the condition predicate Stream<T> dropWhile(Predicete<? super T> predicate); //A flow is generated, and the elements are elements other than those that do not meet the predicate condition static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b); //connect
1.5 other stream conversion
Stream<T> distinct(); //duplicate removal Stream<T> sorted(); Stream<T> sorted(Comparator<? super T> comparator); //Sorting, you can add your own sorting specifications Stream<T> peek(Consumer<? super T> action); //Generate a flow, which is the same as the original flow, and each element in it will be passed to action;
package com.package1; import java.util.Comparator; import java.util.stream.Stream; public class Test { public static void main(String[] args){ Stream<String> uniqueWords = Stream.of("Job","Job","Tom","Job").distinct(); uniqueWords.peek(e -> System.out.println(" "+e)); //Note that there is no output, because peek is an intermediate operation, and there is no termination operation, it is just "flow past". Add a termination operation such as toArray, and there will be output Stream<String> longestFirst = words.stream().sorted(Comparator.comparing(String::length).reversed()); Object[] powers = Stream.iterate(1.0,p -> p*2).peek(e -> System.out.println(" "+e)).limit(20).toArray(); } }
1.6 simple reduction
Reduction is a terminal operation
They do not return a Stream but an Optional type. The Optional type is a better way of missing a return value
The following are termination operations: Optional<T> max(Comparator<? super T> comparator); Optional<T> min(Comparator<? super T> comparator); return max,min, Optional<T> findFirst(); Optional<T> findAny(); Returns the first or any element. If the stream is empty, it returns an empty element Optional; boolean anyMacth(Predicate<? super T> predicate); boolean allMacth(Predicate<? super T> predicate); boolean nonMacth(Predicate<? super T> predicate); Any, all, no element satisfies the predicate return true
1.7 Optional type
1.7. 1 get the value of Optional
You can use the default value when there is no match
String result = optionalString.orElse("");
You can also load default values from the configuration file
String result = optionalString.orElseGet(() -> System.getProperty("myapp.default"));
You can also throw an exception
String result = optionalString.orElseThrow(IllegalStateException);
1.7. 2. Value of consumption option
opionalValue.ifPresent(v->Process v);
For example, if you add the value to a set when it exists, you can call
optionalValue.ifPresent(v -> result.add(v)); //or: optinalValue.ifPresent(result::add);
You can also perform one action when you are satisfied and another action when you are not satisfied
optionalValue.ifPresentOrElse( v->System.out.println("Found"+v), ()->logger.warning("No Match") );
1.7. 3 pipelined Optional value
You can use a map to convert values inside Optional
Optional<String> transformed = optionalString.map(String::toUpperCase);
You can directly use the map for related operations
optionalValue.map(result::add); //You can also add a filter Optional<String> transformed = optionalString.filter(s -> s.length() >= 8).map(String::toUpperCase);
You can use the or method to replace an empty option with an alternative option. It will be calculated in terms of inertia
Optional<String> result = optionalString.or(() -> alternatives.stream().findFirst());
1.7. 4 is not suitable for using Optional values
For the use of Optional, the following principles should be followed:
- Variables of type Optional should never be null
- Do not use the Optional type field, which will only create an additional object (the so-called field is actually the translation of "field", that is, the field we often say, or attribute)
- Do not place an Optional object in the collection and do not use it as the key of the map. The values should be collected directly
Calling methods such as Optional isPresent will complicate the problem handling
1.7. 5 create the value of Optional
static <T> Optional<T> of(T value); static <T> Optional<T> ofNullable(T value); //Both methods will generate an option with the value of value. The first method will throw a NullPointerException exception when value is empty, and the second method will automatically call the empty method to generate an empty option static <T> Optional<T> empty();
1.7. 6 function of creating Optional value with flatMap
<U> Optional<U> flatMap(Function<? super T, ? extends Optional<? extends U>> mapper); take mapper Apply to current Optional Value, or in the current Optional When null, returns a null value Optional
1.7. 7 convert Optional to stream
Stream converts the Optional method into one or two stream objects
package com.package1; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Optional; public class Test { public static void main(String[] args) throws IOException { var contents = new String(Files.readAllBytes(Paths.get("src/com/package1/demo.txt")), StandardCharsets.UTF_8); List<String> wordlist = List.of(contents.split("\\PL+")); Optional<String> optionalValue = wordlist.stream().filter(s -> s.contains("fred")).findFirst(); System.out.println(optionalValue.orElse("No Word") +" contains fred"); Optional<String> optionalString = Optional.empty(); String result = optionalString.orElse("N/A"); System.out.println("result: "+result); result = optionalString.orElseGet(() -> Locale.getDefault().getDisplayName()); System.out.println("result: "+result); try{ result = optionalString.orElseThrow(IllegalStateException::new); System.out.println("result: "+result); } catch (Throwable t){ t.printStackTrace(); } optionalValue = wordlist.stream().filter(s -> s.contains("red")).findFirst(); optionalValue.ifPresent(s -> System.out.println(s+"contains red")); var results = new HashSet<String>(); optionalValue.ifPresent(results::add); Optional<Boolean> added = optionalValue.map(results::add); System.out.println(added); System.out.println(inverse(4.0).flatMap(Test::squareRoot)); System.out.println(inverse(-1.0).flatMap(Test::squareRoot)); System.out.println(inverse(0.0).flatMap(Test::squareRoot)); Optional<Double> result2 = Optional.of(-4.0).flatMap(Test::inverse).flatMap(Test::squareRoot); System.out.println(result2); } public static Optional<Double> inverse(Double x){ return x == 0 ? Optional.empty() : Optional.of(1 / x); } public static Optional<Double> squareRoot(Double x){ return x < 0 ? Optional.empty() : Optional.of(Math.sqrt(x)); } }
1.8 collection results
package com.package1; import com.sun.source.tree.Tree; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; public class Test { public static void main(String[] args) throws IOException { Iterator<Integer> iter = Stream.iterate(0, n -> n+1).limit(10).iterator(); while(iter.hasNext()){ System.out.println(iter.next()); } Object[] numbers = Stream.iterate(0, n -> n+1).limit(10).toArray(); System.out.println("Object array:" + numbers+Arrays.toString(numbers)); //Note that since you cannot create a generic array at run time, the expression stream ToArray returns an Object array try{ var number = (Integer) numbers[0]; System.out.println("number:" + number); System.out.println("The following state will throw an Exception"); var number2 = (Integer[]) numbers; //An error will be reported when Object [] is cast because it is unable to identify whether it belongs to a subclass or the same class before and after the conversion }catch (ClassCastException e){ System.err.println(e); } Integer[] number3 = Stream.iterate(0, n -> n+1).limit(10).toArray(Integer[]::new); //The type can be specified by passing in the constructor System.out.println("Integer Array: "+ number3); Set<String> noVowelSet = noVowels().collect(Collectors.toSet()); show("noVowelSet",noVowelSet); TreeSet<String> noVowelTreeSet = noVowels().collect(Collectors.toCollection(TreeSet::new)); show("noVowelTreeSet",noVowelTreeSet); String result = noVowels().limit(10).collect(Collectors.joining()); System.out.println("Joining: "+result); result = noVowels().limit(10).collect(Collectors.joining(", ")); System.out.println("Joining with commas "+result); IntSummaryStatistics summary = noVowels().collect(Collectors.summarizingInt(String::length)); double averageWorldLength = summary.getAverage(); double maxWorldLength = summary.getMax(); System.out.println("averageWorldLength: "+averageWorldLength); System.out.println("maxWorldLength: "+maxWorldLength); System.out.println("Foreach: "); noVowels().limit(10).forEach(System.out::println); } public static Stream<String> noVowels() throws IOException { var contains = new String(Files.readAllBytes( Paths.get("src/com/package1/demo.txt")), StandardCharsets.UTF_8 ); List<String> wordList = List.of(contains.split("\\PL+")); Stream<String> words = wordList.stream(); return words.map(s -> s.replaceAll("[aeiouAEIOU]", "")); } public static <T > void show (String label, Set< T > set){ System.out.print(label+": "+set.getClass().getName()); System.out.println("["+set.stream().limit(10).map(Object::toString).collect(Collectors.joining(", "))); } }
1.9 collect into mapping table
package com.package1; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; public class Test { public static void main(String[] args) { Map<Integer, String> idToName = people().collect(Collectors.toMap(Person::getId,Person::getName)); System.out.println("idToName:"+idToName); Map<Integer, Person> idToPerson = people().collect(Collectors.toMap(Person::getId, Function.identity())); System.out.println("idToPerson: "+idToPerson.getClass().getName()+idToPerson); idToPerson = people().collect( Collectors.toMap(Person::getId,Function.identity(),(existingValue, newValue) -> { throw new IllegalStateException(); }, TreeMap::new) ); //The third argument describes the solution to the conflict between the original key value and the new key value (in fact, the default solution is to throw an exception). The fourth argument makes the method that should be Map produce TreeMap System.out.println("idToPerson: "+idToPerson.getClass().getName()+idToPerson); Stream<Locale> locals = Stream.of(Locale.getAvailableLocales()); Map<String,String> languageNames = locals.collect( Collectors.toMap( Locale::getDisplayLanguage, l -> l.getDisplayLanguage(l), (existingValue, newValue) -> existingValue, TreeMap::new ) ); System.out.println("languageNames: "+ languageNames); locals = Stream.of(Locale.getAvailableLocales()); Map<String, Set<String>> countryLanguageSets = locals.collect(Collectors.toMap( Locale::getDisplayCountry, l -> Set.of(l.getDisplayLanguage()), (a,b) ->{ Set<String> union = new HashSet<>(a); union.addAll(b); return union; } )); System.out.println("countryLanguageSets"+countryLanguageSets); } public static class Person { private int id; private String name; public Person(int id, String name) { this.id = id; this.name = name; } public int getId() { return id; } public String getName() { return name; } public String toString() { return getClass().getName() + "[id=" + id + "name=" + name + "]"; } } public static Stream<Person> people() { return Stream.of(new Person(100, "Peter"), new Person(1002, "Paul"), new Person(1003, "Mary")); } } result: ================================================================================= D:\Devlop\Environment\JDK-15.0.2\bin\java.exe "-javaagent:C:\Program Files\Devlop\IDE\JetBrains\IntelliJ IDEA 2021.1.2\lib\idea_rt.jar=50157:C:\Program Files\Devlop\IDE\JetBrains\IntelliJ IDEA 2021.1.2\bin" -Dfile.encoding=UTF-8 -classpath C:\Users\MuRuo\Desktop\Dev\IDEA\JavaRelearn\out\production\JavaRelearn com.package1.Test idToName:{100=Peter, 1002=Paul, 1003=Mary} idToPerson: java.util.HashMap{100=com.package1.Test$Person[id=100name=Peter], 1002=com.package1.Test$Person[id=1002name=Paul], 1003=com.package1.Test$Person[id=1003name=Mary]} idToPerson: java.util.TreeMap{100=com.package1.Test$Person[id=100name=Peter], 1002=com.package1.Test$Person[id=1002name=Paul], 1003=com.package1.Test$Person[id=1003name=Mary]} languageNames: {=, Upper Sorbian =hornjoserbšćina, Lower Sorbian=dolnoserbšćina, Esperanto=esperanto, Dongsanghai language=Koyraboro senni, Middle Kurdish=کوردیی ناوەندی, chinese=chinese, Danish=dansk, Ukrainian=українська, Uzbek=o'zbek, Urdu=اردو, =========Many words are omitted here============Makua=Makua, Manx =Gaelg, Malagasy=Malagasy, Marathi=मराठी, Malayalam=മലയാളം, Malay=Melayu, Maltese=Malti, Basay =Maa, Mazandelan=مازرونی, Khmer=ខ្មែរ, Luba Katanga=Tshiluba} countryLanguageSets{Brazil=[Spanish, Portuguese], Thailand=[Thai], =[, Wangdu, Uzbek, Samburu, Javanese, Cebuano , Rundi , Soga , Croatian, =========Many words are omitted here============Kuwait=[Arabic], Jamaica=[English], world=[Arabic, Yidiwen, Prussian, Esperanto, Wolapk, International language, English]} Process finished with exit code 0
1.10 groups and zones
A method of grouping the same values into groups
Map<String,List<Locale>> countryToLocales = locales.collect(Collector.groupingBy(Locale::getCountry)); //Call Locale::getCountry a classification function
When the classification function is an assertion function (that is, a function whose return value is Boolean), it is more efficient to use the partitionby method
Map<Boolean, List<Locale>> englishAndOtherLocales = locales.collect( Collectors.partitingBy(l -> l.getLanguage().equals("en"))); List<Locale> englishLocales = englishAndOtherLocales.get(true);
1.11 downstream collector
Each value of the mapping table generated by the groupingBy method is a list, and the downstream collector is used to process these lists
package com.package1; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; import static java.util.stream.Collectors.*; //Static introduction public class Test { public static void main(String[] args) throws IOException{ Stream<Locale> locales = Stream.of(Locale.getAvailableLocales()); locales = Stream.of(Locale.getAvailableLocales()); Map<String,Set<Locale>> countryToLocaleSet = locales.collect(groupingBy(Locale::getCountry,toSet())); System.out.println("countryToLocaleSet: "+countryToLocaleSet); locales = Stream.of(Locale.getAvailableLocales()); Map<String,Long> countryToLocaleCounts = locales.collect(groupingBy( Locale::getCountry,counting() )); System.out.println("countryToLocaleCounts: "+countryToLocaleCounts); Stream<City> cities = readCities("cities.txt"); Map<String,Integer> stateToCityPopulation = cities.collect(groupingBy( City::getState,summingInt(City::getPopulation) )); System.out.println("stateToCityPopulation: "+ stateToCityPopulation); cities = readCities("cities.txt"); Map<String,Optional<String>> stateToLongestCityName = cities.collect(groupingBy( City::getState,mapping(City::getName,maxBy(Comparator.comparing(String::length ))) )); locales = Stream.of(Locale.getAvailableLocales()); Map<String,Set<String>> countryToLanguages = locales.collect(groupingBy( Locale::getDisplayCountry,mapping(Locale::getDisplayLanguage,toSet()) )); cities = readCities("cities.txt"); Map<String,IntSummaryStatistics> stateToCityPopulationSummary = cities.collect( groupingBy(City::getState,summarizingInt(City::getPopulation)) ); System.out.println(stateToCityPopulationSummary.get("NY")); cities = readCities("cities.txt"); Map<String,String> stateToCityNames = cities.collect(groupingBy( City::getState,reducing("",City::getName,(s,t) -> s.length() == 0 ? t : s + "," +t) )); cities = readCities("cities.txt"); stateToCityNames = cities.collect(groupingBy(City::getState,mapping(City::getName,joining(",")))); } public static class City{ private String name; private String state; private int population; public City(String name, String state, int population) { this.name = name; this.state = state; this.population = population; } public String getName() { return name; } public String getState() { return state; } public int getPopulation() { return population; } } public static Stream<City> readCities(String filename) throws IOException{ return Files.lines(Paths.get(filename)).map(l -> l.split(", ")).map(a -> new City(a[0],a[1],Integer.parseInt(a[2]))); } }
1.12 reduction operation
For V1 op V2, V2 op V3, V3 op V4,..., vn op vn+1,..., op is called reduction operation. In practice, for example, sum, score, string connection, maximum and minimum values, union and intersection of sets, etc. (however, op operation can be combined. For example, reduction operation cannot be used if subtraction operation does not meet permutation)
There is usually a unit e such that e op x = x. for example, in an addition operation, the unit is 0
If the stream is empty, the unitary value is returned instead of the Optional class
List<Integer> values = ...; Optional<Integer> sum = values.stream().reduce((x,y) -> x+y); //It can also be written directly as reduce(Integer::sum);
List<Integer> values = ...; Integer sum = values.stream().reduce(0,(x,y) -> x+y);
int result = words.reduce(0,(total,word) -> total + world.length,(total1,total2) -> total1+total2); //The first lambda expression indicates that the number of each word is added, and the second lambda expression indicates that if multi-threaded operation, the results of each thread are added to form the final result
1.13 basic type flow
IntStream,LongStream,DoubleStream .... Used to store basic type values directly without using a wrapper
package com.package1; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; public class Test { public static void main(String[] args) throws IOException{ IntStream is1 = IntStream.generate(() ->(int)(Math.random()*100)); show("is1",is1); IntStream is2 = IntStream.range(5,10); show("is2",is2); IntStream is3 = IntStream.rangeClosed(5,10); //10 is included show("is3",is3); Path path = Paths.get("./src/com/package1/demo.txt"); var contents = new String((Files.readAllBytes(path)), StandardCharsets.UTF_8); Stream<String> words = Stream.of(contents.split("\\PL+")); IntStream is4 = words.mapToInt(String::length); //mapTo... Method maps each element according to the passed lambda expression and converts it into a basic type stream show("is4",is4); var sentences = "\uD835\uDD46 is the set of octonions"; System.out.println(sentences); IntStream codes =sentences.codePoints(); //The codePoints method of the CharSequence interface generates an IntStream composed of the UnionCode code of the character or the symbols of the UTF-16 encoding mechanism System.out.println(codes.mapToObj(c -> String.format("%X",c)).collect(Collectors.joining())); Stream<Integer> integers = IntStream.range(0,100).boxed(); //The boxed method converts a base type stream to an object stream IntStream is5 = integers.mapToInt(Integer::intValue); show("is5",is5); } public static void show(String title, IntStream stream){ final int SIZE = 10; int[] firstElements = stream.limit(SIZE+1).toArray(); System.out.print(title+": "); for(int i = 0; i < firstElements.length; i++){ if(i > 0){ System.out.print(", "); } if(i < SIZE){ System.out.print(firstElements[i]); } else{ System.out.print("..."); } } System.out.println(); } }
1.14 parallel flow
I don't understand. I'll chew it later