Java Map,使用值属性过滤
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11160382/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java Map, filter with values properties
提问by Ian Bishop
I have a
我有一个
TreeMap resMap new TreeMap<String, Map<String, String>>();
I would like to filter and keep only entries that values contains a known pair, let's say ('mike' => 'jordan'), and avoid a loop like below
我想过滤并只保留值包含已知对的条目,比方说 ('mike' => 'jordan'),并避免像下面这样的循环
Is there in my included libraries apache.commons and google.common a filter method (that probably would do a loop too, but at least it's less verbose
在我包含的库 apache.commons 和 google.common 中是否有过滤器方法(这也可能会执行循环,但至少它不那么冗长
for (Entry<String, TreeMap<String, String>> el : resMap.entrySet()){
if (el.getValue().get("mike").equals("jordan")){
//
}
}
回答by Ian Bishop
You can use filters from Guava and the Predicate
interface.
您可以使用来自 Guava 和Predicate
界面的过滤器。
Predicate<T> yourFilter = new Predicate<T>() {
public boolean apply(T o) {
// your filter
}
};
So, simple example would be:
所以,简单的例子是:
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);
回答by Bohemian
Rather than force your client code to use a filter/loop, build what you need into the API of your class:
不要强制您的客户端代码使用过滤器/循环,而是将您需要的内容构建到您的类的 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
}
}
BTW, if you use your loop, consider changing to this:
顺便说一句,如果您使用循环,请考虑更改为:
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();
}
}
The changes to the loop are:
循环的变化是:
- Changed order of equals to avoid NPE
- Using
iterator
to allow removal of entries directly
- 更改等号的顺序以避免 NPE
- 使用
iterator
直接允许移除项
Even if you don't have a class, you could easily wrap it up in a static method on a utility class, where it could also easily be parameterized to work with any nested map:
即使您没有类,您也可以轻松地将其包装在实用程序类的静态方法中,也可以轻松地对其进行参数化以与任何嵌套映射一起使用:
public static <K1, K2, V> void filter(Map<K1, Map<K2, V>> map, K2 key, V value) {
// Some impl here
}
Here's a non-guava impl for the static method:
这是静态方法的非番石榴实现:
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();
}
}
回答by Peter Lawrey
Here are two examples. The both print the key based on match in the value's properties.
这里有两个例子。两者都根据值的属性中的匹配来打印键。
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");
}
One uses a loop and one use Guava.
一种使用循环,一种使用番石榴。
While the first one performs better, you should really decide which will be the easiest to understand and maintain.
虽然第一个表现更好,但您应该真正决定哪个最容易理解和维护。
Some suggestions from @missingfaktor. You have to use your own judgement, but he highlighted some of the issues well.
来自@missingfaktor 的一些建议。你必须使用你自己的判断,但他很好地强调了一些问题。
- a lot of code duplication.
- special case handling.
- More cyclomatic complexity.
- More chances of error, as a result of first three bullets.
- Hard to follow code.
- 很多代码重复。
- 特殊情况处理。
- 更多的圈复杂度。
- 由于前三颗子弹,出错的机会更多。
- 很难遵循代码。
Imagine you are a new developer who has to support this software. Which would you rather be facing?
想象一下,您是一名必须支持该软件的新开发人员。你更愿意面对哪一个?
回答by Ferrybig
You can filter the map using java 8 and streams. The first step in this process is converting to a stream using entrySet().stream()
. This gives you a Stream<Map.Entry<String, TreeMap<String, String>>
. You can then use filter(...)
to filter the list. When you filter, you should return true when the incoming value should be included in the filter result. After you filtered the results, you can use foreach to loop over the final result.
您可以使用 java 8 和流过滤地图。此过程的第一步是使用entrySet().stream()
. 这给你一个Stream<Map.Entry<String, TreeMap<String, String>>
. 然后您可以使用filter(...)
过滤列表。过滤时,当传入值应包含在过滤结果中时,您应该返回 true。过滤结果后,您可以使用 foreach 循环遍历最终结果。
The final result will look like the following:
最终结果将如下所示:
resMap.entrySet().stream()
.filter(e -> el.getValue().get("mike").equals("jordan"))
.foreach(e -> {
// Do something with your entry here
});