如何优化nested-if块以进行快速比较。下面是我的代码,其中比较了两个不同的Java对象。我有一个成员变量,它的模式也位于if块之一中。
listOfFilters是Map<String, List<Filter>>
的子集。下面的方法使用下面的签名调用。此列表可以多达400〜1000。
checkRequest(incomingRequest,map.get(incomingRequest.getFiltersForThis()))
问题-
public boolean checkRequest(Request incomingRequest, List<Filter> listOfFilters){
for(Filter filter : listOfFilters){
if(incomingRequest.getName() == filter.getName()){
if(incomingRequest.getOrigen() == filter.getOrigen()){
.....
.....
.....
filterMatched = true;
}
}
}
}
}
}
我需要将上述传入请求与系统中可用的每个过滤器进行比较。 O(n)是复杂度。
有什么方法可以使用数据结构将复杂度从O(n)降低到O(log n)。
当系统中配置的过滤器数量更多时,性能就会达到。
我不能使用hashcode()或equals(),因为如果相应的过滤器字段不可用,incomingRequest应该仍然成功。这意味着commitingRequest应该匹配所有过滤器值,但是,如果没有相关的过滤器字段,则应该通过。
public boolean checkMatchOrigen(){
return (filter.getOrigen() == null || filter.getOrigen().isEmpty()) ||
(incomingRequest.getOrigen() != null &&
incomingRequest.getOrigen().trim().equals(filter.getOrigen()));
}
答案 0 :(得分:1)
您可以创建一个decision tree或database index之类的结构。有一个相当复杂的任务。
例如,您有四个过滤器:
可能的决策树之一是:
or-->nameIs(n1)->and->or-->originIs(o1)
| |->originIs(o2)
|
|->nameIs(n2)->and->or-->originIs(o1)
|->originIs(o5)
这个想法是对包含它的两个过滤器仅检查一次“ n1”,依此类推。通常,必须首先检查优势过滤器。同样,很难预测哪个过滤器会拒绝更多请求。
例如,我已经根据您的数据结构构建了树:
public class DemoApplication {
// Group filter list by names, except nulls
public static Map<String, List<Filter>> mapNameToFilter(List<Filter> filters) {
return filters
.stream()
.filter(filter -> filter.getName() != null)
.collect(groupingBy(Filter::getName));
}
// Create predicate to check name and all chunked origins for all entries
public static Predicate<Request> createPredicateByNameAndOrigin(Map<String, List<Filter>> nameToFilterMap) {
return nameToFilterMap
.keySet()
.stream()
.map(name -> {
final Predicate<Request> filterByName = request -> name.equals(request.getName());
final Map<String, List<Filter>> originToFilterMap = mapOriginToFilter(nameToFilterMap.get(name));
return filterByName.and(createPredicateByOrigin(originToFilterMap));
})
.reduce(Predicate::or)
.orElse(filter -> true);
}
// Group filter list by origins, except nulls
public static Map<String, List<Filter>> mapOriginToFilter(List<Filter> filters) {
return filters
.stream()
.filter(filter -> filter.getOrigin() != null)
.collect(groupingBy(Filter::getOrigin));
}
// Create predicate to check origin for all entries
public static Predicate<Request> createPredicateByOrigin(Map<String, List<Filter>> originToFilterMap) {
return originToFilterMap
.keySet()
.stream()
.map(origin -> {
final Predicate<Request> filterByOrigin = request -> origin.equals(request.getOrigin());
return filterByOrigin; // Or go deeper to create more complex predicate
})
.reduce(Predicate::or)
.orElse(filter -> true);
}
public static void main(String[] args) {
List<Filter> list = new ArrayList<>();
list.add(new Filter("n1", "o1"));
list.add(new Filter("n1", "o2"));
list.add(new Filter("n2", "o1"));
list.add(new Filter("n2", "o5"));
list.add(new Filter(null, "o10"));
list.add(new Filter(null, "o20"));
Predicate<Request> p = createPredicateByNameAndOrigin(mapNameToFilter(list));
System.out.println(p.test(new RequestImpl("n1", "2")));
System.out.println(p.test(new RequestImpl("n1", "1")));
System.out.println(p.test(new RequestImpl("n2", "1")));
System.out.println(p.test(new RequestImpl("n10", "3")));
}
}
我使用了JDK Predicates,它可以表示为以操作为节点的树。在这种实现中,没有使用空值的正确处理,但是可以很容易地添加它。
请注意,我的树是静态的,每次更改过滤器列表后都需要重建。而且它不平衡。因此,这不是解决方案,仅是示例。
如果只需要按相等条件进行过滤,则可以为每个字段创建地图。同样,检查时的分组思路相同。在这种情况下,您可以动态重建搜索地图:
public class DemoApplication {
public static List<Filter> filters = new ArrayList<>();
public static Map<String, Set<Filter>> nameToFiltersMap = new HashMap<>();
public static Map<String, Set<Filter>> originToFiltersMap = new HashMap<>();
public static void addFilter(Filter filter) {
filters.add(filter);
// Rebuild name index
Set<Filter> nameFilters = nameToFiltersMap.getOrDefault(filter.getName(), new HashSet<>());
nameFilters.add(filter);
nameToFiltersMap.put(filter.getName(), nameFilters);
// Rebuild origin index
Set<Filter> originFilters = originToFiltersMap.getOrDefault(filter.getOrigin(), new HashSet<>());
originFilters.add(filter);
originToFiltersMap.put(filter.getOrigin(), originFilters);
}
public static boolean test(Request request) {
// Get all filters matched by name
Set<Filter> nameFilters = nameToFiltersMap.get(request.getName());
if (nameFilters != null) {
// Get all filters matched by origin
Set<Filter> originFilters = originToFiltersMap.get(request.getOrigin());
for (Filter nameFilter: nameFilters) {
if (originFilters != null && originFilters.contains(nameFilter)) {
return true; //filter matches
}
}
}
return false;
}
public static void main(String[] args){
addFilter(new Filter("n1", "o1"));
addFilter(new Filter("n1", "o2"));
addFilter(new Filter("n2", "o1"));
addFilter(new Filter("n2", "o5"));
addFilter(new Filter(null, "o7"));
addFilter(new Filter(null, "o8"));
System.out.println(test(new RequestImpl(null, "o7")));
System.out.println(test(new RequestImpl(null, "o9")));
System.out.println(test(new RequestImpl("n1", "o1")));
System.out.println(test(new RequestImpl("n1", "o3")));
System.out.println(test(new RequestImpl("n2", "o5")));
System.out.println(test(new RequestImpl("n3", "o3")));
}
}
此外,您还可以创建具有动态重建和重新平衡功能的自定义树数据结构。但是使用数据库或搜索引擎可能更好?
答案 1 :(得分:0)
首先,您不应使用Object
作为请求的类型。至少对于这个问题,请使用具有适当方法的接口,以便您的代码有机会进行编译。
interface Request { ... }
然后,如果您有很多过滤器,则可以按名称对这些过滤器进行分组。
Map<String, List<Filter>> filtersByName = ...;
之后,您的过滤代码将变为:
String reqName = blankToNull(request.getName());
if (reqName != null) {
List<Filter> nameFilters = filtersByName.get(reqName);
if (anyFilterMatches(nameFilters, request)) {
return Decision.REJECT;
}
}
如果这些过滤器中的任何一个拒绝了请求,那么您就完成了。否则,请继续下一个字段。
如果过滤器的名称相差很大,此模式将更有效。