简体   繁体   中英

How to sort a multimap in Java?

So I am trying to sort a multimap in Java, here's what the maps looks like:

Map<Integer, Pair<Double, Color>> inputMap;
Map<Integer, Pair<Double, Color>> outputMap;

and here is my Pair class:

class Pair<Double, Color> {
  public final Double x;
  public final Color y;
  public Pair(Double x, Color y) {
    this.x = x;
    this.y = y;
  }
}

I am trying to sort it by the first item of Pair. I tried to use something like this:

outputMap = new TreeMap<>(inputMap);

but that doesn't exactly work how I thought it would... It doesn't seem to sort it at all.

How would I sort the map by Pair.X?

All SortedMap implementations (including TreeMap ) sort by the key not by value .

https://docs.oracle.com/javase/7/docs/api/java/util/SortedMap.html

A Map that further provides a total ordering on its keys.

Sorting the Map internally by value also would be bad for the performance which is optimised for access through the key .


A Map only ever associates one value to each key.

This becomes especially clear when you look at the documentation of put

Associates the specified value with the specified key in this map (optional operation). If the map previously contained a mapping for the key, the old value is replaced by the specified value.

So what you defined with Map<Integer, Pair<Double, Color>> is not even a MultiMap . Which explains why it does not behave how you expect it to.


What you are looking for is a SortedSetMultimap

A SetMultimap whose set of values for a given key are kept sorted; that is, they comprise a SortedSet.

With

//Class renamed to reflect what it actually is, to avoid conflicts with other Pairs. Also you don't need generics then, which you did not use any way.
class ColorPair implements Comparable {
  public final Double x;
  public final Color y;
  public ColorPair (Double x, Color y) {
    this.x = x;
    this.y = y;
  }

  //compares only the first component of the Pair
  public int compareTo(ColorPair  other){
    return Double.compare(x, other.x)
  }

}

Then you can create the SortedSetMultimap with

TreeMultimap<Integer, ColorPair> sortedSetMap = new TreeMultimap<>() `

and fill it with the ColorPair s

You can't sort by map values - by keys only.

If you need both - HashMap for O(1) access to objects and sorted traversal - create a sorted list and keep same objects there.

List<Pair<Double, Object>>outputList = inputMap.values().stream().sorted((o1, o2) -> 0 /*IMPLEMENT_COMPARATOR_HERE*/).collect(Collectors.toList());

If the map is mutable - it would be better to create a class that will contain both - map and list - and provide methods like "add" "get" and "remove". Direct access to those structures should be avoided.

Don't forget to synchronize operations on both data structures to make your code thread safe.

Highlight

You can sort the map using:

Map<Integer, Pair<Double, Color>> sortedMap = map.entrySet().stream()
        .sorted(Comparator.comparingDouble(e -> e.getValue().x))
        .collect(Collectors.toMap(
                Map.Entry::getKey,
                Map.Entry::getValue,
                (p1, p2) -> p1,
                LinkedHashMap::new));

Full code

public static void main(String[] args) {
    new MyClass().test();
}

public void test() {
    Map<Integer, Pair<Double, Color>> map = Map.of(
            1, new Pair<>(1.0, Color.BLACK),
            2, new Pair<>(3.0, Color.RED),
            3, new Pair<>(2.0, Color.BLUE));

    Map<Integer, Pair<Double, Color>> sortedMap = map.entrySet().stream()
            .sorted(Comparator.comparingDouble(e -> e.getValue().x))
            .collect(Collectors.toMap(
                    Map.Entry::getKey,
                    Map.Entry::getValue,
                    (p1, p2) -> p1,
                    LinkedHashMap::new));

    System.out.println(sortedMap);
}

class Pair<Double, Color> {
    public final Double x;
    public final Color y;

    public Pair(Double x, Color y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Pair<?, ?> pair = (Pair<?, ?>) o;
        return Objects.equals(x, pair.x) &&
                Objects.equals(y, pair.y);
    }

    @Override
    public int hashCode() {

        return Objects.hash(x, y);
    }

    @Override
    public String toString() {
        return "Pair{" +
                "x=" + x +
                ", y=" + y +
                '}';
    }
}

Output

{1=Pair{x=1.0, y=java.awt.Color[r=0,g=0,b=0]}, 3=Pair{x=2.0, y=java.awt.Color[r=0,g=0,b=255]}, 2=Pair{x=3.0, y=java.awt.Color[r=255,g=0,b=0]}}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM