Hot questions for Sorting the Data in Guava

Top 10 Java Open Source / Guava / Sorting the Data

Sort List in reverse in order

Question: I have List list1 in direct order.

List<String> list = Ordering.natural().sortedCopy(asu2);

How to change order. And I don't know how to rewrite methods from extends class, please write with examples or speak clearly.

Answer: just reverse the order elements.

Collections.reverse(list);

There is another method reverseOrder in the Collections class which returns a Comparator.

You can use it like

Collections.sort(list, Collections.reverseOrder());

Having a Multimap sorted on keys only in Java

Question: I would like to have a c.g.c.c.Multimap that is sorted based on keys only. The values shouldn't be sorted. I've tried to build something with guava's TreeMultimap, but I can't use it because the value type doesn't implement Comparable.

public class MyObject /* doesn't implement Comparable */ {
  private String name;
  private int score;
  // Getters/setters are implemented
  public static Function<MyObject,Integer> myObjectToScore {
    @Override public Integer apply (MyObject o) { return o.score; }
  }
  public static Multimap<Integer,MyObject> indexOnScore(Iterable<MyObject> i) {
    Multimap<Integer,MyObject> m = Multimaps.index(i, myObjectToScore());
    // Do the sort of the keys.
    return m;
  }
}

I've thought about getting a SortedSet of the keys, then iterating over each of these keys in the sorted set to fetch the various values, but I was hoping using an existing (yet undiscovered) feature in Guava rather than using this kind of hack.

I won't make MyObject implement Comparable because it makes no sense with my actual object.

Example of input/output:

Set<MyObject> s = Sets.newHashSet(
  new MyObject("a", 2),
  new MyObject("b", 3),
  new MyObject("c", 1),
  new MyObject("d", 3),
  new MyObject("e", 1)
); // Assuming constructor MyObject(String name, int score)

for (Map.Entry<Integer, MyObject> e: MyObject.indexedOnScore(s).entries()) {
  System.out.printf("%d -> %s%n", e.getKey(), e.getValue().getName());
}

Prints:

1 -> c // or switched with line below
1 -> e
2 -> a
3 -> b // or switched with line below
3 -> d

Answer: Multimaps.index returns an ImmutableListMultimap, so you wouldn't be able to sort it after creating it. You could, however, first create a sorted copy of your Iterable<MyObject> and feed that to Multimap.index... ImmutableListMultimap keeps things in the same order it was given them.

public static ImmutableMultimap<Integer, MyObject> indexOnScore(Iterable<MyObject> i) {
  List<MyObject> sorted = Ordering.natural().onResultOf(myObjectToScore())
      .sortedCopy(i);
  return Multimaps.index(sorted, myObjectToScore());
}

Another option might be to create a TreeMultimap and use Ordering.arbitrary() as the Comparator for the values.


filter and sort list using google collections

Question: Suppose I have a list (or Set):

List<String> testList = Lists.newArrayList("assocX","srcT","destA","srcX", "don't care Y", "garbage", "srcB");

I would like to get back an ImmutableList(Set) that sorts/groups terms in natural order where terms that begin with "src" are first, "assoc" second and "dest" last. If a term does not contain those then it should be removed from the resulting list.

Therefore the result here is "srcB", "srcT", "assocX", "destA".

I think I can do this with some combination of Iterables.filter or Predicates but just not seeing it. There must be a succinct way of doing it I think.

Answer: As long as those three prefixes are the only things you care about, I'd suggest something like this:

    Predicate<String> filter = new Predicate<String>() {
        @Override
        public boolean apply(String input) {
            return input.startsWith("src") || input.startsWith("assoc") || input.startsWith("dest");
        }
    };

    Function<String, Integer> assignWeights = new Function<String, Integer>() {
        @Override
        public Integer apply(String from) {
            if (from.startsWith("src")) {
                return 0;
            } else if (from.startsWith("assoc")) {
                return 1;
            } else if (from.startsWith("dest")) {
                return 2;
            } else {
                /* Shouldn't be possible but have to do something */
                throw new IllegalArgrumentException(from + " is not a valid argument");
            }
        }
    };

    ImmutableList<String> sortedFiltered = ImmutableList.copyOf(
            Ordering.natural().onResultOf(assignWeights).sortedCopy(
                    Iterables.filter(testList, filter)
            )
    );

This solution definitely wouldn't scale out incredibly well if you start adding more prefixes to filter out or sort by, since you'd have to continually update both the filter and the weight of each prefix.


Sort Guava Multimap by number of values

Question: If I have a Guava Multimap, how would I sort the entries based on the number of values for the given key?

For instance:

Multimap<String, String> multiMap = ArrayListMultimap.create();
multiMap.put("foo", "1");
multiMap.put("bar", "2");
multiMap.put("bar", "3");
multiMap.put("bar", "99");

Given this, when iterating over multiMap, how would I get the "bar" entries to come first (since "bar" has 3 values vs. only 1 for "foo")?

Answer: Extract the entries in a list, then sort the list :

List<Map.Entry<String, String>> entries = new ArrayList<Map.Entry<String, String>>(map.entries());
Collections.sort(entries, new Comparator<Map.Entry<String, String>>() {
    @Override
    public int compare(Map.Entry<String, String> e1, Map.Entry<String, String> e2) {
        return Ints.compare(map.get(e2.getKey()).size(), map.get(e1.getKey()).size());
    }
});

Then iterate over the entries.

If what you want is in fact iterate over the entries of the inner map (Entry<String, Collection<String>>), then do the following :

List<Map.Entry<String, Collection<String>>> entries = 
    new ArrayList<Map.Entry<String, Collection<String>>>(map.asMap().entrySet());
Collections.sort(entries, new Comparator<Map.Entry<String, Collection<String>>>() {
    @Override
    public int compare(Map.Entry<String, Collection<String>> e1, 
                       Map.Entry<String, Collection<String>> e2) {
        return Ints.compare(e2.getValue().size(), e1.getValue().size());
    }
});

// and now iterate
for (Map.Entry<String, Collection<String>> entry : entries) {
    System.out.println("Key = " + entry.getKey());
    for (String value : entry.getValue()) {
        System.out.println("    Value = " + value);
    }
}

How to sort guava multimap? (KEY=DATE)

Question: I have a

Multimap<Date,Invoice> multimap = ArrayListMultimap.create();

from guava. I was wondering how to SORT the the Date key in the multimap.

Currently, I'm doing this:

        Iterator<Date> dateItr = multimap.keySet().iterator();
        Set<Date> treeSet = new TreeSet<Date>(Collections.reverseOrder());

and later I loop through the treeSet iterator. Any idea how to avoid this circumvention?

Answer: Use TreeMultimap , or if you need to map into List , use MultimapBuilder:

return MultimapBuilder.treeKeys().arrayListValues().build()

Guava way of sorting List according to another list?

Question: I have List<Integer> consisting Ids of my Users. And after a database query, I am retrieving List<User>. I would like to order this list according to first Id list. List<User> may not include some of the Ids. What is the Guava way for sorting this list?

Answer: The fully "functional" way, using Guava, would combine Ordering#explicit() with Ordering#onResultOf()

public class UserService {

    @Inject private UserDao userDao;

    public List<User> getUsersWithIds(List<Integer> userIds) {
        List<User> users = userDao.loadUsersWithIds(userIds);
        Ordering<User> orderById = Ordering.explicit(userIds).onResultOf(UserFunctions.getId());
        return orderById.immutableSortedCopy(users);
    }

}

You could declare an anonymous function inline, but I like to declare my functions as static factory methods in a separate class, for a cleaner code (the verbosity of Java's function declarations is hidden in the utility class):

public final class UserFunctions {
    private UserFunctions() { /* prevents instantiation */ }

    /**
     * @return a {@link Function} that returns an {@link User}'s id.
     */
    public static Function<User, Integer> getId() {
        return GetIdFunction.INSTANCE;
    }

    // enum singleton pattern
    private enum GetIdFunction implements Function<User, Integer> {
        INSTANCE;

        public Integer apply(User user) {
            return user.getId();
        }
    }

}

Or it can be simplified in Java 8, using a method reference instead of the UserFunctions class:

public class UserService {

    @Inject private UserDao userDao;

    public List<User> getUsersWithIds(List<Integer> userIds) {
        List<User> users = userDao.loadUsersWithIds(userIds);
        Ordering<User> orderById = Ordering.explicit(userIds).onResultOf(User::getId);
        return orderById.immutableSortedCopy(immutableSortedCopy(users));
    }

}

Multi criteria sorting of a list of objects with Guava Ordering

Question: I have a class WHICH CANNOT implement comparable, but needs to be sorted based on 2 fields. How can I achieve this with Guava?

Let's say the class is:

class X {
  String stringValue;
  java.util.Date dateValue;
} 

And I have a list of these:

List<X> lotsOfX;

I want to sort them based on the value field first and then based on dateValue descending within each 'group' of 'value' fields.

What I have been doing so far is:

List<X> sortedList = ImmutableList.copyOf(Ordering.natural().onResultOf(dateValueSortFunction).reverse().sortedCopy(lotsOfX));
sortedList = ImmutableList.copyOf(Ordering.natural().onResultOf(stringValueSortFunction).sortedCopy(sortedList));

The functions are defined as:

public class DateValueSortFunction<X> implements Function<X, Long> {

    @Override
      public Long apply(X input) {
        return input.getDateValue().getTime();  //returns millis time
      }
}

And:

public class StringValueSortFunction<X> implements Function<X, Integer> {

      @Override
        public Integer apply(X input) {
          if(input.getStringValue().equalsIgnoreCase("Something"))
            return 0;
          else if(input.getStringValue().equalsIgnoreCase("Something else"))
            return 1;
          else
            return 2;
        }
}

Expected output in sortedList is:

Something   03/18/2013
Something   03/17/2013
Something else  03/20/2013
Something else  03/19/2013
....

My approach works but is obviously inefficient for traversing the list twice. Is there a better way of doing this?

I am using this in a GWT app. Implementing comparable is not an option.

Answer: I suspect you want Ordering.compound. You could do it all in one statement, but I'd use:

Ordering<X> primary = Ordering.natural().onResultOf(stringValueSortFunction);
Ordering<X> secondary = Ordering.natural()
                              .onResultOf(dateValueSortFunction)
                              .reverse();
Ordering<X> compound = primary.compound(secondary);

List<X> sortedList = compound.immutableSortedCopy(lotsOfX);

Case Insensitive sorting using Google Guava

Question: Current I am using the following 2 pieces of code in 2 different places to create a sorted, immutable list.

return Ordering.natural().immutableSortedCopy(iterable);

and

return Ordering.usingToString().immutableSortedCopy(machines);

However, this makes the 'ordering' case sensitive.

How do I use the guava apis to make a case-insensitive sorted immutable list?

Answer: I believe you will need to use the from method with the String.CASE_INSENSITIVE_ORDER comparator, like this.

return Ordering.from(String.CASE_INSENSITIVE_ORDER).immutableSortedCopy(iterable);

Sorting the data in increasing order based on keys in Multimap Google Guava

Question: I created a Multimap

Multimap<Integer, String> mhm= ArrayListMultimap.create();

my numbers range from 0 to 2400. I have inserted data like

 mhm.put(800 ,"A")
 mhm.put(1200 ,"B")
 mhm.put(1200 ,"A")
 mhm.put(1500 ,"B")
 mhm.put(600 ,"A")
 mhm.put(700 ,"B")
 mhm.put(1200 ,"A")
 mhm.put(1201 ,"B")

I want to sort the Multimap on key field that is Integer? There are not much posts on satckoverflow which tells how to do this.

expected output:

 mhm.put(600 ,"A")
 mhm.put(700 ,"B")
 mhm.put(800 ,"A")
 mhm.put(1200 ,"B")
 mhm.put(1200 ,"A")
 mhm.put(1200 ,"A")
 mhm.put(1201 ,"A")
 mhm.put(1500 ,"B")

Please note that the expected output is sorted only on key. If two keys have same value then the key which appeared first should come first. how to enforce this? Is this case automatically taken care when we sort the Multimap on keys?

Answer: You want keys in natural order - just use custom Multimap using newListMultimap from Multimaps class:

ListMultimap<Integer, String> mhm = Multimaps.newListMultimap(
  new TreeMap<Integer, Collection<String>>(),
  new Supplier<List<String>>() {
    public List<String> get() {
      return Lists.newArrayList();
    }
  });

In Java 8 it's shorter:

ListMultimap<Integer, String> mhm = Multimaps.newListMultimap(
    new TreeMap<>(), ArrayList::new);

if you're using Guava 16+ (and you should now), you can use MultimapBuilder which is even more clean:

ListMultimap<Integer, String> mhm = MultimapBuilder.treeKeys().arrayListValues().build();

Because you can think of multimap as map key -> collection, just use JDK's TreeMap which is sorted according to the natural ordering of its keys.

Example:

mhm.put(2, "some");
mhm.put(1, "value");
mhm.put(2, "here");
System.out.println(mhm.toString());
// { 1: [ "value" ], 2: [ "some", "here" ] }

Google Guava: How to use ImmutableSortedMap.naturalOrder?

Question: I want to create an ImmutableSortedMap using a builder. I know I can create the builder like this:

ImmutableSortedMap.Builder<Integer, String> b1 =
    new ImmutableSortedMap.Builder<Integer, String>(Ordering.natural());

and then use that to build maps, for example:

ImmutableSortedMap<Integer, String> map =
    b1.put(1, "one").put(2, "two").put(3, "three").build();

I noticed that class ImmutableSortedMap has a method naturalOrder() that returns a Builder with natural ordering. However, when I try to call this method, I get strange errors. For example, this gives a strange "; expected" error:

// Does not compile
ImmutableSortedMap.Builder<Integer, String> b2 =
    ImmutableSortedMap<Integer, String>.naturalOrder();

What is the correct syntax to call the naturalOrder() method?

The API documentation of the method mentions some compiler bug. Does that have anything to do with this method not working?

Answer: The correct syntax is

ImmutableSortedMap.Builder<Integer, String> b2 =
    ImmutableSortedMap.<Integer, String>naturalOrder();

In your case:

ImmutableSortedMap<Integer, String> map =
    ImmutableSortedMap.<Integer, String>naturalOrder().put(1, "one").put(2, "two").put(3, "three").build();