Hot questions for Filtering the Data in Guava

Top 10 Java Open Source / Guava / Filtering the Data

Filtering a list of JavaBeans with Google Guava

Question: In a Java program, I have a list of beans that I want to filter based on a specific property.

For example, say I have a list of Person, a JavaBean, where Person has many properties, among them 'name'.

I also have a list of names. Now I want to find all the persons whose name is in the name list.

What is the best way to execute this filter using Google Guava?

Answer: If your list of names is big, you'd better transform it into a Set (HashSet, preferrably) and call contains on this set, rather than the list, because contains is O(1) for a HashSet, and O(n) for a List.

Iterable<Person> filtered = Iterables.filter(allPersons, new Predicate<Person>() {
    @Override
    public boolean apply(Person p) {
        return acceptedNames.contains(p.getName());
    }
});

Or do it the old-fashioned way, without Guava.

List<Person> filtered = Lists.newArrayList();
for(Person p : allPersons) {
   if(acceptedNames.contains(p.getName())) {
       filtered.add(p);
   }
}

Guava: Why is there no Lists.filter() function?

Question: Is there a reason there's

Lists.transform()

but no?

Lists.filter()

How do I filter a list correctly? I could use

new ArrayList(Collection2.filter())

of course, but this way it's not guaranteed that my ordering stays the same, if I understand correctly.

Answer: It wasn't implemented because it would expose a perilous large number of slow methods, such as #get(index) on the returned List view (inviting performance bugs). And ListIterator would be a pain to implement as well.

Since indexed methods can't be efficient in the filtered List view, it's better to just go with a filtered Iterable, which doesn't have them.


Java 8 Stream String Null Or Empty Filter

Question: I've got Google Guava inside Stream:

this.map.entrySet().stream()
.filter(entity -> !Strings.isNullOrEmpty(entity.getValue()))
.map(obj -> String.format("%s=%s", obj.getKey(), obj.getValue()))
.collect(Collectors.joining(","))

As you see there is a statement !String.isNullOrEmpty(entity) inside the filter function.

I don't want to use Guava anymore in the project, so I just want to replace it simply by:

string == null || string.length() == 0;

How can I do it more elegant?

Answer: You can write your own predicate:

final Predicate<Map.Entry<?, String>> valueNotNullOrEmpty
    = e -> e.getValue() != null && !e.getValue().isEmpty();

Then just use valueNotNullOrEmpty as your filter argument.


Filtering on List based on one property with guava

Question: I have a class called Person

public class Person implements Nameable {
    private String name;

    public String getName(){
        return name;
    }
}

Now I have two lists

List<Person>  persons = // some persons
List<Person> subsetOfPersons = // some duplicate persons, but different objects and don't share the same identity

Now I would like to filter the persons which are not present in the subsetOfPersons, equality criteria is name property and Person doesn't have equals.

How can I do this?

Answer: the below would transform person to name for the sake of comparison. For the subsetOfPersons, we actually create a list of names directly, since that's all we really need from them. For the persons, we keep the transformation limited to the context of the comparison.

    Iterable<Person> filtered = Iterables
            .filter(
                persons, 
                Predicates.not(
                    Predicates.compose(
                        Predicates.in(ImmutableSet.copyOf(Iterables.transform(subsetOfPersons, personToNamefunction))),
                        personToNamefunction
                    )
                )
            );

Thought you might appreciate a JUnit:

public class PersonTest {
    public class Person {
        private String name;

        public String getName(){
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

    @Test
    public void testNameBasedFiltering() {
        Person bob = createPerson("bob");
        Person jim = createPerson("jim");
        Person pam = createPerson("pam");
        Person roy = createPerson("roy");

        ImmutableList<Person> persons = ImmutableList.of(
                bob,
                jim,
                pam,
                roy); 
        ImmutableList<Person> subsetOfPersons = ImmutableList.of(
                createPerson("jim"),
                createPerson("pam"));

        Function<Person, String> personToNamefunction = new Function<Person, String>() {
            public String apply(Person arg0) {
                return arg0.getName();
            }
        };

        Iterable<Person> filtered = Iterables
                .filter(
                    persons, 
                    Predicates.not(
                        Predicates.compose(
                            Predicates.in(ImmutableSet.copyOf(Iterables.transform(subsetOfPersons, personToNamefunction))),
                            personToNamefunction
                        )
                    )
                );

        for (Person person : filtered) {
            assertNotSame(jim, person);
            assertNotSame(pam, person);         
        }
    }

    public Person createPerson(String name) {
        Person person = new Person();
        person.setName(name);

        return person;
    }
}

Guava: how to combine filter and transform?

Question: I have a collection of Strings, and I would like to convert it to a collection of strings were all empty or null Strings are removed and all others are trimmed.

I can do it in two steps:

final List<String> tokens =
    Lists.newArrayList(" some ", null, "stuff\t", "", " \nhere");
final Collection<String> filtered =
    Collections2.filter(
        Collections2.transform(tokens, new Function<String, String>(){

            // This is a substitute for StringUtils.stripToEmpty()
            // why doesn't Guava have stuff like that?
            @Override
            public String apply(final String input){
                return input == null ? "" : input.trim();
            }
        }), new Predicate<String>(){

            @Override
            public boolean apply(final String input){
                return !Strings.isNullOrEmpty(input);
            }

        });
System.out.println(filtered);
// Output, as desired: [some, stuff, here]

But is there a Guava way of combining the two actions into one step?

Answer: In the latest version of Guava, there will be a class named FluentIterable. This class provides the missing fluent API for this kind of stuff.

Using FluentIterable, you should be able doing something like this:

final Collection<String> filtered = FluentIterable
    .from(tokens)
    .transform(new Function<String, String>() {
       @Override
       public String apply(final String input) {
         return input == null ? "" : input.trim();
       }
     })
    .filter(new Predicate<String>() {
       @Override
       public boolean apply(final String input) {
         return !Strings.isNullOrEmpty(input);
       }
     })
   .toImmutableList();

Elegant solution for filtering a list to be unique for given object attribute

Question: I have a flattened multi-map (a list) of objects which I would like to convert to a list where a 'key' attribute / field (e.g. name below) is unique amongst all entries.

In the case where multiple entries have the same key (name) the entry with the largest creationDate field should be picked.

Example:

List<Object> myList =
[
  {name="abc", age=23, creationDate = 1234L},
  {name="abc", age=12, creationDate = 2345L},
  {name="ddd", age=99, creationDate = 9999L}
]

Should be converted to:

List<Object> = 
[
  {name="abc", age=12, creationDate = 2345L},
  {name="ddd", age=99, creationDate = 9999L}
]

Is there an elegant way (possibly using Guava libraries?) to solve this in Java? I realize I can just try and use a HashMap with name as the key to find all unique entries, but I get the feeling there is a better way to solve this.

Answer: If you have the possibility to work with Java 8 I would recommend Streams as the other answers allready told. If not you can go with something like this.

First you sort the List desending by the creationDate. Then you create a TreeSet witch treats all Persons with same name as equal. Therefore only the first (highest) creationDate will be added and further ones ignored.

List<Person> persons = new ArrayList<>();
persons.add(new Person("abc", 23, 1234L));
persons.add(new Person("abc", 12, 2345L));
persons.add(new Person("def", 99, 9999L));

Collections.sort(persons, new Comparator<Person>() {
    public int compare(Person o1, Person o2) {
            return (int) (o2.creationDate - o1.creationDate);
    }
});

Set<Person> personsHashed = new TreeSet<>(new Comparator<Person>() {
    public int compare(Person o1, Person o2) {
        return o2.name.compareTo(o1.name);
    }
});
personsHashed.addAll(persons);