迭代两个列表并提取一些内容的最佳方法是什么?

时间:2016-11-24 18:46:27

标签: java list arraylist

我有两个课程,如下所示。我需要使用这两个类来提取一些东西。

public final class ProcessMetadata {
  private final String clientId;
  private final String deviceId;
  // .. lot of other fields here

  // getters here
}

public final class ProcMetadata {
  private final String deviceId;
  private final Schema schema;
  // .. lot of other fields here
}

现在我有以下代码,我在两个类之上迭代,并在给定schema的情况下提取clientId

public Optional<Schema> getSchema(final String clientId) {
  for (ProcessMetadata metadata1 : processMetadataList) {
    if (metadata1.getClientId().equalsIgnoreCase(clientId)) {
      String deviceId = metadata1.getDeviceId();
      for (ProcMetadata metadata2 : procMetadataList) {
        if (metadata2.getDeviceId().equalsIgnoreCase(deviceId)) {
          return Optional.of(metadata2.getSchema());
        }
      }
    }
  }
  return Optional.absent();
}

有没有更好的方法来获得我需要的东西,通过迭代上面的两个类而不是我的东西?我使用的是Java 7。

2 个答案:

答案 0 :(得分:6)

您正在进行quadratic *搜索操作,这是不合适的。您可以通过首先从每个列表的id-&gt;对象创建mapping(在线性时间内)来在恒定时间内执行此操作。这看起来像这样:

// do this once, in the constructor or wherever you create these lists
// even better discard the lists and use the mappings everywhere
Map<String, ProcessMetadata> processMetadataByClientId = new HashMap<>();
for (ProcessMetadata process : processMetadataList) {
  processMetadataByClientId.put(process.getClientId(), process);
}

Map<String, ProcMetadata> procMetadataByDeviceId = new HashMap<>();
for (ProcMetadata metadata2 : procMetadataList) {
  procMetadataByDeviceId.put(proc.getDeviceId(), proc);
}

然后你的查询就变成了:

public Optional<Schema> getSchema(String clientId) {
  ProcessMetadata process = processMetadataByClientId.get(clientId);
  if (process != null) {
    ProcMetadata proc = procMetadataByDeviceId.get(process.getDeviceId());
    if (proc != null) {
      return Optional.of(proc.getSchema());
    }
  }
  return Optional.absent();
}

在Java 8中,您可以这样写:

public Optional<Schema> getSchema(String clientId) {
 return Optional.fromNullable(processMetadataByClientId.get(clientId))
     .map(p -> procMetadataByDeviceId.get(p.getDeviceId()))
     .map(p -> p.getSchema());
}

*实际上,假设客户端ID是唯一的,您的算法是线性的,但它在技术上仍然是O(n ^ 2),因为您可能会触摸进程列表中每个元素的proc列表的每个元素。对算法稍作调整可以保证线性时间(再次假设唯一ID):

public Optional<Schema> getSchema(final String clientId) {
  for (ProcessMetadata metadata1 : processMetadataList) {
    if (metadata1.getClientId().equalsIgnoreCase(clientId)) {
      String deviceId = metadata1.getDeviceId();
      for (ProcMetadata metadata2 : procMetadataList) {
        if (metadata2.getDeviceId().equalsIgnoreCase(deviceId)) {
          return Optional.of(metadata2.getSchema());
        }
      }
      // adding a break here ensures the search doesn't become quadratic
      break;
    }
  }
  return Optional.absent();
}

虽然使用地图当然可以确保恒定时间,这要好得多。

答案 1 :(得分:1)

我想知道可以用番石榴做些什么,并且偶然写下了这个热点。

import static com.google.common.collect.Iterables.tryFind

public Optional<Schema> getSchema(final String clientId) {
  Optional<String> deviceId = findDeviceIdByClientId(clientId);
  return deviceId.isPresent() ? findSchemaByDeviceId(deviceId.get()) : Optional.absent();
}

public Optional<String> findDeviceIdByClientId(String clientId) {
  return tryFind(processMetadataList, new ClientIdPredicate(clientId))
    .transform(new Function<ProcessMetadata, String>() {
      String apply(ProcessMetadata processMetadata) {
        return processMetadata.getDeviceId();
      }
    });
}

public Optional<Schema> findSchemaByDeviceId(String deviceId) {
  return tryFind(procMetadataList, new DeviceIdPredicate(deviceId.get())
    .transform(new Function<ProcMetadata, Schema>() {
      Schema apply(ProcMetadata procMetadata) {
        return processMetadata.getSchema();
      }
    });
}

class DeviceIdPredicate implements Predicate<ProcMetadata> {
  private String deviceId;

  public DeviceIdPredicate(String deviceId) {
    this.deviceId = deviceId;
  }

  @Override
  public boolean apply(ProcMetadata metadata2) {
    return metadata2.getDeviceId().equalsIgnoreCase(deviceId)
  }
}

class ClientIdPredicate implements Predicate<ProcessMetadata> {
  private String clientId;

  public ClientIdPredicate(String clientId) {
    this.clientId = clientId;
  }

  @Override
  public boolean apply(ProcessMetadata metadata1) {
    return metadata1.getClientId().equalsIgnoreCase(clientId);
  }
}

对不起。