使用另一个ArrayList动态过滤ArrayList

时间:2019-03-20 20:27:27

标签: filter java-8

我有一些如下所示的搜索结果对象:

public class TradeSearchResult{ 

    private String tradeRefNo;
    private String relatedTradeId;
    private String custodyDate;
    private String orderNumber;
    private String odrQty;
    private String price : 500;
}

public class CollateralTradesSearchResult{

    private String excludeTradeOUT;
    private String settlementStatus;
    private String fundId;
    private String altFundId;
    private String apNumber;
    private String collateralOrderNumber;
    private String componenetIdentifier;
}

现在我有一个搜索过滤条件对象

public class CRITERION {  

    protected String field; //The field denotes any field name of the either 
                              SearchResult object
    protected String operator; its will be EQUALS or NOT_EQUALS   
    protected String value; Value of the field.
}

现在,我需要编写一个动态过滤器方法,在该方法中,我将传递“条件”对象的列表,并可以传递如下所示的任一SearchResult的列表

public static List<Object> applyFilter(List<CRITERION> comp, List<?> objectList){

    //The CRITERION.fiedName can be same in more than one in the list 
    return filteredList;

}

这是一个示例:考虑下面的

列表
TradeSearchResult{
 tradeRefNo : W12343;
 relatedTradeId: N993093;
 custodyDate : 2018-12-14;
 orderNumber : 0000342343;
 String odrQty : 12;
 String price : 500;
},
{
 tradeRefNo : W12344;
 relatedTradeId: N993093;
 custodyDate : 2018-12-14;
 orderNumber : 0000342344;
 String odrQty : 18;
 String price : 600;

},
{
  tradeRefNo : W12345;
 relatedTradeId: N993094;
 custodyDate : 2018-12-14;
 orderNumber : 0000342345;
 String odrQty : 20;
 String price : 700;
}

Now the Criterion class is like 
CRITERION{

    field :relatedTradeId; 
    operator : EQUALS;    
    value :N993093;
}
{    
    field :relatedTradeId; 
    operator : EQUALS;    
    value :N993094;    
}
{    
    field :orderNumber ; 
    operator : EQUALS;    
    value :0000342344;

}

即使relatedTradeId有两个过滤器,它也只会返回一个结果

TradeSearchResult{
 tradeRefNo : W12344;
 relatedTradeId: N993093;
 custodyDate : 2018-12-14;
 orderNumber : 0000342344;
 String odrQty : 18;
 String price : 600;
}

现在,在相同的applyFIlter方法中,我可以发送Criterion列表和Collat​​eralTradesSearchResult列表,并返回过滤后的结果。 这是我尝试过的东西

public static List<Object> applyFilter(List<CRITERION> criList, List<?> objectList){    
    long startTime = Calendar.getInstance().getTimeInMillis();          
        Set<Object> objectSet = new HashSet<>();    

        for(CRITERION cri : criList){
        String fieldName = cri.getFIELD();
        objectList.stream().filter(p->beanProperties(p).get(fieldName).equals(cri.getVALUE())).forEachOrdered(objectSet::add);
        //objectList.retainAll(objectSet);
        //objectSet.clear();
        }

        List<Object> ret =  new ArrayList<>(objectSet);
        long endTime = Calendar.getInstance().getTimeInMillis();
        System.out.println("Size"+ ret.size());
        System.out.println("Time Taken to Search"+ String.valueOf(endTime-startTime));
    return ret;
    }

这是beanProperties()方法

public static Map<String, Object> beanProperties(Object bean) {
        try {
            Map<String, Object> map = new HashMap<>();
            Arrays.asList(Introspector.getBeanInfo(bean.getClass(), Object.class)
                                      .getPropertyDescriptors())
                  .stream()
                  // filter out properties with setters only
                  .filter(pd -> Objects.nonNull(pd.getReadMethod()))
                  .forEach(pd -> { // invoke method to get value
                      try {
                          Object value = pd.getReadMethod().invoke(bean);
                          if (value != null) {
                              map.put(pd.getName(), value);
                          }
                      } catch (Exception e) {
                          // add proper error handling here
                      }
                  });
            return map;
        } catch (IntrospectionException e) {
            // and here, too
            return Collections.emptyMap();
        }
    }
Any help using Stream or by any means will be helpful.

1 个答案:

答案 0 :(得分:0)

创建一个接口SearchResult,并让您的两个类都实现它。然后创建此类:

public class Filter<T extends SearchResult> {

    public List<T> applyFilter(List<Criterion> criteria, List<T> list) {
        Map<String, Set<String>> allowedValues = new HashMap<>();
        Map<String, Set<String>> prohibitedValues = new HashMap<>();
        populateValues(criteria, "EQUALS", allowedValues);
        populateValues(criteria, "NOT_EQUALS", prohibitedValues);

        prohibitedValues.forEach((k, v) -> list.removeIf(t -> v.contains(getFieldValue(k, t))));
        allowedValues.forEach((k, v) -> list.removeIf(t -> !v.contains(getFieldValue(k, t))));
        return list;
    }

    private static void populateValues(List<Criterion> criteria, String operator, Map<String, Set<String>> values) {
        criteria.stream()
                .filter(c -> c.getOperator().equals(operator))
                .forEach(c -> {
                    values.merge(c.getField(), Stream.of(c.getValue()).collect(Collectors.toSet()),
                            (set1, set2) -> Stream.concat(set1.stream(), set2.stream()).collect(Collectors.toSet()));
                });
    }

    private String getFieldValue(String fieldName, T object) {
        Field field;
        try {
            field = object.getClass().getDeclaredField(fieldName);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
            return null;
        }
        field.setAccessible(true);
        try {
            return (String) field.get(object);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            return null;
        }
    }
}

并将其用作:

Filter<TradeSearchResult> filter = new Filter<>(); //or CollateralTradesSearchResult
List<TradeSearchResult> filteredList = filter.applyFilter(criteria, searchResults);