提问人:leo 提问时间:10/27/2023 最后编辑:leo 更新时间:10/28/2023 访问量:65
如何在 Java 中使用自定义比较器来定义嵌套映射的最内层映射?
How to use a customized comparator to define the innermost map of a nested map in Java?
问:
我在 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 中实现相同的目的。
答:
1赞
Chaosfire
10/27/2023
#1
您只能直接初始化最外层的映射,内部映射与一个键相关联,每个键都有自己的映射作为值。除非您事先知道密钥并提前初始化,否则这是不可能的。
关于您的需求:
the innermost map of the nestedMap to be a TreeMap that uses the RangeComparator to sort its keys
- 您不能声明 will 是具有特定比较器的比较器。Map
TreeMap
您可以使嵌套地图成为 TreeMap 和范围的可比性
Map<String, Map<String, TreeMap<Range, List<String>>>> nestedMap;
...
class Range implements Comparable<Range> {
@Override
public int compareTo(Range other) {
//logic
}
}
由于多种原因,这可能对您的情况不利 - 范围不应具有自然顺序,您不应绑定到具体实现,仍然可以使用不同的比较器创建映射,因此您的顺序不会被遵循,等等。在这种情况下(原则上),更可取的解决方案是封装创建此最内层映射的逻辑。请参阅下面的示例如何操作。Map
the nestedMap is empty when an object of MyClass is created
- 那么 nestedMap 不应该是静态的(这意味着它属于类,而不是实例)。the innermost map has the property of using the specific comparator when an entry is inserted into the map
- 第一个要求已经涵盖了这一点,当使用 A 创建时,它会根据比较器按其键对条目进行排序,如果没有比较器,它们会根据其自然顺序进行排序(如果键未实现,则会抛出)。TreeMap
Comparator
ClassCastException
Comparable
按需初始化地图的方法之一是使用 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>>
MyClass
TreeMap
评论
0赞
leo
10/28/2023
谢谢你的回复。我希望嵌套映射为空,并且最里面的映射具有在创建 MyClass 对象时使用 RangeComparator 的属性。也许我不应该在我的原始帖子中使用“初始化”这个词。我已经更新了帖子并添加了更多详细信息。
0赞
Chaosfire
10/28/2023
@leo 检查更新的答案。它有更完整的例子如何实现你需要的东西,但本质是一样的,那些额外的要求并没有从根本上改变它的方式。
0赞
leo
11/1/2023
谢谢@Chaosfire。我同意应该封装“比较”逻辑。但我觉得 C++ 提供了一种更方便的方式来“封装”它,即在声明映射时(尽管,如果我的理解是正确的,它会在声明映射时初始化映射)。但是,在 Java 中,我们必须编写逻辑来封装它。
评论
RangeComparator