当键是字符串时,如何在Java中创建范围映射
我想创建一个大范围的地图,将按键及其编号映射到桶,例如:当键是字符串时,如何在Java中创建范围映射,java,java-8,treemap,Java,Java 8,Treemap,我想创建一个大范围的地图,将按键及其编号映射到桶,例如: NavigableMap<String, String> map = new TreeMap<>(); map.put("var2#" + 0L, "out0"); // "var2#0..100 => out0 map.put("var2#" + 100L, "out1"); // "var2#100
NavigableMap<String, String> map = new TreeMap<>();
map.put("var2#" + 0L, "out0"); // "var2#0..100 => out0
map.put("var2#" + 100L, "out1"); // "var2#100..200" => out1
map.put("var2#" + 200L, "out2"); // "var2#200..300" => out2
map.put("var2#" + 300L, "out3"); // "var2#300..+" => out3
,但如果发送var2#2000,取而代之的是res“out3”,我得到的是“out2”,依此类推
String res = map.floorEntry("var2#" + 2000L).getValue();
Syso(res) ==> out2 , BUT I expected result => "out3"
// because it is bigger that the range.
附言:
另一个问题-当我在不同的键上有相同的长值时,我希望先匹配字符串,然后匹配数字
map.put("var1#" + 200L, "out0");
map.put("var2#" + 200L, "out1");
map.put("var3#" + 200L, "out2");
map.put("var4#" + 200L, "out3");
String out1 = map.floorEntry("var2#" + 150L).getValue();
System.out.println("====> " + out1); //expected out1 , because only match of "var2
String out3 = map.floorEntry("var2#" + 250L).getValue(); //expected out1 , because only match of "var2
System.out.println("====> " + out3);" ....
有什么建议吗,也许是一些算法 问题在于
TreeMap
使用字符串进行比较。因此它按字母顺序排序,var2#2000
介于var2#200
和var2#300
之间。您应该或者使用Long
或Integer
作为TreeMap
的键。因此,这应该是可行的:
NavigableMap<Long, String> map = new TreeMap<>();
map.put(0L, "out0"); // "var2#0..100 => out0
map.put(100L, "out1"); // "var2#100..200" => out1
map.put(200L, "out2"); // "var2#200..300" => out2
map.put(300L, "out3"); // "var2#300..+" => out3
NavigableMap=newtreemap();
映射输出(0升,“输出0”);//“var2#0..100=>out0
map.put(100L,“out1”);/“var2#100..200”=>out1
map.put(200L,“out2”);/“var2#200..300”=>out2
map.put(300L,“out3”);/“var2#300..+”=>out3
您可以提取密钥的第二部分,并将其用作导航地图的比较器:
Comparator.comparingLong(key -> Long.parseLong(key.split("#")[1]))
因此:
导航地图=
新的树映射(Comparator.comparingLong(key->Long.parseLong(key.split(“#”)1]);
map.put(“var2#“+0L,”out0”);/“var2#0..100=>out0
map.put(“var2#“+100L,“out1”)//“var2#100..200”=>out1
map.put(“var2#“+200L,“out2”)//“var2#200..300”=>out2
map.put(“var2#“+300L,“out3”)//“var2#300..+”=>out3
资产(map.floorEntry(“var2#“+150L).getValue()).isEqualTo(“out1”);
资产(map.floorEntry(“var2#“+2000L).getValue()).isEqualTo(“out3”);
我将拆分键以获得每个变量范围的映射:
Map<String, Ranges> map;
这将很容易填充:
Map<String, Ranges> map = new HashMap<>();
Ranges r = new Ranges();
r.setFloor(0L, "out1");
r.setFloor(100L, "out2");
map.put("var1", r);
r = new Ranges();
r.setFloor(0L, "out3");
r.setFloor(100L, "out4");
map.put("var2", r);
System.out.println(map.get("var1").getFloor(50L));
System.out.println(map.get("var2").getFloor(150L));
Map Map=newhashmap();
范围r=新范围();
r、 设置地板(0升,“室外1”);
r、 设置地板(100L,“out2”);
地图放置(“var1”,r);
r=新范围();
r、 设置地板(0升,“out3”);
r、 设置地板(100L,“out4”);
地图放置(“var2”,r);
System.out.println(map.get(“var1”).getFloor(50L));
系统输出打印LN(映射获取(“var2”).getFloor(150L));
out1
输出4
我们可以使用NavigableMap
而不是HashMap
,但我没有看到这里的要点
请注意,这不是NPE安全的,这并不能保证解决方案的简短和可读性。按字符串比较前缀,然后按数字比较后缀的一种方法是:
public static int compareParts(String a, String b) {
String[] aa = a.split("#", 2), ba = b.split("#", 2);
int c = aa[0].compareTo(ba[0]);
return c != 0? c: Integer.compare(Integer.parseInt(aa[1]), Integer.parseInt(ba[1]));
}
但是,由于比较方法可能会经常被调用,即使是单个查找也可能涉及多个比较,因此值得花一些时间研究以提高性能,即使代码看起来更复杂:
public static int compareParts(String a, String b) {
final int aLen = a.length(), bLen = b.length(), l = Math.min(aLen, bLen);
int ix = 0;
stringPart: {
for(; ix < l; ix++) {
char aCh = a.charAt(ix), bCh = b.charAt(ix);
int cmp = Character.compare(aCh, bCh);
if(cmp != 0)
return aCh == '#'? -1: bCh == '#'? +1: cmp;
if(aCh == '#') break stringPart;
}
return 0;
}
// number part
int aIx = ix+1, bIx = aIx;
while(aIx < aLen && a.charAt(aIx)=='0') aIx++;
while(bIx < bLen && b.charAt(bIx)=='0') bIx++;
int cmp = Integer.compare(aLen-aIx, bLen-bIx);
for(; cmp == 0 && aIx < aLen; aIx++, bIx++) {
cmp = Character.compare(a.charAt(aIx), b.charAt(bIx));
}
return cmp;
}
提高你的答案,因为只保留有意义的部分作为关键是明智的。它更具可读性,也可以避免将来出现错误。如果这些键中的“var2#”
是常量,我们肯定可以使用数值。但是如果想法是为var1
,var2
提供一个范围<代码>变量
。。。这需要一个类似于Map
@AxelH的东西,var2不是const,它是一个非常大的Map,前缀是一些“string”,位于键入的长数字之后。例如,“var1#100,var1#200,…bla1#1000,bla5#2000…”请将此信息添加到您的questin@VitalyT中,这很重要。这对于更大的地图来说将是痛苦的@AxelH是的,这个比较器不是很有效。嗨@Holger,我有一些问题,你的建议不起作用,如果你能看一看,那就太好了,谢谢:)@VitalyT当你使用floorEntry
时,你请求一个等于或更小的条目,所以floorEntry(“var2”+150L)
永远不会出现在“var2”的条目上+200L
,因为200L
既不等于也不小于150L
。由于没有带有“var2#”前缀的较小键,因此最近的键是“var1#”+200L
。前缀具有优先权。但是,比较器仍然强制执行一个总顺序,floorEntry
将根据该顺序始终返回具有相等或更小键的条目。你不能期望它根据附加条件突然返回一个带有更大密钥的条目。谢谢,我明白你的意思,但是如果我需要先通过“var2#”搜索,然后映射到backet范围,你会如何更改代码,因为它工作得很好,直到我发现一个类似于下面描述的场景的错误:(您可以使用String out1=map.subMap(“var2#0”,true,“var3#0”,false)。get(“var2#150”)
强制执行前缀,然后,结果将是null
,因为没有较小的键。或者,如果没有具有该前缀的较小键,则使用较高的键,如map.Entry e=map.subMap(“var2#0”,true,“var2#150”,true)。lastEntry();if(e==null)e=map.subMap(“var2#0”,true,“var3#0”,false)。firstEntry();String out1=e!=null?e.getValue():null;
。如果不存在带有该前缀的键,它仍然可能导致null
。我的意思是在您的建议代码段中,“int compareParts(String a,String b)”。。。
class Ranges {
NavigableMap<Long, String> map = new TreeMap<>();
public String setFloor(long l, String s){
return map.put(l, s);
}
public String getFloor(long l){
return map.floorEntry(l).getValue();
}
}
Map<String, Ranges> map = new HashMap<>();
Ranges r = new Ranges();
r.setFloor(0L, "out1");
r.setFloor(100L, "out2");
map.put("var1", r);
r = new Ranges();
r.setFloor(0L, "out3");
r.setFloor(100L, "out4");
map.put("var2", r);
System.out.println(map.get("var1").getFloor(50L));
System.out.println(map.get("var2").getFloor(150L));
public static int compareParts(String a, String b) {
String[] aa = a.split("#", 2), ba = b.split("#", 2);
int c = aa[0].compareTo(ba[0]);
return c != 0? c: Integer.compare(Integer.parseInt(aa[1]), Integer.parseInt(ba[1]));
}
public static int compareParts(String a, String b) {
final int aLen = a.length(), bLen = b.length(), l = Math.min(aLen, bLen);
int ix = 0;
stringPart: {
for(; ix < l; ix++) {
char aCh = a.charAt(ix), bCh = b.charAt(ix);
int cmp = Character.compare(aCh, bCh);
if(cmp != 0)
return aCh == '#'? -1: bCh == '#'? +1: cmp;
if(aCh == '#') break stringPart;
}
return 0;
}
// number part
int aIx = ix+1, bIx = aIx;
while(aIx < aLen && a.charAt(aIx)=='0') aIx++;
while(bIx < bLen && b.charAt(bIx)=='0') bIx++;
int cmp = Integer.compare(aLen-aIx, bLen-bIx);
for(; cmp == 0 && aIx < aLen; aIx++, bIx++) {
cmp = Character.compare(a.charAt(aIx), b.charAt(bIx));
}
return cmp;
}
NavigableMap<String, String> map = new TreeMap<>(MyClass::compareParts);
map.put("var2#" + 0L, "out0");
map.put("var2#" + 100L, "out1");
map.put("var2#" + 200L, "out2");
map.put("var2#" + 300L, "out3");
String out1 = map.floorEntry("var2#" + 150L).getValue();
System.out.println("out1 = "+out1);
String out3 = map.floorEntry("var2#" + 2000L).getValue();
System.out.println("res = "+out3);