Если вы хотите эту проверку эффективно (обычно это не так важно для модульного теста), вы можете сократить объем работы, если оптимистично предположить, что поля не имеют дубликатов, и сначала выполните предварительный дешевый тест.Кроме того, вы можете использовать результат этого предварительного теста для получения фактического поля с дубликатами (если они есть) без Map
.
. В качестве предварительного условия мы должны инкапсулировать операцию отражения
private static int fieldValue(Field f) {
try {
return f.getShort(null);
}
catch(ReflectiveOperationException ex) {
throw new IllegalStateException();
}
}
Далее, нам нужно отобразить потенциальные значения диапазона значений short
в положительный индекс для BitSet
:
private static int shortToIndex(int shortValue) {
return Math.abs(shortValue<<1) | (shortValue>>>31);
}
Это предполагает, что числа с меньшей величинойчаще встречается и сохраняет их величину небольшой, чтобы уменьшить размер получаемой BitSet
.Если значения предполагаются положительными, shortValue & 0xffff
будет предпочтительным.Если ни то, ни другое не применимо, вы также можете использовать shortValue - Short.MIN_VALUE
.
Имея функцию отображения, мы можем использовать
@Test
public void testIdsAreUnique() {
BitSet value = new BitSet(), duplicate = new BitSet();
Field[] fields = InterfaceWithIds.class.getDeclaredFields();
Arrays.stream(fields)
.filter(f -> f.getType() == short.class)
.mapToInt(f -> shortToIndex(fieldValue(f)))
.forEach(ix -> (value.get(ix)? duplicate: value).set(ix));
if(duplicate.isEmpty()) return; // no duplicates
throw new AssertionError(Arrays.stream(fields)
.filter(f -> duplicate.get(shortToIndex(fieldValue(f))))
.map(f -> f.getName()+"="+fieldValue(f))
.collect(Collectors.joining(", ", "fields with duplicate values: ", "")));
}
Сначала она заполняет набор битов для всех встречающихся значений и другой набор битов для тех, которые встречались более одного раза.Если последний набор битов пуст, мы можем немедленно вернуться, так как дубликатов нет.В противном случае мы можем использовать этот набор битов в качестве дешевого фильтра, чтобы получить поле с проблемными значениями.