从散列映射

时间:2018-01-19 18:47:13

标签: java reflection compare

让我们说我们有人类:

class Person{
long id;
String username;
String password
String firstName;
String lastName;
String address;
String phoneNumber;

// setters and getters..
}

我们有一个散列图,其中包含person hashMap<String,Person>类型的对象,其中string是对象的用户名。

我想搜索具有指定属性(一个或多个)的对象,例如:

用户名:jim54和地址:英国

firstName:吉米姓:tornabone和地址:波兰

居住在英国的人(地址:英国)

没有写出大量的重载方法..

我的方法使用反射来查找单个属性的变换器:

 Method getter=getDeclaredMethodIgnoreCase(Person.class,"get"+"attribute"); 
 Method setter=getDeclaredMethodIgnoreCase(Person.class,"set"+"attribute");

然后比较和更改

1 个答案:

答案 0 :(得分:1)

首先,按用户名查找...

简单地说:

Map<String,Person> people = new HashMap<>();

Person person = people.get("jim54");

这将只返回一个用户,因为任何地图的本质是它只能为一个给定的密钥保留一个值。

现在,关于其他属性,除了遍历地图的值以找到匹配项之外别无选择。

这可以通过流来完成,例如:查找包含UK字符串的地址的所有人(忽略大小写):

List<Person> matching = people.values().stream()
    .filter(p -> p.getAddress().toLowerCase().contains("uk"))
    .collect(Collectors.toList());

例如查找名字为John(忽略大小写)且姓氏为tornabone(也忽略大小写)的所有人,地址包含Poland字符串(忽略大小写) :

List<Person> matching = people.values().stream()
    .filter(p -> "John".equalsIgnoreCase(p.getFirstName()))
    .filter(p -> "tornabone".equalsIgnoreCase(p.getLastName()))
    .filter(p -> p.getAddress().toLowerCase().contains("poland"))
    .collect(Collectors.toList());

如您所见,上面的两个搜索在结构上有所不同,因为应用的过滤器数量以及传递给Stream.filter方法的过滤条件会发生变化。

然而,你不需要反思来以一般方式解决这个问题,这将是一个糟糕的设计IMO。您只需要根据要应用的搜索条件创建谓词的通用方法。这是一个相当广泛的问题,所以我只在这里给出一个提示。

假设您在公共类中使用这些通用实用程序方法,例如: SearchUtils

public static <T, S> Predicate<T> extractThenFilter(
        Function<T, S> extractor, 
        Predicate<S> condition) {

    return t -> condition.test(extractor.apply(t));
}

public static <T> Predicate<T> and(Predicate<T>... conditions) {
    return Arrays.stream(conditions)
        .reduce(Predicate::and)
        .orElse(t -> true);
}

第一个方法接收一个从对象中提取属性然后根据此属性检查条件的函数。第二种方法只是通过将所有给定的谓词减少为单个谓词。

现在您可以使用第一种方法:

Predicate<Person> condition = SearchUtils.extractThenFilter(
        Person::getAddress, 
        address -> address.toLowerCase().contains("uk"));

List<Person> matching = people.values().stream()
    .filter(condition)
    .collect(Collectors.toList());

这相当于上面的第一次搜索。

以下相当于上面的第二次搜索:

Predicate<Person> condition = SearchUtils.and(
        SearchUtils.extractThenFilter(
            Person::getFirstName, 
            str -> "John".equalsIgnoreCase(str)),
        SearchUtils.extractThenFilter(
            Person::getLastName, 
            "tornabone"::equalsIgnoreCase), // just another way
        SearchUtils.extractThenFilter(
            Person::getAddress, 
            str -> str.toLowerCase().contains("poland")));

List<Person> matching = people.values().stream()
    .filter(condition)
    .collect(Collectors.toList());

如您所见,现在流式传输地图的值,对复合条件进行过滤并收集到列表,对于两个不同的搜索都以完全相同的方式完成。我们已经抽象出对一个谓词进行不同搜索的所有复杂性。

现在,如果您有一个匹配属性名称的映射属性提取器函数以及一些接收字符串并创建不同谓词的辅助方法(例如,检查给定字符串是否包含在另一个字符串中的另一个字符串,另一个一个检查忽略大小写相等的东西,然后你只需要解析搜索字符串就可以了。你好了。

编辑:

{8}接口是在java 8中引入的。它是一个功能接口,这意味着可以将lambda表达式和方法引用作为目标。

我在这里使用过,例如Person::getAddress作为将Person的实例转换为其地址的函数。在extractThenFilter实用程序方法中调用extractor.apply(t)时,会调用Person.getAddress方法,tPerson的实例类。然后,将来自该Person实例的提取地址传递给谓词(条件)。由于提取的地址类型为String,因此给定谓词接收此字符串并检查它是否满足条件(下面使用相同的示例,它检查提取的{{1}实例的地址class lowercase-contains Person)。

您可以查看java.util.function.Functionjava.util.function package's javadocs以获取进一步的参考。

相关问题