3

我正在尝试实现一个相当简单的方法,我想在其中过滤列表。这是一个 File 对象的列表,并且应该只有一个以 .asp 结尾的文件——我希望将那个从列表中排除。请记住,我实际上并不想从列表中删除此文件,我只是希望能够在该列表的特定迭代中忽略它。

我原来的(蛮力)实现是这样的:

public List<File> getSurveyFiles() throws Exception {
    List<File> surveyFiles = new ArrayList<File>(files.size() - 1);

    for ( File f : files ) {
        if ( !f.getName().endsWith(".asp") ) {
            surveyFiles.add(f);
        }
    }

    return surveyFiles;
}

它有效,但感觉非常浪费,因为我正在创建第二个列表并从一个列表复制到另一个列表。

我玩过的另一个选择是使用 guava-libraries (http://code.google.com/p/guava-libraries/) 并利用它们的过滤器功能,如下所示:

public class SurveyFileControllerPredicate implements Predicate<File> {

    @Override
    public boolean apply(File file) {
        return file.getName().endsWith(".asp");
    }
}

...

public Iterable<File> getSurveyFiles() throws Exception {

    return Iterables.filter(
        files,
        Predicates.not(new SurveyFileControllerPredicate())    
    );

}

过滤器的实现是在迭代的时候,而不是提前删除.asp文件,所以这段代码的好处是不用再做第二个List,但是我觉得它让我的代码更复杂了。

还有其他我没有考虑的更简单的实现吗?

在整个方案中,我选择哪种实现可能并不重要。我只是好奇其他开发人员将如何解决这个问题以及他们会选择什么选项。

谢谢。

4

3 回答 3

5

您可以使用该函数组成一个正则表达式匹配谓词toString()

public Iterable<File> getSurveyFiles() {
  return Iterables.filter(files, Predicates.compose(
      Predicates.not(Predicates.containsPattern("\\.asp$")),
      Functions.toStringFunction()));
}
于 2012-10-29T15:48:06.207 回答
2

在某些时候,我给自己写了两个非常通用的帮助类来处理这样的问题:

public abstract class IteratorFilter<E> implements Iterator<E> {
  private final Iterator<E> iterator;

  private E next = null;

  public IteratorFilter(Iterator<E> iterator) {
    this.iterator = iterator;
  }

  @Override
  public boolean hasNext() {
    if (next!=null) return true;
    while (iterator.hasNext()) {
      next = iterator.next();
      if (keep(next)) return true;
    }
    return false;
  }

  @Override
  public E next() {
    if (next==null)
      do next = iterator.next(); while (!keep(next));
    E result = next;
    next = null;
    return result;
  }

  @Override
  public void remove() {
    iterator.remove(); // Specs require: throw new UnsupportedOperationException();
  }

  protected abstract boolean keep(E item);
}

和:

public abstract class IterableFilter<T> implements Iterable<T> {

  private final Iterable<T> iterable;

  public IterableFilter(Iterable<T> iterable) {
    this.iterable = iterable;
  }

  @Override
  public Iterator<T> iterator() {
    return new IteratorFilter<T>(iterable.iterator()) {
      @Override
      protected boolean keep(T item) {
        return IterableFilter.this.keep(item);
      }
    };
  }

  protected abstract boolean keep(T item);
}

有了这些,你可以简单地做到这一点:

public Iterable<File> getSurveyFiles() {
  return new IterableFilter<File>(files) {
    @Override
    protected boolean keep(File item) {
      return !item.getName().endsWith(".asp");
    }
  };
}

它与 Guava Predicate 方法基本相同,不同之处在于您不需要跟踪谓词对象并且不引入新的库依赖项。

于 2012-10-29T14:50:11.647 回答
0

如果您愿意在迭代站点编写过滤(而不是编写返回过滤副本或视图的函数),Java 8 流使这变得非常简单:

files.stream().filter(f -> !f.getName().endsWith(".asp")).forEachOrdered(f -> {
    //process file f
});

如果只在少数几个地方做这个过滤,这比写一个返回过滤后的副本或视图的方法更简洁,并保持过滤操作靠近使用过滤列表的地方。如果您在许多地方执行此过滤,并且以后可能希望以不同的方式过滤列表,那么编写一个方法可能会更好——但它可以是一个返回 Stream 的方法:

public Stream<File> getSurveyFiles() {
    return files.stream().filter(f -> !f.getName().endsWith(".asp"));
}

然后,您可以调用forEachOrdered返回值。如果您需要非流操作,请调用iterator以获取迭代器或.collect(Collectors.toList())获取列表的过滤副本。

于 2014-10-05T00:07:07.690 回答