【问题标题】:Faster way for removing elements in nested loops删除嵌套循环中元素的更快方法
【发布时间】:2016-06-23 03:17:49
【问题描述】:

我有一个列表和一个集合:

List<Filter> filters

Collection<Vehicle> vehicles

我想根据某些条件从第一个列表中删除一些元素。但我的解决方案很慢。 你能推荐一个更快的方法吗? 这是我的解决方案:

final Iterator<Filter> iterator = filters.iterator();
        while (iterator.hasNext()) {
            final Filter filter = iterator.next();
            for (final Vehicle vehicle : vehicles) {
                if (filter.getX().equalsIgnoreCase(vehicle.getX())
                        && filter.getY().equalsIgnoreCase(vehicle.getY())
                        && filter.getZ().equalsIgnoreCase(vehicle.getZ())) {
                    iterator.remove();
                }
            }
        }

【问题讨论】:

  • 也许..添加一个具有 xyz 复合值的 id 字段并比较它而不是 xyz 单独?或者使用地图来标识要单独移除的元素
  • Java 8 流的 API 可能更高效
  • 您的方法的时间复杂度为 O(MN),太糟糕了。 1.尝试使用HashMap。你需要额外的空间,但你可以在 O(N+M) 中完成你的工作。如果您限制额外的空间使用。只需对两个集合进行排序,然后使用 2 个指针,如果您的车辆是linkedList(不是arrayList),您可能具有(MlogM)时间复杂度。
  • 车辆的 x、y 和 z 有哪些类型?每个值可以有什么范围?
  • @Bohemian:x、y 和 z 是字符串。

标签: java


【解决方案1】:

您的问题是算法的时间复杂度。随着您的实施,对于每个Filter,您检查每个Vehicle 的坐标。如果有n 过滤器和m 车辆,您的代码将执行n * m 操作。我们说这是O(n * m)(表示为“order n × m”)。

当元素的数量增加时,完成所需的时间会很快耗尽。

为了使您的代码更快,您需要一个O(n) 算法。最好的方法是使用Set 来保存所有车辆“钥匙”,这是 x、y 和 z 的唯一组合。

为清楚起见,从构建密钥的方法开始:

private String key(String x, String y, String z) {
    return String.join("|", x, y, z); // choose separator char not found in values
}

构建一组键,如果在集合中找到它们的键,则删除过滤器:

Set<String> keys = vehicles.stream()
    .map(v -> key(v.getX(), v.getY(), v.getZ())
    .collect(Collectors.toSet());
filters.removeIf(f -> set.contains(key(f.getX(), f.getY(), f.getZ())));

由于每个集合只迭代一次,而HashSetcontains()方法为O(1),所以现在整体时间复杂度为O(n + m),尽可能快。

【讨论】:

    【解决方案2】:

    当 if 测试产生 true 并且过滤器被删除时,它的命运就被密封了,没有更多的故事,所以你可以在 remove() 行之后添加一个 break;,这样你就可以跳转到检查下一个过滤器了离开。

    另外,使用代码的内部循环来查看当前过滤器是否匹配任何汽车需要线性时间,这个时间将乘以过滤器的数量,即n*n 复杂度(或执行时间,更直)。幸运的是,我们有一个名为HashMap 的秘密武器,它可以帮助您在恒定时间内查找事物,因此通过使用HashMap 替换内部循环,我们可以在此处实现n 复杂性,代价是建立一个HashMap,这为我们的复杂性增加了另一个n,但仍然比n*n好。你可以自己搜索HashMaps的内部机制。

    final Map<String, Vehicle> map = new HashMap<String, Vehicle>();
    for (final Vehicle vehicle : vehicles) {
        //suppose the x,y,z values don't contain '|' character, or this could lead to mistaken removals
        map.put(vehicle.getX()+"|"+vehicle.getY()+"|"+vehicle.getZ(), vehicle);
    }
    final Iterator<Filter> iterator = filters.iterator();
    while (iterator.hasNext()) {
        final Filter filter = iterator.next();
        if(map.containsKey(filter.getX()+"|"+filter.getY()+"|"+filter.getZ())) {
            iterator.remove();
        }
    }
    

    另外,我们放入mapvehicles(这是mapvalues)并没有真正使用,所以我们实际上可以切换到HashSet 来制作我们的代码更优雅一点(但这样做的性能并不高)。

    【讨论】:

      【解决方案3】:

      试试这个,但你必须使用 java 8 流

       final Set<Filter> collected = vehicles.stream().map(vehicle -> vehicle).
                  flatMap(vehicle -> filters.stream().filter(filter -> filter.getX().equalsIgnoreCase(vehicle.getX()) && filter.getY().equalsIgnoreCase(vehicle.getY()) &&
                                          filter.getZ().equalsIgnoreCase(vehicle.getZ())
                          )
                  ).
                  collect(Collectors.toSet());
      

      ADAM BIEN'S WEBLOG 中已经很好地解释了

      【讨论】:

        【解决方案4】:

        使用 for each 循环而不是 Iterator 并像这样重构您的代码

            List<Filter> removeFilterList = new ArrayList<Filter>();
        
            for(Filter filter : filters) {
                for(Vehicle vehicle : vehicles) {
                    if(isEqualXYZ(filter, vehicle)) {
                        removeFilterList.add(filter);
                    }
                }
            }
        
            filters.removeAll(removeFilterList);
        
           public boolean isEqualXYZ(Filter filter, Vehicle vehicle) {
                if(filter.getX().equalsIgnoreCase(vehicle.getX())) {//Put your condition here
                    return true;
                }
                return false;
            }
        

        我个人不推荐 Java 8 stream 而不是 for each 循环。 For each loop 比 Java 8 Stream 更快。

        【讨论】:

          猜你喜欢
          • 2014-05-03
          • 1970-01-01
          • 2010-12-07
          • 1970-01-01
          • 1970-01-01
          • 2022-01-07
          • 2015-06-29
          • 2021-11-18
          • 1970-01-01
          相关资源
          最近更新 更多