First of all my objects:
public class Group {
private final ObservableList<IDevice> sourceList;
private final ObservableList<IDevice> destinationList;
private final ObservableList<Mapping> mappingList;
...}
public class Mapping {
private final IDevice source;
private final IDevice destination;
private final MappingMode mode;
public final StringProperty sourceName = new SimpleStringProperty();
public final StringProperty destinationName = new SimpleStringProperty();
public final StringProperty modeName = new SimpleStringProperty();
...}
Basically a group contains two lists of IDevices which can either be source or destination and a mapping list that contains one of them and one of two modes (enum).
The IDevice lists are displayed in an own listview with a table between them, representing the mapping (containing one column from the first, one from the second list and the mode column).
I have added them via setItems, this is the CellFactory for the ListViews
private Callback<ListView<IDevice>, ListCell<IDevice>> getFullNameDisplay() {
return new Callback<ListView<IDevice>, ListCell<IDevice>>() {
#Override
public ListCell<IDevice> call(ListView<IDevice> p) {
ListCell<IDevice> cell = new ListCell<IDevice>() {
#Override
protected void updateItem(IDevice t, boolean bln) {
super.updateItem(t, bln);
if (t != null) {
setText(t.getFullName());
}
else
setText("");
}
};
return cell;
}
};
}
The columns are set like this:
sourceColumn.setCellValueFactory(cellData -> cellData.getValue().sourceName);
destinationColumn.setCellValueFactory(cellData -> cellData.getValue().destinationName);
modeColumn.setCellValueFactory(cellData -> cellData.getValue().modeName);
I added two buttons for each listview to add and remove new items.
Of course if I remove a source or destination device, I want all of its mappings removed, so I added a ListChangeListener to the two lists:
private ListChangeListener<IDevice> getDeviceChangeListener() {
return (javafx.collections.ListChangeListener.Change<? extends IDevice> c) -> {
while (c.next()) {
if (c.wasRemoved()) {
c.getRemoved().stream().forEach((d) -> {
mappingList.stream().filter((map) -> (map.getSource().equals(d) || map.getDestination().equals(d))).forEach((map) -> {
mappingList.remove(map);
});
});
}
}
};
}
This also does what I intended it to do (and also all refactorings I tried did), but i cant get why this invokes (most of the time) a ConcurrentModificationException as I have not yet used any threading in my application. It seems as it doesnt trigger each time, which I understand can be lucky scheduling if I would be using threads.. The result is correct though
Someone any clue?
Thanks in advance
You cannot modify a collection while iterating through it, unless the modification is done via the iterator. In Java 8, the Collection class introduced a removeIf(...) method which helps in this use case:
private ListChangeListener<IDevice> getDeviceChangeListener() {
return (javafx.collections.ListChangeListener.Change<? extends IDevice> c) -> {
while (c.next()) {
if (c.wasRemoved()) {
c.getRemoved().forEach(d ->
mappingList.removeIf(map -> map.getDestination().equals(d)
|| map.getSource().equals(d)));
}
}
};
}
In the same loop if you are trying to iterate over the same collection and trying to modify the same collection, Java throws this concurrency exception.
If you want to modify the collection, please mantain a different collection for addition or modification. Once it comes out of the loop, call Collection.addAll() or Collection.removeAll() interface.
Related
In my table I have one cell that does not update without interaction with the table.
I found the reason already here Java: setCellValuefactory; Lambda vs. PropertyValueFactory; advantages/disadvantages
My problem is, the default value of the cells item is LocalDate.MIN and I want my cell to contain "---" as long as the item has this default value. When I update the item, I want the cell to contain the current date string.
Item Class:
public class ItemEv {
private final ObjectProperty<LocalDate> openedAt;
#XmlJavaTypeAdapter(LocalDateAdapter.class)
public final LocalDate getOpenedAt() {
return openedAt.get();
}
public final ObjectProperty<LocalDate> openedAtProperty() {
return this.openedAt;
}
public final void setOpenedAt(LocalDate openedAt) {
this.openedAt.set(openedAt);
}
}
in another CellFactory I set the new value: i.setOpenedAt(LocalDate.now());
this is working but not wanted:
openedAtColumnEv.setCellValueFactory(cellData -> cellData.getValue().openedAtProperty().asString());
and this is what I tried so far:
openedAtColumnEv.setCellValueFactory(new Callback<CellDataFeatures<ItemEv, String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(CellDataFeatures<ItemEv, String> i) {
if (i.getValue().getOpenedAt().equals(LocalDate.MIN)) {
return new SimpleStringProperty("---");
}
return i.getValue().openedAtProperty().asString();
}
});
and this:
openedAtColumnEv.setCellValueFactory(cellData -> {
if(cellData.getValue().openedAtProperty().getValue().equals(LocalDate.MIN)) {
return new SimpleStringProperty("---");
}
return cellData.getValue().openedAtProperty().asString();
});
Both of my tests return either SimpleStringProperty or StringBinding which should be fine.
In my tests I made a mistake where the first return in the IF statement does never return true, then the cell values show the standard string for LocalDate.MIN and get updated immediately when the item property changes.
Im a bit lost on this. Please forgive my bad english, Im not a native speaker.
If the property in the model class is an ObjectProperty<LocalDate>, then the column should be a TableColumn<ItemEv, LocalDate>, not a TableColumn<ItemEv, String>.
Implementing the cellValueFactory directly (typically with a lambda expression) is always preferable to using the legacy PropertyValueFactory class. You never "need to use" a PropertyValueFactory (and never should).
The cellValueFactory is only used to determine what data to display. It is not used to determine how to display the data. For the latter, you should use a cellFactory.
So:
private TableColumn<ItemEv, LocalDate> opendAtColumnEv ;
// ...
openedAtColumnEv.setCellValueFactory(cellData -> cellData.getValue().openedAtProperty());
openedAtColumnEv.setCellFactory(column -> new TableCell<ItemEv, LocalDate>() {
#Override
protected void updateItem(LocalDate openedAt, boolean empty) {
super.updateItem(openedAt, empty);
if (openedAt == null || empty) {
setText("");
} else {
if (openedAt.equals(LocalDate.MIN)) {
setText("---");
} else {
// Note you can use a different DateTimeFormatter as needed
setText(openedAt.format(DateTimeFormatter.ISO_LOCAL_DATE));
}
}
}
});
I have an ArrayList which is filled by Objects.
My object class called Article which has two fields ;
public class Article {
private int codeArt;
private String desArt;
public Article(int aInt, String string) {
this.desArt = string;
this.codeArt = aInt;
}
public int getCodeArt() {return codeArt; }
public void setCodeArt(int codeArt) {this.codeArt = codeArt;}
public String getDesArt() {return desArt;}
public void setDesArt(String desArt) { this.desArt = desArt;}
}
I want to filter my List using the desArt field, and for test I used the String "test".
I used the Guava from google which allows me to filter an ArrayList.
this is the code I tried :
private List<gestionstock.Article> listArticles = new ArrayList<>();
//Here the I've filled my ArrayList
private List<gestionstock.Article> filteredList filteredList = Lists.newArrayList(Collections2.filter(listArticles, Predicates.containsPattern("test")));
but this code isn't working.
In Java 8, using filter
List<Article> articleList = new ArrayList<Article>();
List<Article> filteredArticleList= articleList.stream().filter(article -> article.getDesArt().contains("test")).collect(Collectors.toList());
This is normal: Predicates.containsPattern() operates on CharSequences, which your gestionStock.Article object does not implement.
You need to write your own predicate:
public final class ArticleFilter
implements Predicate<gestionstock.Article>
{
private final Pattern pattern;
public ArticleFilter(final String regex)
{
pattern = Pattern.compile(regex);
}
#Override
public boolean apply(final gestionstock.Article input)
{
return pattern.matcher(input.getDesArt()).find();
}
}
Then use:
private List<gestionstock.Article> filteredList
= Lists.newArrayList(Collections2.filter(listArticles,
new ArticleFilter("test")));
However, this is quite some code for something which can be done in much less code using non functional programming, as demonstrated by #mgnyp...
You can use a for loop or for each loop to loop thru the list.
Do you want to create another list based on some condition?
This should work I think.
List<Article> secondList = new ArrayList<Article>();
for( Article a : listArticles) {
// or equalsIgnoreCase or whatever your conditon is
if (a.getDesArt().equals("some String")) {
// do something
secondList.add(a);
}
}
Guava is a library that allows you to use some functional programming in Java.
One of the winning things in functional programming is collection transformation like
Collection -> op -> op -> op -> transformedCollection.
Look here:
Collection<Article> filtered = from(listArticles).filter(myPredicate1).filter(myPredicate2).filter(myPredicate3).toImmutableList();
It's beautiful, isn't it?
The second one winning thing is lambda functions. Look here:
Collection<Article> filtered = from(listArticles)
.filter((Predicate) (candidate) -> { return candidate.getCodeArt() > SOME_VALUE })
.toImmutableList();
Actually, Java has not pure lambda functions yet. We will be able to do it in Java 8. But for now we can write this in IDE Inellij Idea, and IDE transforms such lambda into Predicate, created on-the-fly:
Collection<Article> filtered = from(listArticles)
.filter(new Predicate<Article>() {
#Override
public boolean apply(Article candidate) {
return candidate.getCodeArt() > SOME_VALUE;
}
})
.toImmutableList();
If your filter condition requires regexp, the code become more complicated, and you will need to move condition to separate method or move whole Predicate to a separate class.
If all this functional programming seems too complicated, just create new collection and fill it manually (without Guava):
List<Article> filtered = new ArrayList<Article>();
for(Article article : listArticles)
{
if(article.getCodeArt() > SOME_VALUE)
filtered.add(article);
}
With Guava, I would say that the easiest way by far would be by using Collections2.filter, such as:
Collections2.filter(YOUR_COLLECTION, new Predicate<YOUR_OBJECT>() {
#Override
public boolean apply(YOUR_OBJECT candidate) {
return SOME_ATTRIBUTE.equals(candidate.getAttribute());
}
});
Try this:
private List<gestionstock.Article> listArticles = new ArrayList<>();
private List<gestionstock.Article> filteredList filteredList = Lists.newArrayList(Collections2.filter(listArticles, new Predicate<gestionstock.Article>(){
public boolean apply(gestationstock.Article article){
return article.getDesArt().contains("test")
}
}));
The idea being is since you're using a custom object, you should implement your own predicate. If you're using it anywhere else, define it in a file, otherwise, this implementation works nicely.
Please, Is there an elegant and efficient way of doing the following in Post Java 8 i.e.
1. Looping through an arraylist
2. Reading the object
3. Calling different methods of potentially different objects using the values contained in the arraylist items
I did look at streams, switch statement, but it was much messy than my if-else.
Any help will be appreciated. Just looking for continuous improvements
List<JAXBElement<? extends Serializable>> bodyElements = eventRequestBodyTypeSrc.getContent();
for(JAXBElement element: bodyElements){
if(element.getName().getLocalPart().equalsIgnoreCase("A")){
methodA(element.getValue());
}else if(element.getName().getLocalPart().equalsIgnoreCase("B")){
methodB(element.getValue());
}else if(element.getName().getLocalPart().equalsIgnoreCase("C")){
methodC(element.getValue());
}else if(element.getName().getLocalPart().equalsIgnoreCase("D")){
methodD(element.getValue());
}else if(element.getName().getLocalPart().equalsIgnoreCase("E")){
methodE(element.getValue());
}else{
}
}
I think you have a bit of an XY Problem going on. I would refactor this at a higher level to encapsulate the strings and their related actions. Here's a rough concept using an enum:
enum Action {
A("a") {
#Override
void doSomething(Serializable value) {
// ...
}
},
B("b") {
#Override
void doSomething(Serializable value) {
// ...
}
};
private static final Map<String, Action> actionsByName = Arrays.stream(values())
.collect(Collectors.toMap(a -> a.name, a -> a));
private final String name;
Action(String name) {
this.name = name;
}
abstract void doSomething(Serializable value);
public static void processElement(JAXBElement<? extends Serializable> element) {
Action action = actionsByName.get(element.getName().getLocalPart().toLowerCase());
if (action != null) {
action.doSomething(element.getValue());
} else {
// ...
}
}
}
As a lean solution I would gather all mappings first as follows:
Map<String, Consumer<Serializable>> dispatchers = new HashMap<>();
dispatchers.put("A", this::methodA);
// etc.
...and dispatch the elements like that:
Consumer<Serializable> dispatcher = dispatchers.get(element.getName().getLocalPart().toUpperCase(Locale.US));
if (dispatcher != null) {
dispatcher.accept(element.getValue());
}
I have two ComboBoxes:
final ComboBox<MainCategory> comboBoxMainCategory = new ComboBox<>();
final ComboBox<SubCategory> comboBoxSubCategory = new ComboBox<>();
Depending on the value chosen in comboBoxMainCategory, the comboBoxSubCategory should be populated with the corresponding enum.
public enum MainCategory { // extra enum class
EUROPE("Europe"),
USA("USA");
}
public enum SubCategoryEurope { // extra enum class
GERMANY("Germany"),
FRANCE("France");
}
public enum SubCategoryUSA {
COLORADO("Colorado"),
CALIFORNIA("California");
}
If "Europe" is chosen for comboBoxMainCategory, comboBoxSubCategory should be populated with SubCategoryEurope. If "USA", with SubCategoryUSA.
How do you achieve this?
Here's my code:
final ComboBox<MainCategory> comboBoxMainCategory = new ComboBox<();
final ComboBox<SubCategory> comboBoxSubCategory = new ComboBox<>();
comboBoxMainCategory.valueProperty().addListener((obs, oldValue,
newValue) ->
{
if (newValue == null) { // newValue: Europe || USA
comboBoxSubCategory.getItems().clear();
comboBoxSubCategory.setDisable(true);
} else if (newValue.equals(MainCategory.EUROPE)) {
comboBoxSubCategory.setItems(FXCollections.observableArrayList(SubCategoryEurope.values()));
comboBoxSubCategory.setDisable(false);
} else {
comboBoxSubCategory.setItems(FXCollections.observableArrayList(SubCategoryUSA.values()));
comboBoxSubCategory.setDisable(false);}
});
Problem is, because comboBoxSubCategory is "SubCategory", there is a type error if it is populated with 'SubCategoryEurope' or 'SubCategoryUSA'.
What is the best way to solve this? Sorry if it's a silly question, I'm new to JavaFx.
Thanks a lot!
I wouldn't use enums at all, since this doesn't allow for data manipulation without recompiling. If you insist on using enums though, you need to use Object or a interface implemented with both subcategory enum types as parameter type for comboBoxSubCategory:
comboBoxMainCategory.valueProperty().addListener((obs, oldValue, newValue) -> {
if (newValue == null) { // newValue: Europe || USA
comboBoxSubCategory.getItems().clear();
comboBoxSubCategory.setDisable(true);
} else {
comboBoxSubCategory.setDisable(false);
List<? extends Object> list;
switch (newValue) {
case EUROPE:
list = Arrays.asList(SubCategoryEurope.values());
break;
default:
list = Arrays.asList(SubCategoryUSA.values());
break;
}
comboBoxSubCategory.getItems().setAll(list);
}
});
The better approach would be using a Map<String, List<String>> to store the data:
Map<String, List<String>> data = new HashMap<>();
data.put("EUROPE", Arrays.asList("GERMANY", "FRANCE"));
data.put("USA", Arrays.asList("COLORADO", "CALIFORNIA"));
comboBoxMainCategory.valueProperty().addListener((obs, oldValue, newValue) -> {
List<String> list = data.get(newValue);
if (list != null) {
comboBoxSubCategory.setDisable(false);
comboBoxSubCategory.getItems().setAll(list);
} else {
comboBoxSubCategory.getItems().clear();
comboBoxSubCategory.setDisable(true);
}
});
Just for fun (and to flesh out my comments): a more versatile approach than those in the other answers is to move away the focus of interest from the concrete nature of the backing data to a more general solution of the use-case at hand. The drawback of letting the UI implement special cases is always the same - you have to do it over and over again for each special UI and each special data type. The way out is always the same, too: implement a Model that takes over the general aspect and re-use that in concrete UI/data contexts.
The general aspects here are:
there's list of items with each having a list of dependent objects (same or different type)
this (let's call it root) list of items is shown in a control
from root list, a single item can be chosen (aka: selected)
another control should show the dependents of the root
The general approach is to have a Model that
manages list of items
has the notion of one of those items as selected (or current or active or ..)
manages a list of dependent items that always is the dependent list of the selected item
its state (root items, current item, dependent items) is exposed as properties
The advantages of such a Model are
can be formally and rigorouly tested, so using code can rely on its proper functioning
it's re-usable for any data context
it's re-usable for many controls
usage is pretty simple by binding
In the example below, the Model is named RelationModel which expects root items of type RelationProvider (which allows access to a list of dependents, it's one option, could just as well use f.i. a Function to build the dependents). It is used once with a plain Map of String/list and once with enums of Continents/Countries, each very simple to implement. Note that the resulting UI is blissfully unaware of the nature of the data, implemented solely against the model.
Naturally, not production grade, in particular, not formally tested and the model with just the barest functionality :)
public class CombosWithCategories extends Application {
public interface RelationProvider<T> {
default ObservableList<T> getRelations() {
return emptyObservableList();
};
}
/**
* A model that manages a list of RelationProviders and has the notion
* of a current relationProvider with relations (it's a kind-of selectionModel).
*
* <T> the type of elements in the list of relations
*/
public static class RelationModel<T> {
/**
* all relationProviders managed by this model
*/
private ListProperty<RelationProvider<T>> relationProviders;
/**
* The owner of the relations. Must be contained in the providers managed
* by this model.
*/
private ObjectProperty<RelationProvider<T>> relationProvider;
private ListProperty<T> relations;
public RelationModel() {
initProperties();
}
/**
* The RelationProviders managed by the model.
*/
public ListProperty<RelationProvider<T>> relationProvidersProperty() {
return relationProviders;
}
/**
* The RelationProvider that manages the current relations.
*/
public ObjectProperty<RelationProvider<T>> relationProviderProperty() {
return relationProvider;
}
public RelationProvider<T> getRelationProvider() {
return relationProviderProperty().get();
}
public ListProperty<T> relations() {
return relations;
}
/**
* Callback from invalidation of current relationProvider.
* Implemented to update relations.
*/
protected void relationProviderInvalidated() {
RelationProvider<T> value = getRelationProvider();
relations().set(value != null ? value.getRelations() : emptyObservableList());
}
/**
* Creates and wires all properties.
*/
private void initProperties() {
relationProviders = new SimpleListProperty<>(this, "relationProviders", observableArrayList());
relationProvider = new SimpleObjectProperty<>(this, "relationProvider") {
#Override
protected void invalidated() {
// todo: don't accept providers that are not in the list
relationProviderInvalidated();
}
};
relations = new SimpleListProperty<>(this, "relations");
relationProviderInvalidated();
}
}
/**
* Implement the ui against a RelationModel. Here we create
* the same UI with a model backed by enums or a Map, respectively
*/
private Parent createContent() {
TabPane tabPane = new TabPane(
new Tab("Enums", createRelationUI(createEnumRelationModel())),
new Tab("Manual map", createRelationUI(createMapRelationModel()))
);
return new BorderPane(tabPane);
}
/**
* Common factory for UI: creates and returns a Parent that
* contains two combo's configured to use the model.
*/
protected <T> Parent createRelationUI(RelationModel<T> model) {
ComboBox<RelationProvider<T>> providers = new ComboBox<>();
providers.itemsProperty().bind(model.relationProvidersProperty());
providers.valueProperty().bindBidirectional(model.relationProviderProperty());
ComboBox<T> relations = new ComboBox<>();
relations.itemsProperty().bind(model.relations());
relations.valueProperty().addListener((src, ov, nv) -> {
LOG.info("relation changed: " + nv);
});
return new VBox(10, providers, relations);
}
// ------------- manual with maps
/**
* On-the-fly creation of a RelationModel using a backing map.
*/
protected RelationModel<String> createMapRelationModel() {
RelationModel<String> model = new RelationModel<>();
Map<String, ObservableList<String>> data = new HashMap<>();
data.put("EUROPE", observableArrayList("GERMANY", "FRANCE"));
data.put("AMERICA", observableArrayList("MEXICO", "USA"));
for (String key: data.keySet()) {
model.relationProvidersProperty().add(new RelationProvider<String>() {
#Override
public ObservableList<String> getRelations() {
return data.get(key);
}
#Override
public String toString() {
return key;
}
});
}
return model;
}
//-------------------- enum
/**
* RelationModel using Enums.
*/
protected RelationModel<Object> createEnumRelationModel() {
RelationModel<Object> model = new RelationModel<Object>();
model.relationProvidersProperty().setAll(Continent.values());
return model;
}
public enum EuropeanCountry {
FRANCE, GERMANY;
}
public enum AmericanCountry {
MEXICO, CANADA, USA;
}
public enum Continent implements RelationProvider<Object> {
AMERICA(AmericanCountry.values()),
EUROPE(EuropeanCountry.values())
;
ObservableList<Object> subs;
private Continent(Object[] subs) {
this.subs = FXCollections.observableArrayList(subs);
}
#Override
public ObservableList<Object> getRelations() {
return FXCollections.unmodifiableObservableList(subs);
}
}
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.setTitle(FXUtils.version());
stage.show();
}
public static void main(String[] args) {
launch(args);
}
#SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(CombosWithCategories.class.getName());
}
Create a generic placeholder interface
public interface EnumPlaceHolder<E extends Enum<?>> {
public abstract String getDisplayValue();
public abstract E getEnum();
}
Create an implementation for all your enums. For example
public class EuropePlaceholder implements EnumPlaceHolder<Europe> {
private final Europe value;
public EuropePlaceholder(Europe pValue){
value = pValue;
}
#Override
public String getDisplayValue() {
// here you create a user-friendly version of your enum for display
return value.toString();
}
#Override
public Europe getEnum() {
return value;
}
}
Then change the type of your ComboBox<Enum> to ComboBox<EnumPlaceholder<?>> and you can add any of your implemented EnumPlaceholders to it. When retrieving the selected item you can check which one is contained via instance check
EnumPlaceholder<?> selectedItem = ...;
if(selectedItem instanceof EuropePlaceholder){
Europe selectedEuropeEnum = (Europe) selectedItem.getEnum();
} else if(....){
// check with else if for your other enums
}
And to display your enum in your combobox you call the getDisplayValue() of the EnumPlaceholder and show the returned String in your cell :)
EDIT
Tho in general i have to agree with fabians answer. You shouldn't use enums for a construct like this. Rather use a Map<> or a List<> with appropriate content and structure.
I have an ArrayList which is filled by Objects.
My object class called Article which has two fields ;
public class Article {
private int codeArt;
private String desArt;
public Article(int aInt, String string) {
this.desArt = string;
this.codeArt = aInt;
}
public int getCodeArt() {return codeArt; }
public void setCodeArt(int codeArt) {this.codeArt = codeArt;}
public String getDesArt() {return desArt;}
public void setDesArt(String desArt) { this.desArt = desArt;}
}
I want to filter my List using the desArt field, and for test I used the String "test".
I used the Guava from google which allows me to filter an ArrayList.
this is the code I tried :
private List<gestionstock.Article> listArticles = new ArrayList<>();
//Here the I've filled my ArrayList
private List<gestionstock.Article> filteredList filteredList = Lists.newArrayList(Collections2.filter(listArticles, Predicates.containsPattern("test")));
but this code isn't working.
In Java 8, using filter
List<Article> articleList = new ArrayList<Article>();
List<Article> filteredArticleList= articleList.stream().filter(article -> article.getDesArt().contains("test")).collect(Collectors.toList());
This is normal: Predicates.containsPattern() operates on CharSequences, which your gestionStock.Article object does not implement.
You need to write your own predicate:
public final class ArticleFilter
implements Predicate<gestionstock.Article>
{
private final Pattern pattern;
public ArticleFilter(final String regex)
{
pattern = Pattern.compile(regex);
}
#Override
public boolean apply(final gestionstock.Article input)
{
return pattern.matcher(input.getDesArt()).find();
}
}
Then use:
private List<gestionstock.Article> filteredList
= Lists.newArrayList(Collections2.filter(listArticles,
new ArticleFilter("test")));
However, this is quite some code for something which can be done in much less code using non functional programming, as demonstrated by #mgnyp...
You can use a for loop or for each loop to loop thru the list.
Do you want to create another list based on some condition?
This should work I think.
List<Article> secondList = new ArrayList<Article>();
for( Article a : listArticles) {
// or equalsIgnoreCase or whatever your conditon is
if (a.getDesArt().equals("some String")) {
// do something
secondList.add(a);
}
}
Guava is a library that allows you to use some functional programming in Java.
One of the winning things in functional programming is collection transformation like
Collection -> op -> op -> op -> transformedCollection.
Look here:
Collection<Article> filtered = from(listArticles).filter(myPredicate1).filter(myPredicate2).filter(myPredicate3).toImmutableList();
It's beautiful, isn't it?
The second one winning thing is lambda functions. Look here:
Collection<Article> filtered = from(listArticles)
.filter((Predicate) (candidate) -> { return candidate.getCodeArt() > SOME_VALUE })
.toImmutableList();
Actually, Java has not pure lambda functions yet. We will be able to do it in Java 8. But for now we can write this in IDE Inellij Idea, and IDE transforms such lambda into Predicate, created on-the-fly:
Collection<Article> filtered = from(listArticles)
.filter(new Predicate<Article>() {
#Override
public boolean apply(Article candidate) {
return candidate.getCodeArt() > SOME_VALUE;
}
})
.toImmutableList();
If your filter condition requires regexp, the code become more complicated, and you will need to move condition to separate method or move whole Predicate to a separate class.
If all this functional programming seems too complicated, just create new collection and fill it manually (without Guava):
List<Article> filtered = new ArrayList<Article>();
for(Article article : listArticles)
{
if(article.getCodeArt() > SOME_VALUE)
filtered.add(article);
}
With Guava, I would say that the easiest way by far would be by using Collections2.filter, such as:
Collections2.filter(YOUR_COLLECTION, new Predicate<YOUR_OBJECT>() {
#Override
public boolean apply(YOUR_OBJECT candidate) {
return SOME_ATTRIBUTE.equals(candidate.getAttribute());
}
});
Try this:
private List<gestionstock.Article> listArticles = new ArrayList<>();
private List<gestionstock.Article> filteredList filteredList = Lists.newArrayList(Collections2.filter(listArticles, new Predicate<gestionstock.Article>(){
public boolean apply(gestationstock.Article article){
return article.getDesArt().contains("test")
}
}));
The idea being is since you're using a custom object, you should implement your own predicate. If you're using it anywhere else, define it in a file, otherwise, this implementation works nicely.