Java Map, фильтр со свойствами значений
у меня есть
TreeMap resMap new TreeMap<String, Map<String, String>>();
Я хотел бы фильтровать и сохранять только записи, значения которых содержат известную пару, скажем ('mike' = > 'jordan'), и избегать цикла, как показано ниже
есть ли в моих включенных библиотеках apache.commons и google.общий метод фильтра (который, вероятно, тоже будет делать цикл, но, по крайней мере, он менее подробный
for (Entry<String, TreeMap<String, String>> el : resMap.entrySet()){
if (el.getValue().get("mike").equals("jordan")){
//
}
}
5 ответов
вы можете использовать фильтры из гуавы и Predicate
интерфейс.
Predicate<T> yourFilter = new Predicate<T>() {
public boolean apply(T o) {
// your filter
}
};
Итак, простой пример:
Predicate<Integer> evenFilter = new Predicate<Integer>() {
public boolean apply(Integer i) {
return (i % 2 == 0);
}
};
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
Map<Integer, Integer> evenMap = Maps.filterValues(map, evenFilter);
вместо того, чтобы заставить ваш клиентский код использовать фильтр / цикл, создайте то, что вам нужно в API вашего класса:
public class MyClass {
private TreeMap resMap new TreeMap<String, Map<String, String>>();
public void filter(String key, String value) {
// Some impl here. Either your loop or the guava approach
}
}
кстати, если вы используете свой цикл, подумайте об изменении на это:
for (Iterator<Map.Entry<String, TreeMap<String, String>>> i = resMap.entrySet().iterator(); i.hasNext();) {
Map.Entry<String, TreeMap<String, String>> entry = i.next();
if (value.equals(entry.getValue().get(key))) {
i.remove();
}
}
изменения в цикле:
- изменен порядок равных, чтобы избежать NPE
- используя
iterator
разрешить удаление записей непосредственно
даже если у вас нет класса, вы можете легко обернуть его в статическом метод в классе утилиты, где он также может быть легко параметризован для работы с любой вложенной картой:
public static <K1, K2, V> void filter(Map<K1, Map<K2, V>> map, K2 key, V value) {
// Some impl here
}
вот не-guava impl для статического метода:
for (Iterator<Map.Entry<K1, Map<K2, V>>> i = map.entrySet().iterator(); i.hasNext();) {
Map.Entry<K1, Map<K2, V>> entry = i.next();
if (value.equals(entry.getValue().get(key))) {
i.remove();
}
}
вот два примера. Оба печатают ключ на основе соответствия в свойствах значения.
private static void printMatchingEntriesUsingALoop(Map<String, Map<String, String>> resMap, String key, String value) {
for (Map.Entry<String, Map<String, String>> entry : resMap.entrySet())
if (value.equals(entry.getValue().get(key)))
System.out.println(entry.getKey());
}
private static void printMatchingEntriesUsingGuava(Map<String, Map<String, String>> resMap, final String key, final String value) {
Predicate<Map<String, String>> keyValueMatch =
new Predicate<Map<String, String>>() {
@Override
public boolean apply(@Nullable Map<String, String> stringStringMap) {
return value.equals(stringStringMap.get(key));
}
};
Maps.EntryTransformer<String, Map<String, String>, Void> printKeys =
new Maps.EntryTransformer<String, Map<String, String>, Void>() {
@Override
public Void transformEntry(@Nullable String s,
@Nullable Map<String, String> stringStringMap) {
System.out.println(s);
return null;
}
};
Maps.transformEntries(Maps.filterValues(resMap, keyValueMatch), printKeys);
}
public static void main(String... args) {
Map<String, Map<String, String>> resMap = new TreeMap<String, Map<String, String>>();
printMatchingEntriesUsingALoop(resMap, "first", "mike");
printMatchingEntriesUsingGuava(resMap, "first", "mike");
}
один использует цикл и один использует Guava.
в то время как первый работает лучше, вы должны действительно решить, что будет проще всего понять и поддерживать.
некоторые предложения от @missingfaktor. Вы должны использовать свое собственное суждение, но он хорошо осветил некоторые из вопросов.
- много дублирования кода.
- специальная обработка корпуса.
- более цикломатическая сложность.
- больше шансов на ошибку, в результате первых трех пуль.
- трудно следовать кодексу.
представьте, что вы новый разработчик, который должен поддерживать эту программу. С чем бы вы предпочли столкнуться?
вы можете фильтровать карту с помощью java 8 и потоков. Первым шагом в этом процессе является преобразование в поток с помощью entrySet().stream()
. Это дает вам Stream<Map.Entry<String, TreeMap<String, String>>
. Затем вы можете использовать filter(...)
для фильтрации списка. При фильтрации следует возвращать значение true, когда входящее значение должно быть включено в результат фильтра. После фильтрации результатов можно использовать foreach для выполнения цикла над конечным результатом.
конечный результат будет выглядеть следующим образом:
resMap.entrySet().stream()
.filter(e -> el.getValue().get("mike").equals("jordan"))
.foreach(e -> {
// Do something with your entry here
});