流的DoubleProperty的最小值作为DoubleProperty

huangapple go评论64阅读模式
英文:

Minimum of a Stream of DoubleProperty as a DoubleProperty

问题

有没有办法获取Stream(或ListSetArrayList)中DoubleProperty的最小值并将其写入DoubleProperty变量中?

特别是,我有一个包含JavaFX矩形的ArrayList,我想将这些矩形的xProperty()的最小值写入一个DoubleProperty变量中。我尝试了以下代码

import javafx.beans.property.DoubleProperty;
import javafx.scene.shape.Rectangle;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.stream.Stream;
public class Test4 {

    public static void main(String[] args) {

        Random rnd = new Random();

        List<Rectangle> rectangles = new ArrayList<>();

        for (int i = 0; i < 50; i++) {
            rectangles.add(new Rectangle(rnd.nextDouble(), rnd.nextDouble(), 100, 100));
        }

        Optional<DoubleProperty> minX = rectangles.stream().map(Rectangle::xProperty).min();

    }

}

然而,这行代码Stream<DoubleProperty> minX = rectangles.stream().map(Rectangle::xProperty).min(); 引发了以下错误:

'min(java.util.Comparator<? super javafx.beans.property.DoubleProperty>)' in 'java.util.stream.Stream' cannot be applied to '()'

有没有办法解决这个问题?

英文:

Is there any way to fetch the minimum of a Stream (or List, Set, ArrayList) of DoubleProperty and write it into a DoubleProperty variable?

In particular, I have an ArrayList of JavaFX Rectangles and I want to write the minimum of xProperty() of these Rectangles into a DoubleProperty variable. I tried the following code

import javafx.beans.property.DoubleProperty;
import javafx.scene.shape.Rectangle;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.stream.Stream;
public class Test4 {

    public static void main(String[] args) {

        Random rnd = new Random();

        List&lt;Rectangle&gt; rectangles = new ArrayList&lt;&gt;();

        for (int i = 0; i &lt; 50; i++) {
            rectangles.add(new Rectangle(rnd.nextDouble(), rnd.nextDouble(), 100, 100));
        }

        Optional&lt;DoubleProperty&gt; minX = rectangles.stream().map(Rectangle::xProperty).min();

    }

}

However, the line Stream&lt;DoubleProperty&gt; minX = rectangles.stream().map(Rectangle::xProperty).min(); raises the following error:

&#39;min(java.util.Comparator? super javafx.beans.property.DoubleProperty&gt;)&#39; in &#39;java.util.stream.Stream&#39; cannot be applied to &#39;()&#39;

Is there anyway to tackle this problem?

答案1

得分: 3

您遇到的具体错误是因为Stream.min()需要一个参数来指定如何对流中的元素排序。在这种情况下,您希望按其包装的double值对DoubleProperty进行排序,因此可以这样做:

Optional<DoubleProperty> minX = rectangles.stream()
    .map(Rectangle::xProperty())
    .min(Comparator.comparing(Double::getValue));

(获取x属性的流,然后选择具有最小值的属性)

或者等效地:

Optional<DoubleProperty> minX = rectangles.stream()
    .min(Comparator.comparing(Rectangle::getX))
    .map(Rectangle::xProperty());

(选择具有最小x值的矩形,然后获取其x属性)。

但是请注意,这些可能不会实现您想要的功能。最小的x值将在执行该代码的时间找到,并且您将获得包装该值的DoubleProperty。如果将另一个具有较小x值的矩形添加到列表中,您仍然引用原始的DoubleProperty;类似地,如果与接收的属性相对应的矩形的x值发生更改,以至于不再是最小值,则您的属性将不再表示最小值。

我假设您希望的是一个可以被观察的值(您可以向其注册监听器),并且如果列表发生更改或列表中的任何矩形的x属性发生更改,它将更新。

您可以创建一个可以实现此功能的绑定。您需要创建一个带有提取器的可观察列表,以便可以在列表更改或列表中的任何矩形的xProperty()更改时使绑定失效。

然后使用 Bindings.createDoubleBinding(),并使用一个计算最小值并绑定到列表的函数:

public static void main(String[] args) {

    Random rnd = new Random();

    // 创建带有提取器的列表:
    ObservableList<Rectangle> rectangles =
            FXCollections.observableArrayList(rect -> new Observable[] { rect.xProperty()});

    DoubleBinding minX = Bindings.createDoubleBinding(
            () -> rectangles.stream()
                    .mapToDouble(Rectangle::getX)
                    .min()
                    .orElse(Double.POSITIVE_INFINITY),
            rectangles
    );

    minX.addListener((obs, oldMin, newMin) -> System.out.println("Min X changed from " + oldMin + " to " + newMin));

    for (int i = 0; i < 50; i++) {
        rectangles.add(new Rectangle(rnd.nextDouble(), rnd.nextDouble(), 100, 100));
    }
}

DoubleBinding 可能适用于您需要的任何功能(类似于 DoubleProperty,它实现了 ObservableDoubleValue,提供了您可能需要的几乎所有API)。如果您确切地需要一个属性,只需执行以下操作:

DoubleProperty minXProperty = new SimpleDoubleProperty();
minXProperty.bind(minX);
英文:

The specific error you get is because Stream.min() requires a parameter specifying how to order the elements of the stream. In this case, you want to order the DoubleProperty by its wrapped double value, so you can do

Optional&lt;DoubleProperty&gt; minX = rectangles.stream()
    .map(Rectangle::xProperty)
    .min(Comparator.comparing(Double::getValue));

(get a stream of the x properties, and then choose the one with the minimum value)

Equivalently:

Optional&lt;DoubleProperty&gt; minX = rectangles.stream()
    .min(Comparator.comparing(Rectangle::getX))
    .map(Rectangle::xProperty);

(choose the rectangle with the minimum x, and then get its x property).

Note, however, that these probably don't do what you want. The minimum x value will be found at the time that code is executed, and you get the DoubleProperty that wraps that value. If another rectangle with a smaller x value is added to the list, you still have a reference to the original DoubleProperty; similarly if the x-value of the rectangle corresponding to the property you received is changed so it is no longer the minimum, then your property will no longer represent the minimum value.

I'm assuming that what you want is a value which can be observed (you can register listeners with it) and which updates if either the list changes or if the x property of any rectangles in the list change.

You can create a binding that does this. You need to create an observable list with an extractor so that the binding can be invalidated when either the list changes, or the xProperty() of any of the rectangles in the list change.

Then use Bindings.createDoubleBinding() with a function that computes the minimum value and binds to the list:

public static void main(String[] args) {

    Random rnd = new Random();

    // create list with an extractor:
    ObservableList&lt;Rectangle&gt; rectangles =
            FXCollections.observableArrayList(rect -&gt; new Observable[] { rect.xProperty()});


    DoubleBinding minX = Bindings.createDoubleBinding(
            () -&gt; rectangles.stream()
                    .mapToDouble(Rectangle::getX)
                    .min()
                    .orElse(Double.POSITIVE_INFINITY),
            rectangles
    );

    minX.addListener((obs, oldMin, newMin) -&gt; System.out.println(&quot;Min X changed from &quot;+oldMin+&quot; to &quot;+newMin));
    
    for (int i = 0; i &lt; 50; i++) {
        rectangles.add(new Rectangle(rnd.nextDouble(), rnd.nextDouble(), 100, 100));
    }
}

The DoubleBinding will probably work for anything you need (like a DoubleProperty it implements ObservableDoubleValue, which provides almost all the API you are likely to need). It you specifically need a property, just do

DoubleProperty minXProperty = new SimpleDoubleProperty();
minXProperty.bind(minX);

huangapple
  • 本文由 发表于 2023年6月19日 21:29:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/76507114.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定