Оптимизируйте Nested, используя любую альтернативную DataStructure в Java - PullRequest
0 голосов
/ 20 февраля 2019

Как оптимизировать вложенный блок 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 (), потому что входящий запрос все равно должен быть успешным, если соответствующее поле фильтра для него недоступно.Это означает, что входящий запрос должен соответствовать всем значениям фильтра, но в случае, если у него нет связанного поля фильтра, он должен просто пройти.

public boolean checkMatchOrigen(){
    return (filter.getOrigen() == null || filter.getOrigen().isEmpty()) || 
    (incomingRequest.getOrigen() != null && 
    incomingRequest.getOrigen().trim().equals(filter.getOrigen()));
}

Ответы [ 2 ]

0 голосов
/ 20 февраля 2019

Во-первых, вы не должны использовать 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;
    }
}

Если какой-либо из этих фильтров отклоняет запрос, все готово.В противном случае перейдите к следующему полю.

Этот шаблон будет более эффективным, если имена фильтров сильно различаются.

0 голосов
/ 20 февраля 2019

Вы можете создать структуру, такую ​​как дерево решений или индекс базы данных .Это довольно сложная задача.

Например, у вас есть четыре фильтра:

  1. Имя n1, происхождение o1;
  2. Имя n1, происхождениеo2;
  3. Имя n2, источник o1;
  4. Имя n2, источник o5;

Одно из возможных деревьев решений:

or-->nameIs(n1)->and->or-->originIs(o1)
  |                     |->originIs(o2)
  |
  |->nameIs(n2)->and->or-->originIs(o1)
                        |->originIs(o5)

Идея состоит в том, чтобы проверять 'n1' только один раз, чтобы оба фильтра включали его и так далее.Как правило, фильтры Stronges должны быть проверены в первую очередь.Опять же, трудно предсказать, какой фильтр будет отклонять больше запросов.

Например, я построил дерево из вашей структуры данных:

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 Предикаты , которые могут быть представлены в виде дерева с операциями в качестве узлов.В этой реализации нет правильной обработки с нулевыми значениями, но ее легко добавить.

Обратите внимание, что мое дерево статично и его необходимо перестраивать после каждого изменения списка фильтров.И это не сбалансировано.Так что это не решение, а пример.

Если вам нужно фильтровать только по критерию равенства, вы можете создать карту для каждого поля.Опять та же идея группировки при проверке.В этом случае вы можете динамически перестраивать поисковые карты:

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")));
    }
}

Также вы можете создать пользовательскую древовидную структуру данных с динамической перестройкой и перебалансировкой.Но может быть лучше использовать базу данных или поисковик?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...