ZetCode

Java DoubleSupplier 接口

最后修改时间:2025 年 4 月 16 日

java.util.function.DoubleSupplier 接口代表一个提供 double 类型结果的供应器。它是一个函数式接口,只有一个抽象方法 getAsDouble。这个接口不需要输入参数。

DoubleSupplier 是 Java 8 中添加的函数式编程实用程序的一部分。当您需要生成或提供 double 值而无需任何输入时,它非常有用。该接口通常用于惰性求值场景。

DoubleSupplier 接口概述

DoubleSupplier 接口包含一个抽象方法。关键方法 getAsDouble 返回一个 double 值。此接口中没有默认方法或静态方法。

@FunctionalInterface
public interface DoubleSupplier {
    double getAsDouble();
}

上面的代码展示了 DoubleSupplier 的简单结构。它使用 @FunctionalInterface 注解来表明其单一抽象方法的性质。该接口专门用于原始 double 类型值。

DoubleSupplier 的基本用法

使用 DoubleSupplier 的最简单方法是使用 lambda 表达式。我们在 getAsDouble 方法中定义如何生成 double 值。示例提供随机数。

Main.java
package com.zetcode;

import java.util.function.DoubleSupplier;

public class Main {

    public static void main(String[] args) {

        // Define a supplier that generates random numbers
        DoubleSupplier randomSupplier = () -> Math.random();
        
        // Get values from the supplier
        System.out.println("Random 1: " + randomSupplier.getAsDouble());
        System.out.println("Random 2: " + randomSupplier.getAsDouble());
        
        // Supplier with fixed value
        DoubleSupplier fixedSupplier = () -> 3.1415;
        System.out.println("Fixed value: " + fixedSupplier.getAsDouble());
    }
}

此示例演示了使用 lambda 表达式的 DoubleSupplier 的基本用法。randomSupplier 每次调用时都会生成新的随机数。fixedSupplier 总是返回相同的值。供应器是惰性求值的。

使用方法引用的 DoubleSupplier

当现有方法与接口匹配时,方法引用提供了一种创建 DoubleSupplier 实例的简洁方法。这适用于不带参数并返回 double 值的方法。

Main.java
package com.zetcode;

import java.util.function.DoubleSupplier;

public class Main {

    public static void main(String[] args) {

        // Create a supplier using method reference
        DoubleSupplier piSupplier = Math::PI;
        DoubleSupplier nanSupplier = Double::NaN;
        
        System.out.println("PI value: " + piSupplier.getAsDouble());
        System.out.println("NaN value: " + nanSupplier.getAsDouble());
        
        // Instance method reference
        Random random = new Random();
        DoubleSupplier randomSupplier = random::nextDouble;
        System.out.println("Random from instance: " + randomSupplier.getAsDouble());
    }
}

此示例展示了通过方法引用创建 DoubleSupplier。我们使用了 Math 和 Double 类的静态方法,以及 Random 的实例方法。方法引用使代码更简洁易读。

DoubleSupplier 在流生成中

DoubleSupplier 对于生成无限的 double 值流非常有用。DoubleStream.generate 方法接受一个 DoubleSupplier 来生成流元素。这使得值的惰性生成成为可能。

Main.java
package com.zetcode;

import java.util.function.DoubleSupplier;
import java.util.stream.DoubleStream;

public class Main {

    public static void main(String[] args) {

        // Create a supplier for sequence numbers
        double[] counter = {0.0};
        DoubleSupplier sequenceSupplier = () -> counter[0] += 1.0;
        
        // Generate stream of 5 sequence numbers
        DoubleStream.generate(sequenceSupplier)
            .limit(5)
            .forEach(System.out::println);
            
        // Random numbers stream
        DoubleStream.generate(Math::random)
            .limit(3)
            .forEach(d -> System.out.printf("Random: %.4f%n", d));
    }
}

此示例演示了 DoubleSupplier 在流生成中的应用。我们创建了一个递增计数器的序列供应器和一个随机数供应器。该流被限制以避免无限处理。每个元素都按需生成。

使用 DoubleSupplier 的惰性求值

DoubleSupplier 通过推迟值生成直到需要时启用惰性求值。这对于昂贵的计算或可能不使用值的情况非常有用。供应器充当一个值工厂。

Main.java
package com.zetcode;

import java.util.function.DoubleSupplier;

public class Main {

    public static void main(String[] args) {

        // Expensive computation wrapped in supplier
        DoubleSupplier expensiveCalc = () -> {
            System.out.println("Performing expensive calculation...");
            try {
                Thread.sleep(1000); // Simulate long computation
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return Math.PI * Math.E;
        };
        
        System.out.println("Supplier created, but no calculation yet");
        
        // Only calculate when actually needed
        if (args.length > 0) {
            System.out.println("Result: " + expensiveCalc.getAsDouble());
        }
    }
}

此示例展示了使用 DoubleSupplier 的惰性求值。昂贵的计算仅在调用 getAsDouble() 时执行。如果没有供应器,计算将在初始化期间立即发生。惰性求值提高了性能。

DoubleSupplier 用于配置值

DoubleSupplier 可以提供灵活的配置值。实际值可以在调用之间更改,或者在运行时确定。这对于动态配置系统非常有用。

Main.java
package com.zetcode;

import java.util.function.DoubleSupplier;
import java.time.LocalTime;

public class Main {

    public static void main(String[] args) {

        // Time-based discount factor
        DoubleSupplier discountSupplier = () -> {
            int hour = LocalTime.now().getHour();
            return hour >= 20 || hour < 6 ? 0.9 : 1.0; // 10% discount at night
        };
        
        System.out.println("Current discount factor: " + discountSupplier.getAsDouble());
        
        // Environment-based configuration
        DoubleSupplier configSupplier = () -> 
            "prod".equals(System.getenv("APP_ENV")) ? 1.0 : 0.5;
            
        System.out.println("Config value: " + configSupplier.getAsDouble());
    }
}

此示例演示了 DoubleSupplier 用于动态配置。折扣供应器根据当前时间更改值。配置供应器从环境变量读取。供应器实现了运行时值的确定。

组合 DoubleSuppliers

虽然 DoubleSupplier 没有内置的组合方法,但我们可以手动组合供应器来创建更复杂的值生成器。这允许从简单的供应器构建复杂的供应器。

Main.java
package com.zetcode;

import java.util.function.DoubleSupplier;

public class Main {

    public static void main(String[] args) {

        // Base suppliers
        DoubleSupplier randomSupplier = Math::random;
        DoubleSupplier fixedSupplier = () -> 10.0;
        
        // Combined supplier - average of random and fixed
        DoubleSupplier averageSupplier = () -> 
            (randomSupplier.getAsDouble() + fixedSupplier.getAsDouble()) / 2;
            
        System.out.println("Average value: " + averageSupplier.getAsDouble());
        
        // Conditional supplier
        boolean useHighPrecision = true;
        DoubleSupplier precisionSupplier = useHighPrecision 
            ? () -> Math.PI 
            : () -> 3.14;
            
        System.out.println("PI value: " + precisionSupplier.getAsDouble());
    }
}

此示例展示了如何组合 DoubleSuppliers。我们创建了一个平均值供应器,它结合了另外两个供应器。我们还演示了一个条件供应器,该供应器根据标志更改行为。组合实现了灵活的值生成。

DoubleSupplier 与其他供应器的比较

Java 提供了几个针对不同类型的供应器接口。DoubleSupplier 专门用于原始 double 类型,避免了装箱开销。其他供应器如 Supplier<Double> 适用于包装对象。

Main.java
package com.zetcode;

import java.util.function.DoubleSupplier;
import java.util.function.Supplier;

public class Main {

    public static void main(String[] args) {

        // Primitive double supplier
        DoubleSupplier primitiveSupplier = () -> Math.random();
        
        // Wrapper Double supplier
        Supplier<Double> wrapperSupplier = () -> Math.random();
        
        System.out.println("Primitive: " + primitiveSupplier.getAsDouble());
        System.out.println("Wrapper: " + wrapperSupplier.get());
        
        // Performance consideration
        long start = System.nanoTime();
        for (int i = 0; i < 1_000_000; i++) {
            primitiveSupplier.getAsDouble();
        }
        System.out.println("Primitive time: " + (System.nanoTime() - start)/1_000_000 + " ms");
        
        start = System.nanoTime();
        for (int i = 0; i < 1_000_000; i++) {
            wrapperSupplier.get();
        }
        System.out.println("Wrapper time: " + (System.nanoTime() - start)/1_000_000 + " ms");
    }
}

此示例将 DoubleSupplier 与 Supplier<Double> 进行了比较。原始版本避免了装箱开销,对于密集的数值运算更有效。当使用通用 API 时,需要包装器版本。

来源

Java DoubleSupplier 接口文档

在本文中,我们介绍了 Java DoubleSupplier 接口的基本方法和特性。理解这些概念对于函数式编程和 Java 应用程序中的高效数值运算至关重要。

作者

我叫 Jan Bodnar,是一位经验丰富的程序员,在编程领域拥有多年经验。我从 2007 年开始撰写编程文章,至今已撰写了 1400 多篇文章和 8 本电子书。凭借超过八年的教学经验,我致力于分享我的知识,并帮助其他人掌握编程概念。

列出所有Java教程