3

I need to filter a list with a large amount of objects. For each of these objects I need to verify if any of the parameters contain one of the words that are in other lists. I have developed a method to do this, but it is taking too long, I would like to know if there is a more efficient way to do this.

The main idea could be written in sql for better understanding:

SELECT * FROM PROJECT P WHERE P.NAME LIKE "%JAVA%" OR P.NAME LIKE "%PASCAL%" OR P.PRODUCT LIKE "%JAVA%" OR P.PRODUCT LIKE "%PASCAL% OR. P.ADDRESS LIKE "%JAVA" OR P.ADDRESS LIKE "%PASCAL%";

In Java I wrote in this way:

private List<Projeto> filtraResultado(List<Projeto> projetosAssinados, String[] filtros){
List<Projeto> result = new ArrayList<Projeto>();
for(Projeto p: projetosAssinados) {
    if(existeFiltroBuscadoNosCamposDePesquisa(p.getDsProjeto(), filtros) ||
       existeFiltroBuscadoNosCamposDePesquisa(p.getNomeProjeto(), filtros) ||
       existeFiltroBuscadoNosCamposDePesquisa(p.getSetor(),filtros) ||
       existeFiltroBuscadoNosCamposDePesquisa(p.getUn(), filtros) ||
       existeFiltroBuscadoNosCamposDePesquisa(p.getProcessosModelados(),filtros)||
       existeFiltroBuscadoNosCamposDePesquisa(p.getServicosPrestados(),filtros) ||
       existeFiltroBuscadoNosCamposDePesquisa(p.getTecnologias(),filtros)||
       existeFiltroBuscadoNosCamposDePesquisa(p.getDetalhamento(),filtros)) {
            result.add(p);
    }

}
return result;

}

public boolean existeFiltroBuscadoNosCamposDePesquisa(String campoPesquisado,String[] filtros ){
if(campoPesquisado == null) {
    return false;
}
for(String f: filtros) {
    if(StringUtils.containsIgnoreCase(campoPesquisado, f.trim())) {
        return true;
    }
}
return false;

}

2
  • Your main problem as I see it is that your filters (?), filtros, is applicable for all those variables instead of one group of filter per variable. Now you are comparing everything to everything so to speak. Maybe you could re-design this part somehow? Commented Aug 14, 2019 at 20:29
  • Exactly, I have a list of objects and each of these objects have several attributes. And I have a list of strings. I need to check if at least one of these strings is in at least one of these attributes. Commented Aug 14, 2019 at 20:35

2 Answers 2

4

For comparing two List you'd need anyMatch() and stream between two lists. But if you are checking against a filter I assume each records on that list are different and this can easily be converted to a use Set which will reduce time and be lot faster.

For example:

List< String > filterList = Arrays.asList( "z" );
List< String > toCheckList = Arrays.asList( "z", "b", "a", "q", "w" );

boolean contains = toCheckList.stream( ).anyMatch( list -> filterList.stream( ).anyMatch( filter -> StringUtils.containsIgnoreCase( filter, list ) ) );

    if ( contains ) {
        System.out.println( "Contains" );
        //your logic
    }

If your filter is a Set:

Set< String > filterList = Arrays.asList( "z", "b" ).stream( ).collect( Collectors.toSet( ) );
List< String > toCheckList = Arrays.asList( "z", "b", "a", "q", "w" );
boolean contains = toCheckList.stream( ).anyMatch( list -> filterList.contains( list ) );

    if ( contains ) {
        System.out.println( "Contains" );

        //your logic
    }

Give it a thought if your filter List can be changed to Set on your code.

Sign up to request clarification or add additional context in comments.

Comments

4

The time complexity of your method can be improved by creating a Set<String> that contains the filter elements. This way, searching for a specific element will be O(1) instead of O(n):

private List<Projeto> filtraResultado(List<Projeto> projetosAssinados, String[] filtros) {
    Set<String> filterSet = Arrays.stream(filtros)
            .map(String::toLowerCase)
            .map(String::trim)
            .collect(Collectors.toSet());

    List<Projeto> result = new ArrayList<Projeto>();

    for (Projeto p : projetosAssinados) {
        boolean contained = Stream.of(p.getDsProjeto(), p.getNomeProjeto(),
                p.getSetor(), p.getUn(), p.getProcessosModelados(), 
                p.getServicosPrestados(), p.getTecnologias(), p.getDetalhamento())
            .filter(Objects::nonNull)
            .map(String::toLowerCase)
            .anyMatch(filterSet::contains);

        if (contained) {
            result.add(p);
        }
    }

    return result;
}

This should be significantly faster than your method for large inputs.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.