我们有一个包含4列的表格:
A B C VALUE
1 2 0 100
0 3 3 200
0 0 7 400
0 0 0 700
键由3列组成:A,B和C。
我们将此表保存在HashMap<ImmutableTriple<Integer, Integer, Integer>, Integer>
对象上。
零代表默认值,因此在找不到确切密钥的情况下,我们应根据以下逻辑拉出相关密钥:
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的键获取会产生代码异味的问题。有没有更好,更清洁的方法?
答案 0 :(得分:0)
您可以使用默认值为三胞胎的所有可能状态创建“掩码映射”:
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});
}
假设为空,表示必须填充键值。然后,您可以从该掩码创建三元组键的列表:
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);
但是我不确定这是否比您的“代码气味”;)好。
编辑: 当然,如果您避免创建中间集合(findFirst是短循环运算符),这样做会更有效:
Integer result = mask.stream()
.map(row -> genrateTriple(row, a, b, c))
.map(cache::get)
.findFirst()
.orElse(null);
答案 1 :(得分:0)
有时候,如果它使代码更具可读性,那么爆炸循环不一定是一件坏事。
我建议您将代码保持原样,因为稍后您会看到,如果尝试压缩它,很可能会遇到更糟糕的情况。如果您坚持要继续阅读。
我的想法很简单:您实际上是试图遍历几个掩码以用于您的匹配模式。在这种情况下,掩码为“是否使用id或默认值”。
因此,我们可以使用某种数据结构来存储掩码,如下所示:
// 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;
}
}
P.S。对于此特定情况,由于0 =默认值,因此您可以为掩码使用1和0而不是true和false来进行int aId = mask.getLeft() * a;
,但这很简单。
如您所见,尽管我们确实将代码“压缩”成循环,但其效果还是值得商de的。实际上,我认为该代码实际上不那么可读。因此,再次强调一下,有时候爆炸循环并不一定很糟糕。