如何在 Java 中使用自定义比较器来定义嵌套映射的最内层映射?

How to use a customized comparator to define the innermost map of a nested map in Java?

提问人:leo 提问时间:10/27/2023 最后编辑:leo 更新时间:10/28/2023 访问量:65

问:

我在 Java 中有一个类 Range 和一个比较器:

class Range{
    int start;
    int end;
}

class RangeComparator implements Comparator<Range> {
  @Override
  public int compare(Range range1, Range range2) {
    //implementation
  }
}

另一个类具有嵌套映射;

class MyClass{
    private static Map<String, Map<String, Map<Range, List<String>>>> nestedMap;
}

我需要:

  • nestedMap 的最内层映射,该映射是使用 RangeComparator 对其键进行排序的 TreeMap,以及
  • 创建 MyClass 对象时,nestedMap 为空,并且
  • 最内层的映射具有在将条目插入映射时使用特定比较器的属性。

如何实现?

在 C++ 中,这些可以通过在嵌套地图的声明中分离比较器来实现,如下所示:

struct Comp {
  const bool operator()(const Range& range1, const Range& range2) const {
    // implementation
  }
};

class MyClass{
  // declare the nested map with the comparator Comp
  map<string, map<string, map<Range, vector<string>, Comp>>> nestedMap;
};

我想知道如何在 Java 中实现相同的目的。

Java 字典 比较器

评论

0赞 daniu 10/27/2023
拥有如此深度嵌套的地图是一种明确的代码气味。我建议考虑为每个级别使用单独的课程。
0赞 Bohemian 10/28/2023
有没有充分的理由 范围不应该总是可比的 使用?RangeComparator

答:

1赞 Chaosfire 10/27/2023 #1

您只能直接初始化最外层的映射,内部映射与一个键相关联,每个键都有自己的映射作为值。除非您事先知道密钥并提前初始化,否则这是不可能的。

关于您的需求:

  1. the innermost map of the nestedMap to be a TreeMap that uses the RangeComparator to sort its keys- 您不能声明 will 是具有特定比较器的比较器。MapTreeMap

您可以使嵌套地图成为 TreeMap 和范围的可比性

Map<String, Map<String, TreeMap<Range, List<String>>>> nestedMap;
...
class Range implements Comparable<Range> {

  @Override
  public int compareTo(Range other) {
    //logic
  }
}

由于多种原因,这可能对您的情况不利 - 范围不应具有自然顺序,您不应绑定到具体实现,仍然可以使用不同的比较器创建映射,因此您的顺序不会被遵循,等等。在这种情况下(原则上),更可取的解决方案是封装创建此最内层映射的逻辑。请参阅下面的示例如何操作。Map

  1. the nestedMap is empty when an object of MyClass is created- 那么 nestedMap 不应该是静态的(这意味着它属于类,而不是实例)。
  2. the innermost map has the property of using the specific comparator when an entry is inserted into the map- 第一个要求已经涵盖了这一点,当使用 A 创建时,它会根据比较器按其键对条目进行排序,如果没有比较器,它们会根据其自然顺序进行排序(如果键未实现,则会抛出)。TreeMapComparatorClassCastExceptionComparable

按需初始化地图的方法之一是使用 Map.computeIfAbsent()。

如果指定的键尚未与值关联(或映射到 null),则尝试使用给定的映射函数计算其值,并将其输入到此映射中,除非为 null。

正如我上面所说,这种逻辑应该被封装起来,对外界隐藏起来。

public class MyClass {

  //random implementation to make it compile
  private static final Comparator<Range> RANGE_COMPARATOR = Comparator.comparing(Range::start).thenComparing(Range::end);

  private final Map<String, Map<String, TreeMap<Range, List<String>>>> nestedMap;

  public MyClass() {
    this.nestedMap = new HashMap<>();
  }

  public Map<Range, List<String>> getRangesMap(String key1, String key2) {
    return nestedMap
            .computeIfAbsent(key1, k -> new HashMap<>())
            .computeIfAbsent(key2, k -> new TreeMap<>(RANGE_COMPARATOR));
  }
}

我不知道确切的用例(您可能需要以不同的方式获取地图),但这显示了原理 - 现在您从中获得的每个条目都将根据您的比较器对条目进行排序。Map<Range, List<String>>MyClassTreeMap

评论

0赞 leo 10/28/2023
谢谢你的回复。我希望嵌套映射为空,并且最里面的映射具有在创建 MyClass 对象时使用 RangeComparator 的属性。也许我不应该在我的原始帖子中使用“初始化”这个词。我已经更新了帖子并添加了更多详细信息。
0赞 Chaosfire 10/28/2023
@leo 检查更新的答案。它有更完整的例子如何实现你需要的东西,但本质是一样的,那些额外的要求并没有从根本上改变它的方式。
0赞 leo 11/1/2023
谢谢@Chaosfire。我同意应该封装“比较”逻辑。但我觉得 C++ 提供了一种更方便的方式来“封装”它,即在声明映射时(尽管,如果我的理解是正确的,它会在声明映射时初始化映射)。但是,在 Java 中,我们必须编写逻辑来封装它。