How to find an object in an ArrayList by property - java

How can I find an object, Carnet, in a ArrayList<Carnet> knowing its property codeIsin.
List<Carnet> listCarnet = carnetEJB.findAll();
public class Carnet {
private String codeTitre;
private String nomTitre;
private String codeIsin;
// Setters and getters
}

In Java8 you can use streams:
public static Carnet findByCodeIsIn(Collection<Carnet> listCarnet, String codeIsIn) {
return listCarnet.stream().filter(carnet -> codeIsIn.equals(carnet.getCodeIsin())).findFirst().orElse(null);
}
Additionally, in case you have many different objects (not only Carnet) or you want to find it by different properties (not only by cideIsin), you could build an utility class, to ecapsulate this logic in it:
public final class FindUtils {
public static <T> T findByProperty(Collection<T> col, Predicate<T> filter) {
return col.stream().filter(filter).findFirst().orElse(null);
}
}
public final class CarnetUtils {
public static Carnet findByCodeTitre(Collection<Carnet> listCarnet, String codeTitre) {
return FindUtils.findByProperty(listCarnet, carnet -> codeTitre.equals(carnet.getCodeTitre()));
}
public static Carnet findByNomTitre(Collection<Carnet> listCarnet, String nomTitre) {
return FindUtils.findByProperty(listCarnet, carnet -> nomTitre.equals(carnet.getNomTitre()));
}
public static Carnet findByCodeIsIn(Collection<Carnet> listCarnet, String codeIsin) {
return FindUtils.findByProperty(listCarnet, carnet -> codeIsin.equals(carnet.getCodeIsin()));
}
}

You can't without an iteration.
Option 1
Carnet findCarnet(String codeIsIn) {
for(Carnet carnet : listCarnet) {
if(carnet.getCodeIsIn().equals(codeIsIn)) {
return carnet;
}
}
return null;
}
Option 2
Override the equals() method of Carnet.
Option 3
Storing your List as a Map instead, using codeIsIn as the key:
HashMap<String, Carnet> carnets = new HashMap<>();
// setting map
Carnet carnet = carnets.get(codeIsIn);

If you use Java 8 and if it is possible that your search returns null, you could try using the Optional class.
To find a carnet:
private final Optional<Carnet> findCarnet(Collection<Carnet> yourList, String codeIsin){
// This stream will simply return any carnet that matches the filter. It will be wrapped in a Optional object.
// If no carnets are matched, an "Optional.empty" item will be returned
return yourList.stream().filter(c -> c.getCodeIsin().equals(codeIsin)).findAny();
}
Now a usage for it:
public void yourMethod(String codeIsin){
List<Carnet> listCarnet = carnetEJB.findAll();
Optional<Carnet> carnetFound = findCarnet(listCarnet, codeIsin);
if(carnetFound.isPresent()){
// You use this ".get()" method to actually get your carnet from the Optional object
doSomething(carnetFound.get());
}
else{
doSomethingElse();
}
}

To find an object in an ArrayList by the property, We can use a function like this:
To find all the objects with a specific codeIsIn:
public static List<Item> findBycodeIsin(Collection<Carnet> listCarnet, String codeIsIn) {
return items.stream().filter(item -> codeIsIn.equals(item.getCodeIsIn()))
.collect(Collectors.toList());
}
To find a Single item (If the codeIsIn is unique for each object):
public static Carnet findByCodeIsIn(Collection<Carnet> listCarnet, String codeIsIn) {
return listCarnet.stream().filter(carnet-> codeIsIn.equals(carnet.getCodeIsIn()))
.findFirst().orElse(null);
}

Here is a solution using Guava
private User findUserByName(List<User> userList, final String name) {
Optional<User> userOptional =
FluentIterable.from(userList).firstMatch(new Predicate<User>() {
#Override
public boolean apply(#Nullable User input) {
return input.getName().equals(name);
}
});
return userOptional.isPresent() ? userOptional.get() : null; // return user if found otherwise return null if user name don't exist in user list
}

Here is another solution using Guava in Java 8 that returns the matched element if one exists in the list. If more than one elements are matched then the collector throws an IllegalArgumentException. A null is returned if there is no match.
Carnet carnet = listCarnet.stream()
.filter(c -> c.getCodeIsin().equals(wantedCodeIsin))
.collect(MoreCollectors.toOptional())
.orElse(null);

Following with Oleg answer, if you want to find ALL objects in a List filtered by a property, you could do something like:
//Search into a generic list ALL items with a generic property
public final class SearchTools {
public static <T> List<T> findByProperty(Collection<T> col, Predicate<T> filter) {
List<T> filteredList = (List<T>) col.stream().filter(filter).collect(Collectors.toList());
return filteredList;
}
//Search in the list "listItems" ALL items of type "Item" with the specific property "iD_item=itemID"
public static final class ItemTools {
public static List<Item> findByItemID(Collection<Item> listItems, String itemID) {
return SearchTools.findByProperty(listItems, item -> itemID.equals(item.getiD_Item()));
}
}
}
and similarly if you want to filter ALL items in a HashMap with a certain Property
//Search into a MAP ALL items with a given property
public final class SearchTools {
public static <T> HashMap<String,T> filterByProperty(HashMap<String,T> completeMap, Predicate<? super Map.Entry<String,T>> filter) {
HashMap<String,T> filteredList = (HashMap<String,T>) completeMap.entrySet().stream()
.filter(filter)
.collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue()));
return filteredList;
}
//Search into the MAP ALL items with specific properties
public static final class ItemTools {
public static HashMap<String,Item> filterByParentID(HashMap<String,Item> mapItems, String parentID) {
return SearchTools.filterByProperty(mapItems, mapItem -> parentID.equals(mapItem.getValue().getiD_Parent()));
}
public static HashMap<String,Item> filterBySciName(HashMap<String,Item> mapItems, String sciName) {
return SearchTools.filterByProperty(mapItems, mapItem -> sciName.equals(mapItem.getValue().getSciName()));
}
}

For finding objects which are meaningfully equal, you need to override equals and hashcode methods for the class. You can find a good tutorial here.
http://www.thejavageek.com/2013/06/28/significance-of-equals-and-hashcode/

Related

Java - enum where one variable can be multi-valued

I have an enum like below. Until recently, all variables were single-valued. However, now TYPE4 can have one of three acceptable values. I was hoping to simply modify this enum to accommodate for TYPE4, but thinking perhaps having only one type that is multi-valued means I need to use an object for mapping rather than an enum. I would be grateful for any insights. Thank you.
public enum Record {
TYPE1("TYPE1"),
TYPE2("TYPE2"),
TYPE3("TYPE3"),
TYPE4_MULTI(TYPE_A or TYPE_B or TYPE_C);
private final String value;
public static final Map<Record, String> enumMap = new EnumMap<Record, String>(
Record.class);
static {
for (Record e : Record.values())
enumMap.put(e, e.getValue());
}
Record(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
Operationally, I use this enum in a factory class to determine which of 4 types of subclasses I should instantiate. I do this by have each of the subclasses know its own type like this:
#Override
public String getType() {
return Record.TYPE1.getValue();
}
,and then the factory class pre-builds a set of the subclasses like this:
#Component
public class RecordProcessorFactory {
#Autowired
public RecordProcessorFactory(List<RecordProcessor> processors) {
for (RecordProcessor recordProcessor : processors) {
processorCache.put(recordProcessor.getType(), recordProcessor);
}
}
private static final Map<String, RecordProcessor> processorCache = new HashMap<String, RecordProcessor>();
public RecordProcessor getSyncProcessor(String type) {
RecordProcessor service = processorCache.get(type);
if(service == null) throw new RuntimeException("Unknown service type: " + type);
return service;
}
}
You could use a String array to store multiple values, note that your logic may change with enumMap that way.
public enum Record {
TYPE1("TYPE1"),
TYPE2("TYPE2"),
TYPE3("TYPE3"),
TYPE4_MULTI("TYPE_A", "TYPE_B", "TYPE_C");
private final String[] values;
public static final Map<Record, String[]> enumMap = new EnumMap<Record, String[]>(Record.class);
static {
for (Record e : Record.values())
enumMap.put(e, e.getValues());
}
Record(String... values) {
this.values = values;
}
public String[] getValues() {
return values;
}
}
In case you need to get the Enum from a String value, you could add this static method:
public static Optional<Record> optionalValueOf(final String value) {
for (Record record : values()) {
for (String recordValue : record.values) {
if (null == value && null == recordValue || value.equals(recordValue)) {
return Optional.of(record);
}
}
}
return Optional.empty();
}
I think it's better to encapsulate values in the enum. It should be immutable (array is not immutable data storage).
#lombok.Getter
public enum Record {
TYPE1("TYPE1"),
TYPE2("TYPE2"),
TYPE3("TYPE3"),
TYPE4_MULTI("TYPE_A", "TYPE_B", "TYPE_C");
// immutable list
private final List<String> values;
Record(String... values) {
this.values = Arrays.stream(values)
.collect(Collectors.toList());
}
}
P.S. Map<Record, String> enumMap I think is useless, because you have a Record already and all you need just call record.getValues() instead of Record.enumMaps.get(record). Also, this is breakes OOP encapsulation.

Guava: transform an List<Optional<T>> to List<T> keeping just present values

Is there an elegant way of using Guava to transform from a list of optionals to a list of present values?
For example, going from
ImmutableList.of(
Optional.of("Tom"), Optional.<String>absent(), Optional.of("Dick"),
Optional.of("Harry"), Optional.<String>absent()
)
to a list containing just
["Tom", "Dick", "Harry"]
One approach would be:
List<T> filterPresent(List<Optional<T>> inputs) {
return FluentIterable.from(inputs)
.filter(new Predicate<Optional<T>>() {
#Override
public boolean apply(Optional<T> optional) {
return optional.isPresent();
}
}).transform(new Function<Optional<T>, T>() {
#Override
public T apply(Optional<T> optional) {
return optional.get();
}
}).toList();
}
But this is verbose.
Java 8 is not an option, unfortunately.
There's actualy built-in method for this in Guava: presentInstances in Optional:
Returns the value of each present instance from the supplied optionals, in order, skipping over occurrences of absent(). Iterators are unmodifiable and are evaluated lazily.
Example:
List<Optional<String>> optionalNames = ImmutableList.of(
Optional.of("Tom"), Optional.<String>absent(), Optional.of("Dick"),
Optional.of("Harry"), Optional.<String>absent());
Iterable<String> presentNames = Optional.presentInstances(optionalNames); // lazy
// copy to List if needed
List<String> presentNamesList = ImmutableList.copyOf(presentNames);
System.out.println(presentNamesList); // ["Tom", "Dick", "Harry"]
Why not do it in the old-fashioned Java way:
List<T> result = new ArrayList<T>();
for (Optional<T> optional : inputs) {
if (optional.isPresent()) {
result.add(optional.get());
}
}
return result;
You could hide the predicate instances behind method calls to make the code more readable:
List<T> filterPresent(List<Optional<T>> inputs) {
return FluentIterable.from(inputs).filter(present()).transform(value()).toList();
}
static <T> Predicate<Optional<T>> present() {
return new Predicate<Optional<T>>() {
#Override
public boolean apply(Optional<T> optional) {
return optional.isPresent();
}
};
}
static <T> Function<Optional<T>, T> value() {
return new Function<Optional<T>, T>() {
#Override
public T apply(Optional<T> optional) {
return optional.get();
}
};
}
Unfortunately, there is no easier way in the pre-Java-8-life.
If you are calling a method in a loop which returns an Optional and you want to create a List of the returned values which are present, then you can use the toSet method on the Optional in conjunction with the addAll method on the List, like so:
List<String> strings = newArrayList();
for (Long id : ids) {
strings.addAll(getString(id).toSet());
}
This is useful if you want to return a List, rather than an Iterable, which you get from Optional.presentInstances.

Dynamically filter List with Predicate

I have the following list of Strings:
{"New York","London","Paris","Berlin","New York"}
I am trying to use the Guava Library and I want to filter this list in such a way that I will get only the strings which will equal to a string that I will provide. If I had a fixed value let's say "New York" I would do the following:
Predicate<String> myCity = new Predicate<String>() {
#Override public boolean apply(String city) {
return city == "New York";
}
};
But what if I want the return statement to be something like this :
return city == myStringVariable
Can I give the argument for the city to the Predicate or combine two predicates somehow ?
Guava provides a number of really generic Predicates, via the Predicates helper class.
To filter on equality (be it for a String or any other object), it provides the equalTo() predicate:
Predicate<String> myCity = Predicates.equalTo(myStringVariable);
Update to answer a question in the comments: how to filter when the list is not of Strings but of objects which have a String property.
You have several options, depending on what you already have:
Use imperative code
For a one-time use, unless you use Java 8, it's a bit verbose to use the functional constructs. See the FunctionalExplained page in the wiki.
private List<SomeObject> filterOnMyCity(List<SomeObject> list,
String value) {
List<SomeObject> result = new ArrayList<>();
for (SomeObject o : list) {
if (value.equals(o.getMyCity())) {
result.add(o);
}
}
return result;
}
Use an ad-hoc predicate
class MyCityPredicate implements Predicate<SomeObject> {
private final String value;
public MyCityPredicate(String value) {
this.value = value;
}
#Override
public boolean apply(#Nullable SomeObject input) {
return input != null && value.equals(input.getPropertyX());
}
}
return FluentIterable.from(list)
.filter(new MyCityPredicate(myStringVariable))
.toList();
Use an existing Function
class MyCityFunction implements Function<SomeObject, String> {
#Override
public String apply(#Nullable SomeObject input) {
return input == null ? null : input.getPropertyX();
}
}
return FluentIterable.from(list)
.filter(Predicates.compose(
Predicates.equalTo(myStringVariable),
new MyCityFunction()))
.toList();
However, I wouldn't override equals() as mentioned in the comments, it could be too specific to say that 2 instances of SomeObject are equals just because one of their properties is. It doesn't scale if you need to filter on 2 different properties in different contexts.
If you have Java 8, the code becomes much more compact using lambda expressions, and you can directly use the Stream API anyway so you don't need Guava for that.
Either you use a final String or subclass of Predicate :
final String testStr = textbox.getText(); //for example
Predicate<String> myCity = new Predicate<String>() {
#Override public boolean apply(String city) {
return city.equals(testStr);
}
};
or
public class CityPredicate implements Predicate<String>{
String cityName;
public CityPredicate(String cityName){
this.cityName = cityName;
}
#Override public boolean apply(String city) {
return city.equals(cityName);
}
}
//Use example :
Predicate<String> myCity = new CityPredicate("New York");
And as #Sotirios Delimanolis told you, always compare String with equals()
EDIT : example with Frank Pavageau's solution :
Given your class :
public class City{
String cityName;
public City(String cityName){
this.cityName=cityName;
}
#Override
public boolean equals(Object obj) {
if(obj instanceof String){
return cityName.equals(obj);
}
return false;
}
}
List<City> cities = Lists.newArrayList(new City("New York"),new City("Chicago"));
Predicate<String> myCityPredicate = Predicates.equalTo("New York");
final List<City> res = Lists.newArrayList(Iterables.filter(cities , myCityPredicate));
//res.size() will be 1 and containing only your City("New York")
//To check whether it is present you can do :
final boolean isIn = Predicates.in(cities).apply("New York");
You can construct the predicate by closing over a some other variable
final String cityName = "New York";
Predicate<String> myCity = new Predicate<String>() {
#Override public boolean apply(String city) {
return city.equals(cityName);
}
};
Note how I compare strings with their equals method rather than the == reference equality operator. See below for why.
How do I compare strings in Java?

Refactoring: Map<String, Double> to Map<String, Double or String>

I've got widely used method like:
public Map<String, Double> parseData(String[] data) {
.................
Where data is something like new String[] { "column1 -> 2.00", "column2 -> New York", ... }
Problem: It appears that data can contains both: String -> Double & String -> String values. So I need smth like:
public Map<String, String or Double> parseData(String[] data) {
................
Question: Any ideas besides return Map<String, Object>?
Create a Wrapper StringOrDouble which will look a bit like this:
public class StringOrDouble {
private String internalString;
private Double internalDouble;
public StringOrDouble(String input) {
internalString = input;
}
public StringOrDouble(Double input) {
internalDouble = input;
}
public boolean hasString() {
return internalString != null;
}
public boolean hasDouble() {
return internalDouble != null;
}
public String getString() {
return internalString;
}
public Double getDouble() {
return internalDouble;
}
}
Then have a map of type Map<String, StringOrDouble> and use that. When you use the values, you can check which one it is by testing with hasString() and/or hasDouble(). Alternatively you could have an enum which determines which type it is.
public Map<String, Container> parseData(String[] data)
You can introduce a wrapper class for this
public class Container {
private String s;
private Double d;
public Container(String s) {
this.s=s;
}
public Container(Double d) {
this.d=d;
}
public hasString() {
return s!=null;
}
public hasDouble() {
return d!=null;
}
//getters/setters
}
As far as I understand, you want something like Map<String, ? extends String || Double as the return type, but no such thing is supported in Java:
4.9 Intersection Types An intersection type takes the form T1 & ... & Tn, n>0, where Ti, 1in, are type expressions. Intersection types arise
in the processes of capture conversion (§5.1.10) and type inference
(§15.12.2.7). It is not possible to write an intersection type
directly as part of a program; no syntax supports this. The values of
an intersection type are those objects that are values of all of the
types Ti, for 1in.
So you'd better parse the input array and hold different arrays for each different type or you can use a wrapper class to represent the values in the map returned, as some other answerers explained.
Use superclass:
public Map<String, Object> parseData(String[] data)
Just an alternative to #blalasaadri. don't pretend to be better:
public static class StringDoubleValue {
private final Optional<String> sValue;
private final Optional<Double> dValue;
public MetricValue(String sValue) {
this.sValue = Optional.of(sValue);
this.dValue = Optional.absent();
}
public MetricValue(Double dValue) {
this.sValue = Optional.absent();
this.dValue = Optional.of(dValue);
}
public Object get() {
return (sValue.isPresent()) ? sValue.get() : dValue.get();
}
#Override
public String toString() {
if (sValue.isPresent()) ? sValue.get() : dValue.get().toString();
}
}

Return a sublist based on member variable or mapping function

I have a list of pojo's List<Pojo> pojoList; and pojo.getColour(); returns an Enum instance.
And I want to do this :
List<Pojo> newlist = new ArrayList<Pojo>();
for(Pojo pojo:pojoList){
if(pojo.getColour() == Colour.Red){
newList.add(pojo);
}
}
I could see myself using a similar function for lists of other types so rather than repeating a lot of code is their a way to make it generic and/or functional ? So that I could create sublists of different types based upon a different rule ?
First of all, I should note that if you just want a new ArrayList containing the matching elements, the way you did it in your example is just fine. Until Java has lambda expressions, you're not going to get it simpler or better looking than that.
Since you tagged this with guava, here's how you could do this with Guava. You're basically filtering the original list on the composition of a predicate (== Color.Red) and a function (pojo.getColour()). So if you had a static final Function<Pojo, Colour> called COLOUR on Pojo (like this):
public static final Function<Pojo, Colour> COLOUR =
new Function<Pojo, Colour>() {
#Override public Colour apply(Pojo input) {
return input.getColour();
}
};
you could create that combination like this:
Predicate<Pojo> isRedPojo = Predicates.compose(
Predicates.equalTo(Colour.Red), Pojo.COLOUR);
You can then create a filtered view of the original list:
Iterable<Pojo> redPojos = Iterables.filter(pojoList, isRedPojo);
And you could copy that filtered view into an ArrayList if you want:
List<Pojo> copy = Lists.newArrayList(redPojos);
You'd have to make your type implement a common interface for the check:
public interface Candidate {
public boolean isAddable();
}
The loop then would look like this
List<Candidate> newlist = new ArrayList<Candidate>();
for(Candidate pojo:pojoList){
if(pojo.isAddable()){
newList.add(pojo);
}
}
and the Pojo class would have to implement the interface:
public class Pojo implments Candidate {
// ...
#Override
public boolean isAddable() {
return isRed();
}
}
Depending on how often you use it / how many different filters (only red, only green etc.) you are using, it could make sense to create a Filter interface - if it is only to check isRed then it is probably too much code and you are better off with a simple static method.
The good thing about this design is you can use it with any objects that you want to filter (see example with String below).
public static void main(String[] args) {
List<Pojo> originalList = Arrays.asList(new Pojo(true), new Pojo(false), new Pojo(false));
List<Pojo> filteredList = Utils.getFilteredList(originalList, new Filter<Pojo>() {
#Override
public boolean match(Pojo candidate) {
return candidate.isRed();
}
});
System.out.println(originalList.size()); //3
System.out.println(filteredList.size()); //1
//Now with strings
List<String> originalStringList = Arrays.asList("abc", "abd", "def");
List<String> filteredStringList = Utils.getFilteredList(originalStringList, new Filter<String>() {
#Override
public boolean match(String candidate) {
return candidate.contains("a");
}
});
System.out.println(originalStringList.size()); //3
System.out.println(filteredStringList.size()); //2
}
public static class Utils {
public static <T> List<T> getFilteredList(List<T> list, Filter<T> filter) {
List<T> selected = new ArrayList<>();
for (T t : list) {
if (filter.match(t)) {
selected.add(t);
}
}
return selected;
}
}
public static class Pojo {
private boolean isRed;
public Pojo(boolean isRed) {
this.isRed = isRed;
}
public boolean isRed() {
return isRed;
}
}
public interface Filter<T> {
/**
* When passed a candidate object, match returns true if it matches the filter conditions,
* or false if it does not.
* #param candidate the item checked against the filter
* #return true if the item matches the filter criteria
*/
boolean match(T candidate);
}
make an generic filter interface
public interface Filter<T>{
public boolean match(T item);
}
make a method using the filter
public <T> List<T> getFilteredList(List<T> oldList, List<T> filter){
List<T> newlist = new ArrayList<T>();
for(T item:oldList){
if(filter.match(item)){
newlist.add(item);
}
}
return newlist;
}
put it all together
List<Pojo> myList = ..
List<Pojo> redList = getFilteredList(myList,new Filter<Pojo>(){
public boolean match(Pojo item){ return item.isRed()};
});
List<Pojo> blueList = getFilteredList(myList,new Filter<Pojo>(){
public boolean match(Pojo item){ return item.COLOR== Color.BLUE};
});

Categories

Resources