当前位置:网站首页>2021-04-12 - new features lambda expression and function functional interface programming

2021-04-12 - new features lambda expression and function functional interface programming

2022-07-08 01:17:00 Mengqi D Xiaowu

New characteristics Lambda Expression and Function Functional interface programming

Catalog

1、lambda stay JSR(Java Specification Requests)

2、lambda stay JLS(Java LanguageSpecifications)

3、lambda The expression in jvm standard

4、Stream flow

4.1 Two kinds of stream flow

4.2 Concise traversal

4.3 Filter

4.4 mapping

4.5 Look for the element

4.6 reduce Method

4.7 stream actual combat

5、Collectors The art of collection

5.1 Grouping

5.2 partitioningBy The collector

5.3 reduce

5.4 Summarizing

6、 Summary


Preface :jdk1.8 Launched lambda Expression and functional interface programming , This paper mainly from java in JSR Requirements specification to JLS The language specification goes to JVM standard , Introduce lambda and function.

1、lambda stay JSR(Java Specification Requests)

First , If you want to know lambda, Start with the requirements specification , We from JSR Requirements can be seen ,JSR335 That is to say lambda Requirements specification , We can do it download, Open as shown in the figure below :

Above ,Overview in , The overview defines the following functions :

  • Lambda expressions and method references——lambda Expressions and method references
  • Enhanced type inference and target typing—— Enhance type inference and target type conversion
  • Default and static methods in interfaces—— Default and static methods in the interface

These three new features, It stipulates our lambda;jdk in util New API, also , Use a lot of util Under the stream, About function As shown in the figure below :

Contents The content includes the following three points :

  • Specification changes , It's mainly about Java Update of language specification , And support for Java Virtual machine specification ( here ) and Java Object serialization specification ( here ) Change of
  • Java SE API Update summary
  • Informal about new languages and library functions ( Non normative ) summary

2、lambda stay JLS(Java LanguageSpecifications)

stay JLS standard in , We can have a look lambda Canonical definition of expression :

Defined ,lambda Expression in assignment context 、 Call context 、 Cast context , Otherwise, the compiler will report an error .

The evaluation of the expression will produce a function Interface instance . The evaluation of an expression does not necessarily lead to the execution of the body of the expression , contrary , This may happen later when the appropriate method of the functional interface is called .

Next is ,Lambda Parameters and  Lambda Body, Explicit and implicit parameter types ; If the formal parameter has an inferred type , Then these types are from lambda Derived from the function interface type for which the expression is directed .

More details , Let's not go over it here . Interested students can check the official website by themselves .

3、lambda The expression in jvm standard

For example, we aim at one class View its bytecode file ,javap -v Filtering.class.

It's obvious , Functional interfaces at the bottom of bytecode are InvokeDynamic, Through the following jdk You can see in the source code , The bottom is newInvokeDynamicItem, When class build when Put it into the constant pool .

invokedynamic The execution method of this opcode will be associated with a dynamic call point object (Call Site object), This call site The object will point to a specific bootstrap Method ( The binary byte stream information of the method is BootstrapMethods In the property sheet ) Implementation ,invokedynamic The call of instructions will have a unique call chain , Unlike other instructions that call methods directly , The actual operation process is also relatively more complex .

   /**
     * Adds an invokedynamic reference to the constant pool of the class being
     * build. Does nothing if the constant pool already contains a similar item.
     * <i>This method is intended for {@link Attribute} sub classes, and is
     * normally not needed by class generators or adapters.</i>
     *
     * @param name
     *            name of the invoked method.
     * @param desc
     *            descriptor of the invoke method.
     * @param bsm
     *            the bootstrap method.
     * @param bsmArgs
     *            the bootstrap method constant arguments.
     *
     * @return a new or an already existing invokedynamic type reference item.
     */
    Item newInvokeDynamicItem(final String name, final String desc,
            final Handle bsm, final Object... bsmArgs) {
        // cache for performance
        ByteVector bootstrapMethods = this.bootstrapMethods;
        if (bootstrapMethods == null) {
            bootstrapMethods = this.bootstrapMethods = new ByteVector();
        }

        int position = bootstrapMethods.length; // record current position

Execute by the following method ,// (site.)invokedynamic(a*):R => mh = site.getTarget(); mh.invokeBasic(a*). At the bottom of the stack // NA, //INVOKEDYNAMIC, // visitInvokeDynamicInsn.

In the underlying jvm in generateOopMap.cpp, It can be seen as follows .

Included in Frame.java Stack in .

case Opcodes.INVOKEDYNAMIC:
            pop(item.strVal2);
            push(cw, item.strVal2);
            break;

No more details , Interested students can learn more by themselves .

4、Stream flow

The definition of flow : A sequence of elements generated from sources that support data processing operations

Stream API Basically return Stream In itself , So many operations can be connected in series into a pipe , Just like the flow style (fluent style). This optimizes the operation , For example, delay execution (laziness) And short circuit ( short-circuiting)

4.1 Two kinds of stream flow

•stream() − Create a serial stream for the collection .
•parallelStream() − Create parallel streams for collections .

4.2 Concise traversal

stream Provides a way of internal traversal forEach(), adopt forEach It can greatly simplify the code of collection traversal

4.3 Filter

stream Provides a method for data filtering filter(), And stream Other API When used together, it can simply realize data filtering

public class Filtering {

    public static void main(String...args){

        // Filter out vegetables 
        List<Dish> vegetarianMenu =
            menu.stream()
                //filter Parameters are predicates    That is to say boolean type 
                .filter(Dish::isVegetarian)   //method reference
                .collect(toList());
        vegetarianMenu.forEach(System.out::println);

        //  Filter the specified elements   duplicate removal 
        List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
        numbers.stream()
               .filter(i -> i % 2 == 0)
               .distinct()// duplicate removal 
               .forEach(System.out::println);

        //limit  Truncation stream 
        List<Dish> dishesLimit3 =
            menu.stream()
                .filter(d -> d.getCalories() > 300)
                .limit(3)
                .collect(toList());

        dishesLimit3.forEach(System.out::println);

        //skip: skip n Elements 
        List<Dish> dishesSkip2 =
            menu.stream()
                .filter(d -> d.getCalories() > 300)
                .skip(2)
                .collect(toList());

        dishesSkip2.forEach(System.out::println);
    }

public static final List<Dish> menu =
            Arrays.asList( new Dish("pork", false, 800, Type.MEAT),
                           new Dish("beef", false, 700, Type.MEAT),
                           new Dish("chicken", false, 400, Type.MEAT),
                           new Dish("french fries", true, 530, Type.OTHER),
                           new Dish("rice", true, 350, Type.OTHER),
                           new Dish("season fruit", true, 120, Type.OTHER),
                           new Dish("pizza", true, 550, Type.OTHER),
                           new Dish("prawns", false, 400, Type.FISH),
                           new Dish("salmon", false, 450, Type.FISH));
}

4.4 mapping

stream Provides a method for data mapping map(), And stream Other API When used together, data mapping can be easily realized

public class Mapping {

    public static void main(String...args){

        // map
        List<String> dishNames = menu.stream()
                                     .map(Dish::getName)
//                                     .map(dish -> dish.getName())
                                     .collect(toList());
        System.out.println(dishNames);

        // map
        List<String> words = Arrays.asList("Hello", "World");
        List<Integer> wordLengths = words.stream()
                                         .map(String::length)
                                         .collect(toList());
        System.out.println(wordLengths);

        // flatMap
        words.stream()
             // Cut into single words 
                 .flatMap((String line) -> Arrays.stream(line.split("")))
                 .distinct()// duplicate removal 
                 .forEach(System.out::println);

        // flatMap
        List<Integer> numbers1 = Arrays.asList(1,2,3,4,5);
        List<Integer> numbers2 = Arrays.asList(6,7,8);
        List<int[]> pairs =
                        numbers1.stream()
                                // Two list In the middle, two vertical pairs are combined to form a two-dimensional array 
                                .flatMap((Integer i) -> numbers2.stream().map((Integer j) -> new int[]{i, j})
                                 )
                                // Filter two-dimensional arrays   The sum of the two columns is 3 Multiple 
                                .filter(pair -> (pair[0] + pair[1]) % 3 == 0)
                                .collect(toList());
        pairs.forEach(pair -> System.out.println("(" + pair[0] + ", " + pair[1] + ")"));
    }
}

4.5 Look for the element

public class Finding {

    public static void main(String...args){
        if(isVegetarianFriendlyMenu()){
            System.out.println("Vegetarian friendly");
        }

        System.out.println(isHealthyMenu());
        System.out.println(isHealthyMenu2());
        
        Optional<Dish> dish = findVegetarianDish();
        dish.ifPresent(d -> System.out.println(d.getName()));
    }

    // amount to ||
    private static boolean isVegetarianFriendlyMenu(){
        return menu.stream().anyMatch(Dish::isVegetarian);
    }

    // amount to  &&
    private static boolean isHealthyMenu(){
        return menu.stream().allMatch(d -> d.getCalories() < 1000);
    }

    // amount to  !=
    private static boolean isHealthyMenu2(){
        return menu.stream().noneMatch(d -> d.getCalories() >= 1000);
    }

    // Match any one   Applicable to concurrent flow execution 
    private static Optional<Dish> findVegetarianDish(){
        return menu.stream().filter(Dish::isVegetarian).findAny();
    }
    
}

4.6 reduce Method

reduce Method reduces the flow to a value . In terms of functional programming language , This is called folding (fold)
public class Reducing {

    public static void main(String...args){

        List<Integer> numbers = Arrays.asList(3,4,5,1,2);
        // grammar  reduce( The starting point ,  Algorithm rules );
        int sum = numbers.stream().reduce(0, (a, b) -> a + b);
        System.out.println(sum);

        int sum2 = numbers.stream().reduce(0, Integer::sum);
        System.out.println(sum2);

        int max = numbers.stream().reduce(0, (a, b) -> Integer.max(a, b));
        System.out.println(max);

        Optional<Integer> min = numbers.stream().reduce(Integer::min);
        min.ifPresent(System.out::println);

        int calories = menu.stream()
                           .map(Dish::getCalories)
                           .reduce(0, Integer::sum);
        System.out.println("Number of calories:" + calories);
    }
}

4.7 stream actual combat

public class PuttingIntoPractice {
    public static void main(String...args){
        Trader raoul = new Trader("Raoul", "Cambridge");
        Trader mario = new Trader("Mario","Milan");
        Trader alan = new Trader("Alan","Cambridge");
        Trader brian = new Trader("Brian","Cambridge");
		
		List<Transaction> transactions = Arrays.asList(
            new Transaction(brian, 2011, 300),
        new Transaction(raoul, 2012, 1000),
                new Transaction(raoul, 2011, 400),
                new Transaction(mario, 2012, 710),
                new Transaction(mario, 2012, 700),
                new Transaction(alan, 2012, 950)
        );	
        
        
        // Query 1: Find all transactions from year 2011 and sort them by value (small to high).
        List<Transaction> tr2011 = transactions.stream()
                                               .filter(transaction -> transaction.getYear() == 2011)
                                               .sorted(comparing(Transaction::getValue))
                                               .collect(toList());
        System.out.println(tr2011);
        
        // Query 2: What are all the unique cities where the traders work?
        List<String> cities =
            transactions.stream()
                        .map(transaction -> transaction.getTrader().getCity())
                        .distinct()
                        .collect(toList());
        System.out.println(cities);

        // Query 3: Find all traders from Cambridge and sort them by name.
        
        List<Trader> traders =
            transactions.stream()
                        .map(Transaction::getTrader)
                        .filter(trader -> trader.getCity().equals("Cambridge"))
                        .distinct()
                        .sorted(comparing(Trader::getName))
                        .collect(toList());
        System.out.println(traders);
        
        
        // Query 4: Return a string of all traders’ names sorted alphabetically.
        
        String traderStr =
            transactions.stream()
                        .map(transaction -> transaction.getTrader().getName())
                        .distinct()
                        .sorted()
                        .reduce("", (n1, n2) -> n1 + n2);
        System.out.println(traderStr);
        
        // Query 5: Are there any trader based in Milan?
        
        boolean milanBased =
            transactions.stream()
                        .anyMatch(transaction -> transaction.getTrader()
                                                            .getCity()
                                                            .equals("Milan")
                                 );
        System.out.println(milanBased);
        
        
        // Query 6: Update all transactions so that the traders from Milan are set to Cambridge.
        transactions.stream()
                    .map(Transaction::getTrader)
                    .filter(trader -> trader.getCity().equals("Milan"))
                    .forEach(trader -> trader.setCity("Cambridge"));
        System.out.println(transactions);
        
        
        // Query 7: What's the highest value in all the transactions?
        int highestValue = 
            transactions.stream()
                        .map(Transaction::getValue)
                        .reduce(0, Integer::max);
        System.out.println(highestValue);
    }
}
public  class Trader {
	
	private String name;
	private String city;

	public Trader(String n, String c){
		this.name = n;
		this.city = c;
	}

	public String getName(){
		return this.name;
	}

	public String getCity(){
		return this.city;
	}

	public void setCity(String newCity){
		this.city = newCity;
	}

	@Override
	public String toString(){
		return "Trader:"+this.name + " in " + this.city;
	}
}
public class Transaction {

	private Trader trader;
	private int year;
	private int value;

	public Transaction(Trader trader, int year, int value)
	{
		this.trader = trader;
		this.year = year;
		this.value = value;
	}

	public Trader getTrader(){ 
		return this.trader;
	}

	public int getYear(){
		return this.year;
	}

	public int getValue(){
		return this.value;
	}
	
	@Override
	public String toString(){
	    return "{" + this.trader + ", " +
	           "year: "+this.year+", " +
	           "value:" + this.value +"}";
	}
}

5、Collectors The art of collection

Collectors Class implements many reduction operations , For example, converting streams to collections and aggregate elements .Collectors Can be used to return a list or string

5.1 Grouping

Multi level grouping 、 Various complex groupings

public class Grouping {

    enum CaloricLevel { DIET, NORMAL, FAT };

    public static void main(String... args) {
        System.out.println("Dishes grouped by type: " + groupDishesByType());
        System.out.println("Dish names grouped by type: " + groupDishNamesByType());
        System.out.println("Dish tags grouped by type: " + groupDishTagsByType());
        System.out.println("Caloric dishes grouped by type: " + groupCaloricDishesByType());
        System.out.println("Dishes grouped by caloric level: " + groupDishesByCaloricLevel());
        System.out.println("Dishes grouped by type and caloric level: " + groupDishedByTypeAndCaloricLevel());
        System.out.println("Count dishes in groups: " + countDishesInGroups());
        System.out.println("Most caloric dishes by type: " + mostCaloricDishesByType());
        System.out.println("Most caloric dishes by type: " + mostCaloricDishesByTypeWithoutOprionals());
        System.out.println("Sum calories by type: " + sumCaloriesByType());
        System.out.println("Caloric levels by type: " + caloricLevelsByType());
    }

    // to groupingBy Method passes a Function( In the form of method references ), It extracts every single channel in the stream Dish Of Dish.Type.
    // So let's take this Function It's called the classification function , Because it's used to divide the elements in the stream into different groups 
    private static Map<Dish.Type, List<Dish>> groupDishesByType() {
        return menu.stream().collect(groupingBy(Dish::getType));
    }

    // Multi level grouping , You can use a two parameter version of Collectors.groupingBy Collector created by factory method ,
    // In addition to ordinary classification functions , It's acceptable collector The second parameter of type .
    private static Map<Dish.Type, List<String>> groupDishNamesByType() {
        return menu.stream().collect(groupingBy(Dish::getType, mapping(Dish::getName, toList())));
    }

    private static Map<Dish.Type, Set<String>> groupDishTagsByType() {
        return menu.stream().collect(
            groupingBy(Dish::getType,
                flatMapping(dish -> dishTags.get( dish.getName() ).stream(), toSet())
            )
        );
    }

    private static Map<Dish.Type, List<Dish>> groupCaloricDishesByType() {
//        return menu.stream().filter(dish -> dish.getCalories() > 500).collect(groupingBy(Dish::getType));
        return menu.stream().collect(groupingBy(Dish::getType, filtering(dish -> dish.getCalories() > 500, toList())));
    }

    private static Map<CaloricLevel, List<Dish>> groupDishesByCaloricLevel() {
        return menu.stream().collect(
                groupingBy(dish -> {
                    if (dish.getCalories() <= 400) return CaloricLevel.DIET;
                    else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
                    else return CaloricLevel.FAT;
                } ));
    }

    private static Map<Dish.Type, Map<CaloricLevel, List<Dish>>> groupDishedByTypeAndCaloricLevel() {
        return menu.stream().collect(
                groupingBy(Dish::getType,
                        groupingBy((Dish dish) -> {
                            if (dish.getCalories() <= 400) return CaloricLevel.DIET;
                            else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
                            else return CaloricLevel.FAT;
                        } )
                )
        );
    }

    private static Map<Dish.Type, Long> countDishesInGroups() {
        return menu.stream().collect(groupingBy(Dish::getType, counting()));
    }

    private static Map<Dish.Type, Optional<Dish>> mostCaloricDishesByType() {
        return menu.stream().collect(
                groupingBy(Dish::getType,
                        reducing((Dish d1, Dish d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2)));
    }

    private static Map<Dish.Type, Dish> mostCaloricDishesByTypeWithoutOprionals() {
        return menu.stream().collect(
                groupingBy(Dish::getType,
                        collectingAndThen(
                                reducing((d1, d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2),
                                Optional::get)));
    }

    private static Map<Dish.Type, Integer> sumCaloriesByType() {
        return menu.stream().collect(groupingBy(Dish::getType,
                summingInt(Dish::getCalories)));
    }

    // Customize the internal implementation 
    private static Map<Dish.Type, Set<CaloricLevel>> caloricLevelsByType() {
        return menu.stream().collect(
            groupingBy(Dish::getType, mapping(
                dish -> {
                    if (dish.getCalories() <= 400) return CaloricLevel.DIET;
                    else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
                    else return CaloricLevel.FAT;
                },
                toSet())));
    }
}
public class Dish {

    private final String name;
    private final boolean vegetarian;
    private final int calories;
    private final Type type;

    public Dish(String name, boolean vegetarian, int calories, Type type) {
        this.name = name;
        this.vegetarian = vegetarian;
        this.calories = calories;
        this.type = type;
    }

    public String getName() {
        return name;
    }

    public boolean isVegetarian() {
        return vegetarian;
    }

    public int getCalories() {
        return calories;
    }

    public Type getType() {
        return type;
    }

    public enum Type { MEAT, FISH, OTHER }

    @Override
    public String toString() {
        return name;
    }

    public static final List<Dish> menu =
            Arrays.asList( new Dish("pork", false, 800, Type.MEAT),
                           new Dish("beef", false, 700, Type.MEAT),
                           new Dish("chicken", false, 400, Type.MEAT),
                           new Dish("french fries", true, 530, Type.OTHER),
                           new Dish("rice", true, 350, Type.OTHER),
                           new Dish("season fruit", true, 120, Type.OTHER),
                           new Dish("pizza", true, 550, Type.OTHER),
                           new Dish("prawns", false, 400, Type.FISH),
                           new Dish("salmon", false, 450, Type.FISH));

    public static final Map<String, List<String>> dishTags = new HashMap<>();

    static {
        dishTags.put("pork", asList("greasy", "salty"));
        dishTags.put("beef", asList("salty", "roasted"));
        dishTags.put("chicken", asList("fried", "crisp"));
        dishTags.put("french fries", asList("greasy", "fried"));
        dishTags.put("rice", asList("light", "natural"));
        dishTags.put("season fruit", asList("fresh", "natural"));
        dishTags.put("pizza", asList("tasty", "salty"));
        dishTags.put("prawns", asList("tasty", "roasted"));
        dishTags.put("salmon", asList("delicious", "fresh"));
    }
}

5.2 partitioningBy The collector

partitioningBy The collector : By a predicate ( A function that returns a Boolean value ) As a classification function , It's called a partition function . Advantages of zoning : Is that the partition function returns true or false Two sets of stream element lists of , You can partition twice 
public class Partitioning {

    public static void main(String... args) {
        System.out.println("Dishes partitioned by vegetarian: " + partitionByVegeterian());
        System.out.println("Vegetarian Dishes by type: " + vegetarianDishesByType());
        System.out.println("Most caloric dishes by vegetarian: " + mostCaloricPartitionedByVegetarian());
    }

    private static Map<Boolean, List<Dish>> partitionByVegeterian() {
        return menu.stream().collect(partitioningBy(Dish::isVegetarian));
    }

    private static Map<Boolean, Map<Dish.Type, List<Dish>>> vegetarianDishesByType() {
        return menu.stream().collect(partitioningBy(Dish::isVegetarian, groupingBy(Dish::getType)));
    }

    // Veggies and non veggies have the most calories map aggregate 
    // Demonstrate secondary partitioning 
    private static Object mostCaloricPartitionedByVegetarian() {
        return menu.stream().collect(
                partitioningBy(Dish::isVegetarian,
                        collectingAndThen(
                                maxBy(comparingInt(Dish::getCalories)),
                                Optional::get)));
    }
}

5.3 reduce

public class Reducing {

    public static void main(String... args) {
        System.out.println("Total calories in menu: " + calculateTotalCalories());
        System.out.println("Total calories in menu: " + calculateTotalCaloriesWithMethodReference());
        System.out.println("Total calories in menu: " + calculateTotalCaloriesWithoutCollectors());
        System.out.println("Total calories in menu: " + calculateTotalCaloriesUsingSum());
    }

    private static int calculateTotalCalories() {
        return menu.stream().collect(reducing(0, Dish::getCalories, (Integer i, Integer j) -> i + j));
    }

    private static int calculateTotalCaloriesWithMethodReference() {
        return menu.stream().collect(reducing(0, Dish::getCalories, Integer::sum));
    }

    private static int calculateTotalCaloriesWithoutCollectors() {
        return menu.stream().map(Dish::getCalories).reduce(Integer::sum).get();
    }

    private static int calculateTotalCaloriesUsingSum() {
        return menu.stream().mapToInt(Dish::getCalories).sum();
    }
}

5.4 Summarizing

public class Summarizing {

    public static void main(String... args) {
        System.out.println("Nr. of dishes: " + howManyDishes());
        System.out.println("The most caloric dish is: " + findMostCaloricDish());
        System.out.println("The most caloric dish is: " + findMostCaloricDishUsingComparator());
        System.out.println("Total calories in menu: " + calculateTotalCalories());
        System.out.println("Average calories in menu: " + calculateAverageCalories());
        System.out.println("Menu statistics: " + calculateMenuStatistics());
        System.out.println("Short menu: " + getShortMenu());
        System.out.println("Short menu comma separated: " + getShortMenuCommaSeparated());
    }


    private static long howManyDishes() {
        return menu.stream().collect(counting());
    }

    private static Dish findMostCaloricDish() {
        return menu.stream().collect(reducing((d1, d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2)).get();
    }

    private static Dish findMostCaloricDishUsingComparator() {
        Comparator<Dish> dishCaloriesComparator = Comparator.comparingInt(Dish::getCalories);
        BinaryOperator<Dish> moreCaloricOf = BinaryOperator.maxBy(dishCaloriesComparator);
        return menu.stream().collect(reducing(moreCaloricOf)).get();
    }

    private static int calculateTotalCalories() {
        return menu.stream().collect(summingInt(Dish::getCalories));
    }

    private static Double calculateAverageCalories() {
        return menu.stream().collect(averagingInt(Dish::getCalories));
    }

    private static IntSummaryStatistics calculateMenuStatistics() {
        return menu.stream().collect(summarizingInt(Dish::getCalories));
    }

    private static String getShortMenu() {
        return menu.stream().map(Dish::getName).collect(joining());
    }

    private static String getShortMenuCommaSeparated() {
        return menu.stream().map(Dish::getName).collect(joining(", "));
    }
}

6、 Summary

Through the above, , Basically, I have a preliminary understanding lambda How expressions are defined 、 analysis 、 perform . Not only Lambda It's very convenient to use , Performance is also better than anonymous inner classes in most cases . We're writing lambda Expression time , Try to write the code of the introduction , Reduce internal variable capture ( Because this will create additional variable objects ). I hope this article can give you some reference .

 

 

 

 

 

 

 

 

 

 

原网站

版权声明
本文为[Mengqi D Xiaowu]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202130545511234.html