Java 8比较同一列表中的对象(数组索引超出范围)

时间:2018-07-22 04:17:55

标签: java java-8 java-stream

我有一个哈希表,其键值为Map<String,List<Trade>>。在值对象中,即List<Trade>,我必须比较每个对象。如果对象的任何两个属性名称“ TradeType”相同,则必须从列表中删除这两个属性。我正在努力实现以下目标。但是我得到了“数组索引超出限制的异常”,还有没有更好的方法来使用流比较列表中的同一对象?

Map<String,List<Trade>> tradeMap = new HashMap<>();
// Insert some data here
tradeMap.entrySet()
        .stream()
        .forEach(tradeList -> {
             List<Trade> tl = tradeList.getValue();
             String et = "";
         // Is there a better way to refactor this? 
             for(int i = 0;i <= tl.size();i++){
                   if(i == 0) {
                       et = tl.get(0).getTradeType();
                   }
                   else {
                       if(et.equals(tl.get(i).getTradeType())){
                           tl.remove(i);
                       }
                   }
              }
        });

4 个答案:

答案 0 :(得分:2)

您的描述与您的代码并不完全同步,因此我将提供一些解决方案,您可以选择其中的一种。

首先IndexOutOfBoundsException可以通过将循环条件从i <= tl.size()更改为i < tl.size()来解决。这是因为列表基于tl.size() - 1,因此列表中的最后一项位于索引0

要改善您当前的尝试,您可以按照以下步骤进行操作:

tradeMap.values()
        .stream()
        .filter(list -> list.size() > 1)
        .forEach(T::accept);

其中accept定义为:

private static void accept(List<Trade> list) {
      String et = list.get(0).getTradeType();
      list.subList(1, list.size()).removeIf(e -> e.getTradeType().equals(et));
}

T应该替换为包含accept方法的类。

上面的代码段仅按交易类型删除第一个元素之后的对象,该对象等于第一个元素,这就是您的示例代码段所尝试的操作。但是,如果您要区分所有对象,则一种选择是如下重写equals类中的hashcodeTrade

@Override
public boolean equals(Object o) {
     if (this == o) return true;
     if (o == null || getClass() != o.getClass()) return false;
     Trade trade = (Trade) o;
     return Objects.equals(tradeType, trade.tradeType);
}

@Override
public int hashCode() {
    return Objects.hash(tradeType);
}

然后accept方法需要修改为:

private static void accept(List<Trade> list) {
        List<Trade> distinct = list.stream()
                .distinct()
                .collect(Collectors.toList());

        list.clear(); // clear list
        list.addAll(distinct);  // repopulate with distinct objects by tradeType
}

或者如果您根本不想覆盖均值和哈希码,则可以使用toMap收集器来获取不同的对象:

private static void accept(List<Trade> list) {
        Collection<Trade> distinct = list.stream()
                .collect(Collectors.toMap(Trade::getTradeType,
                        Function.identity(), (l, r) -> l, LinkedHashMap::new))
                .values();

        list.clear(); // clear list
        list.addAll(distinct);  // repopulate with distinct objects by tradeType    
}

但是,如果您说:

  

“如果对象的属性名称“ TradeType”中的任何两个相同,则我   必须从列表中删除这两个。”

您实际上想通过Trade删除所有相等的tradeType对象,这些对象具有2次或多次出现,然后将accept方法修改如下:

private static void accept(List<Trade> list) {
       list.stream()
           .collect(Collectors.groupingBy(Trade::getTradeType))
           .values()
           .stream()
           .filter(l -> l.size() > 1)
           .map(l -> l.get(0))
           .forEach(t -> list.removeIf(trade -> trade.getTradeType().equals(t.getTradeType())));
}

答案 1 :(得分:1)

Map<String,List<Trade>> tradeMap = new HashMap<>();
tradeMap.put("1",Arrays.asList(new Trade("A"),new Trade("B"),new Trade("A")));
tradeMap.put("2",Arrays.asList(new Trade("C"),new Trade("C"),new Trade("D")));

Map<String,Collection<Trade>> tradeMapNew = tradeMap.entrySet()
    .stream()
    .collect(Collectors.toMap(Entry::getKey,
                        e -> e.getValue().stream()                         //This is to remove the duplicates from the list.
                            .collect(Collectors.toMap(Trade::getTradeType,
                                        t->t,
                                        (t1,t2) -> t1,
                                        LinkedHashMap::new))
                            .values()));

输出:

{1=[A, B], 2=[C, D]}

答案 2 :(得分:1)

public void test(){
    Map<String, List<Trade>> data = new HashMap<>();
    List<Trade> list1 = Arrays.asList(new Trade("1"), new Trade("2"), new Trade("1"), new Trade("3"), new Trade("3"), new Trade("4"));
    List<Trade> list2 = Arrays.asList(new Trade("1"), new Trade("2"), new Trade("2"), new Trade("3"), new Trade("3"), new Trade("4"));

    data.put("a", list1);
    data.put("b", list2);

    Map<String, List<Trade>> resultMap = data.entrySet()
                                             .stream()
                                             .collect(Collectors.toMap(Entry::getKey, this::filterDuplicateListObjects));

    resultMap.forEach((key, value) -> System.out.println(key + ": " + value));
    // Don't forget to override the toString() method of Trade class.
}

public List<Trade> filterDuplicateListObjects(Entry<String, List<Trade>> entry){
    return entry.getValue()
                .stream()
                .filter(trade -> isDuplicate(trade, entry.getValue()))
                .collect(Collectors.toList());
}

public boolean isDuplicate(Trade trade, List<Trade> tradeList){
    return tradeList.stream()
            .filter(t -> !t.equals(trade))
            .noneMatch(t -> t.getTradeType().equals(trade.getTradeType()));
}

答案 3 :(得分:0)

Map<String, List<Trade>> tradeMap = new HashMap<>();

tradeMap.values()
    .stream()
    .forEach(trades -> trades.stream()
        .collect(Collectors.groupingBy(Trade::getType))
        .values()
        .stream()
        .filter(tradesByType -> tradesByType.size() > 1)
        .flatMap(Collection::stream)
        .forEach(trades::remove));