Java 从另一个ObservableList导出映射到ObservableList的不同值?
我有一个有趣的问题,我对JavaFX比较陌生,我需要创建一个有点小的Java 从另一个ObservableList导出映射到ObservableList的不同值?,java,javafx,javafx-8,Java,Javafx,Javafx 8,我有一个有趣的问题,我对JavaFX比较陌生,我需要创建一个有点小的observelist实现 本质上,我需要一个observebleList,它维护另一个observebleList的映射派生值列表。我需要创建一个接受另一个ObservableList和一个函数lambda作为其构造函数参数的observableStincList。observatedistinclist为observatedList中的每个元素维护应用的函数的不同值列表 例如,假设我有observeListFlights,有
observelist
实现
本质上,我需要一个observebleList
,它维护另一个observebleList
的映射派生值列表。我需要创建一个接受另一个ObservableList
和一个函数
lambda作为其构造函数参数的observableStincList
。observatedistinclist
为observatedList
中的每个元素维护应用的函数的不同值列表
例如,假设我有observeListFlights
,有以下实例
Flt # Carrier Orig Dest Dep Date
174 WN ABQ DAL 5/6/2015
4673 WN DAL HOU 5/6/2015
485 DL DAL PHX 5/7/2015
6758 UA JFK HOU 5/7/2015
如果我创建了一个新的ObservabledStinctList,列出每个飞行对象的载体值,我将在客户端这样做
ObservableDistinctList<Flight,String> distinctCarriers = new
ObservableDistinctList(flights, f -> f.getCarrier());
如果航班被添加到航班
,则在添加之前,它将首先检查新的不同值是否实际存在。因此,新的WN
航班不会导致添加到distinctCarriers
列表中,但AA
航班会。相反,如果从航班
中删除航班,则需要检查其他实例是否会在删除之前保留该值。从flights
中删除WN航班不会导致从distinctcarries
列表中删除WN
,但删除DL
航班将导致其删除
这是我的实现。我是否正确实现了ListChangeListener
?我对列表可变性感到非常不舒服,所以我想在我把它应用到我的项目之前张贴。另外,我是否需要担心使用ArrayList
来支持线程安全
public final class ObservableDistinctList<P,V> extends ObservableListBase<V> {
private final ObservableList<P> parentList;
private final Function<P,V> valueExtractor;
private final List<V> values;
public ObservableDistinctList(ObservableList<P> parentList, Function<P,V> valueExtractor) {
this.parentList = parentList;
this.valueExtractor = valueExtractor;
this.values = parentList.stream().map(p -> valueExtractor.apply(p)).distinct().collect(Collectors.toList());
this.parentList.addListener((ListChangeListener.Change<? extends P> c) -> {
while (c.next()) {
if (c.wasRemoved()) {
final Stream<V> candidatesForRemoval = c.getRemoved().stream().map(p -> valueExtractor.apply(p));
final List<V> persistingValues = parentList.stream().map(p -> valueExtractor.apply(p)).distinct().collect(Collectors.toList());
final Stream<V> valuesToRemove = candidatesForRemoval.filter(v -> ! persistingValues.contains(v));
valuesToRemove.forEach(v -> values.remove(v));
}
if (c.wasAdded()) {
final Stream<V> candidatesForAdd = c.getAddedSubList().stream().map(p -> valueExtractor.apply(p));
final List<V> existingValues = parentList.stream().map(p -> valueExtractor.apply(p)).distinct().collect(Collectors.toList());
final Stream<V> valuesToAdd = candidatesForAdd.filter(v -> ! values.contains(v));
valuesToAdd.forEach(v -> values.add(v));
}
}
});
}
@Override
public V get(int index) {
return values.get(index);
}
@Override
public int size() {
return values.size();
}
}
public final类ObservabledStincList扩展了ObservableListBase{
私人最终观察者parentList;
私有最终函数值提取程序;
私有最终列表值;
公共ObservableStinctList(ObservableList
parentList,函数值提取器){
this.parentList=父列表;
this.valueExtractor=valueExtractor;
this.values=parentList.stream().map(p->valueExtractor.apply(p)).distinct().collect(Collectors.toList());
this.parentList.addListener((下面的ListChangeListener.Change是一个简单的示例(加上驱动程序-提示:这是您应该在问题中提供的:-)自定义ObservableList,它在源列表中保留元素属性的不同值。它在添加/删除项时保持自身与源同步。同步通过以下方式实现:
- 侦听源的列表更改
- 当收到一个被删除的消息时:如果被删除的消息是最后一个具有不同属性的消息,请从自己的消息中删除该属性,并通知其自己的侦听器该消息已被删除。否则,就没有什么可做的了
- 当收到一个added时:如果added是第一个具有distinct属性的,则将该属性添加到自己(在末尾),并将添加通知其自己的侦听器。否则,就没有什么可做的了
根据需要,通过消息传递实用工具方法nextRemove/nextAdd来处理通知
/**
* Example of how to implement a custom ObservableList.
*
* Here: an immutable and unmodifiable (in itself) list containing distinct
* values of properties of elements in a backing list, the values are extracted
* via a function
*/
public class DistinctMapperDemo extends Application {
public static class DistinctMappingList<V, E> extends ObservableListBase<E> {
private List<E> mapped;
private Function<V, E> mapper;
public DistinctMappingList(ObservableList<V> source, Function<V, E> mapper) {
this.mapper = mapper;
mapped = applyMapper(source);
ListChangeListener l = c -> sourceChanged(c);
source.addListener(l);
}
private void sourceChanged(Change<? extends V> c) {
beginChange();
List<E> backing = applyMapper(c.getList());
while(c.next()) {
if (c.wasAdded()) {
wasAdded(c, backing);
} else if (c.wasRemoved()) {
wasRemoved(c, backing);
} else {
// throw just for the example
throw new IllegalStateException("unexpected change " + c);
}
}
endChange();
}
private void wasRemoved(Change<? extends V> c, List<E> backing) {
List<E> removedCategories = applyMapper(c.getRemoved());
for (E e : removedCategories) {
if (!backing.contains(e)) {
int index = indexOf(e);
mapped.remove(index);
nextRemove(index, e);
}
}
}
private void wasAdded(Change<? extends V> c, List<E> backing) {
List<E> addedCategories = applyMapper(c.getAddedSubList());
for (E e : addedCategories) {
if (!contains(e)) {
int last = size();
mapped.add(e);
nextAdd(last, last +1);
}
}
}
private List<E> applyMapper(List<? extends V> list) {
List<E> backing = list.stream().map(p -> mapper.apply(p)).distinct()
.collect(Collectors.toList());
return backing;
}
@Override
public E get(int index) {
return mapped.get(index);
}
@Override
public int size() {
return mapped.size();
}
}
int categoryCount;
private Parent getContent() {
ObservableList<DemoData> data = FXCollections.observableArrayList(
new DemoData("first", "some"),
new DemoData("second", "some"),
new DemoData("first", "other"),
new DemoData("dup", "other"),
new DemoData("dodo", "next"),
new DemoData("getting", "last")
);
TableView<DemoData> table = new TableView<>(data);
TableColumn<DemoData, String> name = new TableColumn<>("Name");
name.setCellValueFactory(new PropertyValueFactory<>("name"));
TableColumn<DemoData, String> cat = new TableColumn<>("Category");
cat.setCellValueFactory(new PropertyValueFactory<>("category"));
table.getColumns().addAll(name, cat);
Function<DemoData, String> mapper = c -> c.categoryProperty().get();
ObservableList<String> mapped = new DistinctMappingList<>(data, mapper);
ListView<String> cats = new ListView<>(mapped);
Button remove = new Button("RemoveSelected DemoData");
remove.setOnAction(e -> {
int selected = table.getSelectionModel().getSelectedIndex();
if (selected <0) return;
data.remove(selected);
});
Button createNewCategory = new Button("Create DemoData with new Category");
createNewCategory.setOnAction(e -> {
String newCategory = data.size() == 0 ? "some" + categoryCount :
data.get(0).categoryProperty().get() + categoryCount;
data.add(new DemoData("name" + categoryCount, newCategory));
categoryCount++;
});
VBox buttons = new VBox(remove, createNewCategory);
HBox box = new HBox(table, cats, buttons);
return box;
}
public static class DemoData {
StringProperty name = new SimpleStringProperty(this, "name");
StringProperty category = new SimpleStringProperty(this, "category");
public DemoData(String name, String category) {
this.name.set(name);
this.category.set(category);
}
public StringProperty nameProperty() {
return name;
}
public StringProperty categoryProperty() {
return category;
}
}
@Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(new Scene(getContent()));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
/**
*如何实现自定义ObservableList的示例。
*
*这里:一个不可变且不可修改(本身)的列表,包含不同的
*支持列表中元素的属性值,提取这些值
*通过函数
*/
公共类DistinctMapperDemo扩展了应用程序{
公共静态类DistinctMappingList扩展了ObservableListBase{
私有列表映射;
私有函数映射器;
public DistinctMappingList(可观察列表源、函数映射器){
this.mapper=mapper;
映射=applyMapper(源);
ListChangeListener l=c->sourceChanged(c);
source.addListener(l);
}
private void sourceChanged(更改您的ObservatedStincList是否需要是一个列表,还是可以是提供的映射事件流?(只是尝试看看是否有一种替代方法比您现在尝试的更适合您)还有,顺序重要吗?如果不重要,使用帮助吗?顺序可能很重要,但我需要将其绑定到TableView和ListView,这样我就不能使用集合。虽然我完全支持第三方库,但我正在尝试将其用于ControlsFX项目中的一个组件,我不想遇到引入de的障碍悬而未决。不过,我需要查看ReactFX。阅读描述听起来很有趣。为了清楚起见,这个问题要求对问题中包含的实现进行审查和检查?用于..代码审查:-)再说一次,为什么要重新发明轮子?这里的八角形是生成通知所缺少的。TransformList看起来是一个合适的起点:它将为您将侦听器连接到源。您仍然需要实现sourceChanged,更新beginChange/endChange块中的内部状态,并将适当的更改发布到changeBuilder、 您是一位艺术家,谢谢!我想我现在看到了这一切的联系。ListChangeListener
订阅了source
,它触发sourcedChanged()
处理程序。在beginChange()
和endChange()之间
您处理所有添加和删除,这些添加和删除会传递给幕后的某个更改生成器。wasAdded()
和wasaremove()
非常有趣。它们非常重要,但它似乎可以处理所有索引参数的细微差别。这不仅对我眼前的问题有很大帮助,而且可以帮助我理解ObservaleBaselist
。谢谢!您可能对@kleopatra代码的更精简和完整版本感兴趣:请注意l到“dis”
/**
* Example of how to implement a custom ObservableList.
*
* Here: an immutable and unmodifiable (in itself) list containing distinct
* values of properties of elements in a backing list, the values are extracted
* via a function
*/
public class DistinctMapperDemo extends Application {
public static class DistinctMappingList<V, E> extends ObservableListBase<E> {
private List<E> mapped;
private Function<V, E> mapper;
public DistinctMappingList(ObservableList<V> source, Function<V, E> mapper) {
this.mapper = mapper;
mapped = applyMapper(source);
ListChangeListener l = c -> sourceChanged(c);
source.addListener(l);
}
private void sourceChanged(Change<? extends V> c) {
beginChange();
List<E> backing = applyMapper(c.getList());
while(c.next()) {
if (c.wasAdded()) {
wasAdded(c, backing);
} else if (c.wasRemoved()) {
wasRemoved(c, backing);
} else {
// throw just for the example
throw new IllegalStateException("unexpected change " + c);
}
}
endChange();
}
private void wasRemoved(Change<? extends V> c, List<E> backing) {
List<E> removedCategories = applyMapper(c.getRemoved());
for (E e : removedCategories) {
if (!backing.contains(e)) {
int index = indexOf(e);
mapped.remove(index);
nextRemove(index, e);
}
}
}
private void wasAdded(Change<? extends V> c, List<E> backing) {
List<E> addedCategories = applyMapper(c.getAddedSubList());
for (E e : addedCategories) {
if (!contains(e)) {
int last = size();
mapped.add(e);
nextAdd(last, last +1);
}
}
}
private List<E> applyMapper(List<? extends V> list) {
List<E> backing = list.stream().map(p -> mapper.apply(p)).distinct()
.collect(Collectors.toList());
return backing;
}
@Override
public E get(int index) {
return mapped.get(index);
}
@Override
public int size() {
return mapped.size();
}
}
int categoryCount;
private Parent getContent() {
ObservableList<DemoData> data = FXCollections.observableArrayList(
new DemoData("first", "some"),
new DemoData("second", "some"),
new DemoData("first", "other"),
new DemoData("dup", "other"),
new DemoData("dodo", "next"),
new DemoData("getting", "last")
);
TableView<DemoData> table = new TableView<>(data);
TableColumn<DemoData, String> name = new TableColumn<>("Name");
name.setCellValueFactory(new PropertyValueFactory<>("name"));
TableColumn<DemoData, String> cat = new TableColumn<>("Category");
cat.setCellValueFactory(new PropertyValueFactory<>("category"));
table.getColumns().addAll(name, cat);
Function<DemoData, String> mapper = c -> c.categoryProperty().get();
ObservableList<String> mapped = new DistinctMappingList<>(data, mapper);
ListView<String> cats = new ListView<>(mapped);
Button remove = new Button("RemoveSelected DemoData");
remove.setOnAction(e -> {
int selected = table.getSelectionModel().getSelectedIndex();
if (selected <0) return;
data.remove(selected);
});
Button createNewCategory = new Button("Create DemoData with new Category");
createNewCategory.setOnAction(e -> {
String newCategory = data.size() == 0 ? "some" + categoryCount :
data.get(0).categoryProperty().get() + categoryCount;
data.add(new DemoData("name" + categoryCount, newCategory));
categoryCount++;
});
VBox buttons = new VBox(remove, createNewCategory);
HBox box = new HBox(table, cats, buttons);
return box;
}
public static class DemoData {
StringProperty name = new SimpleStringProperty(this, "name");
StringProperty category = new SimpleStringProperty(this, "category");
public DemoData(String name, String category) {
this.name.set(name);
this.category.set(category);
}
public StringProperty nameProperty() {
return name;
}
public StringProperty categoryProperty() {
return category;
}
}
@Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(new Scene(getContent()));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}