我们有一个包含4列的表:
A B C VALUE
1 2 0 100
0 3 3 200
0 0 7 400
0 0 0 700 key由A、B、C三列组成。
我们将这个表保存在一个HashMap<ImmutableTriple<Integer, Integer, Integer>, Integer>对象上。
0表示默认值,所以如果找不到确切的key,我们应该按照下面的逻辑拉取相应的key:
value = cache.get(new ImmutableTriple<>(a, b, c));
if (value != null) {
return value;
}
value = cache.get(new ImmutableTriple<>(a, b, 0));
if (value != null) {
return value;
}
value = cache.get(new ImmutableTriple<>(a, 0, c));
if (value != null) {
return value;
}
value = cache.get(new ImmutableTriple<>(a, 0, 0));
if (value != null) {
return value;
}
value = cache.get(new ImmutableTriple<>(0, 0, 0));
if (value != null) {
return value;
}通过键获取3个ID的默认值会产生代码气味的问题。有没有更好\更干净的方法呢?
发布于 2018-07-08 23:45:46
您可以使用默认值为三元组的所有可能状态创建一个“掩码映射”,例如:
private static List<Integer[]> mask = new ArrayList<>();
static {
mask.add(new Integer[] {null, null, null});
mask.add(new Integer[] {0, null, null});
mask.add(new Integer[] {null, 0, null});
mask.add(new Integer[] {null, null, 0});
mask.add(new Integer[] {null, 0, 0});
mask.add(new Integer[] {0, null, 0});
mask.add(new Integer[] {0, 0, null});
mask.add(new Integer[] {0, 0, 0});
}设为null,表示必须填充键值。然后,您可以使用此掩码创建三元组密钥的列表:
List<Triple<Integer, Integer, Integer>> triples = mask.stream()
.map(row -> genrateTriple(row, a, b, c))
.collect(Collectors.toList());generateTriplets的代码如下:
private Triple<Integer, Integer, Integer> genrateTriple(Integer[] row, int... values) {
int[] tripleElems = new int[3];
for (int i = 0; i < row.length; i++) {
tripleElems[i] = row[i] == null ? values[i] : row[i];
}
return new ImmutableTriple<>(tripleElems[0], tripleElems[1], tripleElems[2]);
}并在搜索中使用它:
return triples.stream()
.map(triple -> cache.get(triple))
.findFirst()
.orElse(null);但我不确定这是否比你的“代码气味”更好;)。
EDITED:当然,如果您避免创建中间集合(findFirst是一个简短的回路运算符),这将更加有效:
Integer result = mask.stream()
.map(row -> genrateTriple(row, a, b, c))
.map(cache::get)
.findFirst()
.orElse(null);发布于 2018-07-08 23:57:19
有时候,有一个松散的循环不一定是一件坏事,如果它使代码更具可读性的话。
我建议你保持代码的原样,因为你很快就会看到,如果你试图压缩它,你很可能会得到更糟糕的结果。如果你坚持要继续读下去。
我的想法很简单:您实际上是在尝试遍历几个掩码,以用于匹配模式。这种情况下的掩码是“使用id还是default”。
掩码a b c,如果不匹配,则返回H210H111返回null。G213
因此,我们可以使用某种数据结构来存储掩码,如下所示:
// true = use id (don't mask); false = use default (mask).
List<ImmutableTriple<Boolean, Boolean, Boolean>> matchMasks = new ArrayList<>();
matchMasks.add(new ImmutableTriple<>(true, true, true));
matchMasks.add(new ImmutableTriple<>(true, true, false));
matchMasks.add(new ImmutableTriple<>(true, false, true));
matchMasks.add(new ImmutableTriple<>(true, false, false));
matchMasks.add(new ImmutableTriple<>(false, false, false));当然,您可以使用List的List、Array的Array或任何合理的方法来存储此掩码。
现在我们可以使用循环来压缩您的代码:
private Integer match(int a, int b, int c)
{
Integer value = null;
// Loop through match rules.
for (ImmutableTriple<Boolean, Boolean, Boolean> mask : matchMasks)
{
// Check whether to apply mask.
int aId = mask.getLeft() ? a : 0;
int bId = mask.getMiddle() ? b : 0;
int cId = mask.getRight() ? c : 0;
// Try to match cache.
value = cache.get(new ImmutableTriple<>(aId, bId, cId));
// Stop if match found.
if (value != null) break;
}
return value;
}将所有内容放在一个类中:
public class Test
{
private HashMap<ImmutableTriple<Integer, Integer, Integer>, Integer> cache = <your table here>;
private List<ImmutableTriple<Boolean, Boolean, Boolean>> matchMasks = new ArrayList<>();
public Test()
{
// true = use id (don't mask); false = use default (mask).
this.matchMasks.add(new ImmutableTriple<>(true, true, true));
this.matchMasks.add(new ImmutableTriple<>(true, true, false));
this.matchMasks.add(new ImmutableTriple<>(true, false, true));
this.matchMasks.add(new ImmutableTriple<>(true, false, false));
this.matchMasks.add(new ImmutableTriple<>(false, false, false));
}
private Integer match(int a, int b, int c)
{
Integer value = null;
// Loop through match rules.
for (ImmutableTriple<Boolean, Boolean, Boolean> mask : this.matchMasks)
{
// Check whether to apply mask.
int aId = mask.getLeft() ? a : 0;
int bId = mask.getMiddle() ? b : 0;
int cId = mask.getRight() ? c : 0;
// Try to match cache.
value = this.cache.get(new ImmutableTriple<>(aId, bId, cId));
// Stop if match found.
if (value != null) break;
}
return value;
}
}备注:对于此特定情况,由于0=默认值,因此您可以使用1和0代替true和false作为掩码,并执行此int aId = mask.getLeft() * a;,但这是微不足道的。
正如您所看到的,尽管我们确实将您的代码“压缩”到一个循环中,但它的效果是非常有争议的。事实上,我认为这段代码实际上可读性较差。因此,有时坚持使用爆炸循环并不一定是坏事。
https://stackoverflow.com/questions/51231990
复制相似问题