在 TreeMap 中的 Key 上对自定义数据结构进行排序

发布于 2024-12-04 14:33:02 字数 2283 浏览 0 评论 0原文

我正在尝试按键对 TreeMap 进行排序。 Key 是一些自定义的 DataStructure,具有 int、List、String 等。 我期望对其进行排序的成员有一些重复项。假设该成员是 Rank。超过 1 个对象可以具有相同的等级。

简化版本示例:

注意:在 CompareTo 方法中,不会有意返回 0,以不忽略重复项。(如果这不是避免重复项的正确方法,请纠正我)

import java.util.TreeMap;


public class TreeTest {

public static void main(String[] args) {
    TreeMap<Custom,String> t = new TreeMap<Custom,String>();
    Custom c1 = new Custom();
    c1.setName("a");
    c1.setRank(0);

    Custom c2 = new Custom();
    c2.setName("b");
    c2.setRank(1);

    Custom c3 = new Custom();
    c3.setName("c");
    c3.setRank(0);

    t.put(c1, "first");
    t.put(c2, "Second");
    t.put(c3, "Third");

    System.out.println(t.keySet());

    for(Custom c:t.keySet()){
        System.out.println(t.get(c));
    }
  }
}

和自定义对象

package com.example.ui;

 public class Custom implements Comparable<Custom>{

int rank;
String name;

public int getRank() {
    return rank;
}

public void setRank(int rank) {
    this.rank = rank;
}

public String getName() {
    return name;
}

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



@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((name == null) ? 0 : name.hashCode());
    result = prime * result + rank;
    return result;
}

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    Custom other = (Custom) obj;
    if (name == null) {
        if (other.name != null)
            return false;
    } else if (!name.equals(other.name))
        return false;
    if (rank != other.rank)
        return false;
    return true;
}

    // 0 is not returned intentionally to NOT ignore duplicates.
 public int compareTo(Custom o) {
    if(o.rank>this.rank)
        return 1;
    if(o.rank==this.rank)
        return -1;
    return -1;
 }
 }

输出::

[com.example.ui.Custom@fa0, com.example.ui.Custom@fbe, com.example.ui.Custom@f80]
null
null
null

预期: 第一、第二、第三分别基于排名 0、1、0。

我在谷歌上看了几个例子。其中大多数是使用具有原始数据类型的键或值进行 TreeMap 排序的基本用法,但在对成员进行排序时没有重复项 是自定义键数据结构的一部分。

请帮忙?

I am trying to sort a TreeMap on key. Key is some custom DataStructure having int, List, String, etc.
The member on which I am expecting a sort has some duplicates. Let's say that member is Rank. More than 1 object can have same rank.

Simplified version example:

NOTE: in the CompareTo method below 0 is not returned intentionally to NOT ignore duplicates.(Please correct me if this is not the right way to avoid duplicates)

import java.util.TreeMap;


public class TreeTest {

public static void main(String[] args) {
    TreeMap<Custom,String> t = new TreeMap<Custom,String>();
    Custom c1 = new Custom();
    c1.setName("a");
    c1.setRank(0);

    Custom c2 = new Custom();
    c2.setName("b");
    c2.setRank(1);

    Custom c3 = new Custom();
    c3.setName("c");
    c3.setRank(0);

    t.put(c1, "first");
    t.put(c2, "Second");
    t.put(c3, "Third");

    System.out.println(t.keySet());

    for(Custom c:t.keySet()){
        System.out.println(t.get(c));
    }
  }
}

And Custom Object

package com.example.ui;

 public class Custom implements Comparable<Custom>{

int rank;
String name;

public int getRank() {
    return rank;
}

public void setRank(int rank) {
    this.rank = rank;
}

public String getName() {
    return name;
}

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



@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((name == null) ? 0 : name.hashCode());
    result = prime * result + rank;
    return result;
}

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    Custom other = (Custom) obj;
    if (name == null) {
        if (other.name != null)
            return false;
    } else if (!name.equals(other.name))
        return false;
    if (rank != other.rank)
        return false;
    return true;
}

    // 0 is not returned intentionally to NOT ignore duplicates.
 public int compareTo(Custom o) {
    if(o.rank>this.rank)
        return 1;
    if(o.rank==this.rank)
        return -1;
    return -1;
 }
 }

Output::

[com.example.ui.Custom@fa0, com.example.ui.Custom@fbe, com.example.ui.Custom@f80]
null
null
null

Expected:
First, Second, Third based on Rank 0,1,0 respectively.

I looked at couple of examples on Google. Most of them were basic usage on TreeMap sort using keys or values with primitive datatypes, but none with duplicates when sorting member
is a part of custom key DataStructure.

Please help?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

静若繁花 2024-12-11 14:33:02

问题是您的 compareTo 实现与 TreeMap。来自 API 文档:

请注意,排序映射维护的顺序(无论是否是
提供了显式比较器)必须与 equals 一致,如果
这个排序后的map是为了正确实现Map接口。

一种可能的一致实现是首先按排名进行比较,如果排名值相等则再按名称进行比较。对于两个具有相同等级和相同名称的 Custom 实例,您不应期望能够将它们作为键存储在同一个 Map 中 - 这违反了 Map 的约定

public int compareTo(Custom o) {
  int ret = this.rank - o.rank;

  // Equal rank so fall back to comparing by name.
  if (ret == 0) {
    ret = this.name.compareTo(o.name);
  }

  return ret;
}

The problem is that your implementation of compareTo is not consistent with equals, which is required by TreeMap. From the API docs:

Note that the ordering maintained by a sorted map (whether or not an
explicit comparator is provided) must be consistent with equals if
this sorted map is to correctly implement the Map interface.

One possible consistent implementation would be to first compare by rank and then by name if the rank values are equal. For two instances of Custom with equal ranks and identical names you should not expect to be able to store them both as keys within the same Map - This violates the contract of Map.

public int compareTo(Custom o) {
  int ret = this.rank - o.rank;

  // Equal rank so fall back to comparing by name.
  if (ret == 0) {
    ret = this.name.compareTo(o.name);
  }

  return ret;
}
梦言归人 2024-12-11 14:33:02

如前所述,您的 equals 和 CompareTo 实现彼此不一致。如果我正确地阅读了您的问题,您需要的是保留具有相同密钥的重复项。我建议您查看 TreeMultimap< /a> Google Guava 集合。它为每个值对象创建集合容器,以便保留具有相同键的不同值。
例如,

treeMultimap.put ("rank1", "Joe");
treeMultimap.put ("rank1", Jane");
treeMultimap.get ("rank1"); // Set("Joe","Jane");

此数据结构中的约束是 K,V 对必须是唯一的。也就是说,您不能在 Multimap 中插入 ("rank1", "Joe") 两次。

一个重要的注意事项:您看到如此多使用简单类型(特别是字符串)的 Map 示例的原因是映射中的键必须是不可变的。对象的 equals 和 hashcode 值在用作映射中的键时不得更改。转换为您的示例,您无法执行 customObject.setRank(...) 并在将排名值用作键时更新排名值。为此,您首先需要删除键及其值,更新它,然后再次插入。

As mentioned, your implementation of equals and compareTo are not consistent with each other. If I read your question correctly, what you require is to preserve duplicates that have the same key. I'd recommend you to look into the TreeMultimap of the Google Guava collections. It creates set containers for each value object sothat different values having the same key are preserved.
e.g.

treeMultimap.put ("rank1", "Joe");
treeMultimap.put ("rank1", Jane");
treeMultimap.get ("rank1"); // Set("Joe","Jane");

The constrain in this data structure is that K,V pairs must be unique. That is, you can't insert ("rank1", "Joe") twice in the Multimap.

One important note: The reason why you see so many examples of Map, using simple types and, in particular, strings, is that keys in a map must be immutable. The equals and hashcode values of an object must not change in the time it's used as a key in a map. Translated to your example, you cannot do customObject.setRank(...) and updates a rank value when it's used as a key. To do so, you first need to remove the key and its values, update it and then insert it again.

徒留西风 2024-12-11 14:33:02

您还可以通过将 Comparator 实现为匿名内部类型并重写 Compare() 以返回所需的比较来实现此目的。

public class TreeMaps 
{
    public static void main(String[] args) 
    {
    Custom c1 = new Custom(1,"A");
    Custom c2 = new Custom(3,"C");
    Custom c3 = new Custom(2,"B");

    TreeMap<Custom , Integer > tree = new TreeMap<Custom, Integer>  (new Comparator<Custom>() {

                                            @Override
                                            public int compare(Custom o1, Custom o2) {

                                                return o1.rank - o2.rank;
                                            }
                                        });
    tree.put(c1, 1);
    tree.put(c2, 2);
    tree.put(c3, 3);

    System.out.println(tree);
}
}

class Custom
{
int rank ; 
String name  ; 
public Custom(int rank , String name) {
    this.rank = rank ;
    this.name = name ;

}

@Override
public String toString()
{
    return "Custom[" + this.rank + "-" + this.name + "]" ;
}
}

You can also do it by implementing Comparator as anonymous inner type and override compare() to return desired comparison.

public class TreeMaps 
{
    public static void main(String[] args) 
    {
    Custom c1 = new Custom(1,"A");
    Custom c2 = new Custom(3,"C");
    Custom c3 = new Custom(2,"B");

    TreeMap<Custom , Integer > tree = new TreeMap<Custom, Integer>  (new Comparator<Custom>() {

                                            @Override
                                            public int compare(Custom o1, Custom o2) {

                                                return o1.rank - o2.rank;
                                            }
                                        });
    tree.put(c1, 1);
    tree.put(c2, 2);
    tree.put(c3, 3);

    System.out.println(tree);
}
}

class Custom
{
int rank ; 
String name  ; 
public Custom(int rank , String name) {
    this.rank = rank ;
    this.name = name ;

}

@Override
public String toString()
{
    return "Custom[" + this.rank + "-" + this.name + "]" ;
}
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文