Java 如何从列表中筛选元素

Java 如何从列表中筛选元素,java,guava,Java,Guava,我正在尝试实现一个相当简单的方法,在这个方法中我想要过滤一个列表。这是一个文件对象列表,应该只有一个文件以.asp结尾-我希望从列表中排除该文件。请记住,我实际上并不想从列表中删除此文件,我只是希望能够在该列表的特定迭代中忽略它 我最初的(暴力)实现如下所示: public List<File> getSurveyFiles() throws Exception { List<File> surveyFiles = new ArrayList<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;
}
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())    
    );

}
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 List getSurveyFiles()引发异常{
List surveyFiles=new ArrayList(files.size()-1);
用于(文件f:文件){
如果(!f.getName().endsWith(“.asp”)){
调查文件。添加(f);
}
}
返回调查文件;
}
它是有效的,但我正在创建第二个列表,并且从一个列表复制到另一个列表,这让我觉得非常浪费

我曾经玩弄过的另一个选择是使用番石榴库(http://code.google.com/p/guava-libraries/)并利用其过滤功能,如下所示:

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;
}
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())    
    );

}
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);
}
公共类SurveyFileControllerPredicate实现谓词{
@凌驾
公共布尔应用(文件){
返回文件.getName().endsWith(“.asp”);
}
}
...
public Iterable getSurveyFiles()引发异常{
返回Iterables.filter(
文件夹,
谓词.not(新的SurveyFileControllerPredicate())
);
}
filter的实现在迭代时而不是提前删除.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;
}
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())    
    );

}
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);
}
公共抽象类迭代器过滤器实现迭代器{
私有最终迭代器;
private next=null;
公共迭代器过滤器(迭代器迭代器){
this.iterator=迭代器;
}
@凌驾
公共布尔hasNext(){
if(next!=null)返回true;
while(iterator.hasNext()){
next=迭代器.next();
if(keep(next))返回true;
}
返回false;
}
@凌驾
公共教育{
if(next==null)
do next=iterator.next();而(!keep(next));
E结果=下一个;
next=null;
返回结果;
}
@凌驾
公共空间删除(){
迭代器.remove();//规范要求:抛出新的UnsupportedOperationException();
}
受保护抽象布尔保留(E项);
}
以及:

公共抽象类IterableFilter实现Iterable{
私人终审法院;
公共Iterable过滤器(Iterable-Iterable){
this.iterable=iterable;
}
@凌驾
公共迭代器迭代器(){
返回新的IteratorFilter(iterable.iterator()){
@凌驾
受保护布尔保持(T项){
返回IterableFilter.this.keep(项目);
}
};
}
保护抽象布尔保留(T项);
}
有了这些,您可以简单地执行以下操作:

public Iterable<File> getSurveyFiles() {
  return new IterableFilter<File>(files) {
    @Override
    protected boolean keep(File item) {
      return !item.getName().endsWith(".asp");
    }
  };
}
public Iterable getSurveyFiles(){
返回新的IterableFilter(文件){
@凌驾
受保护的布尔保留(文件项){
return!item.getName().endsWith(“.asp”);
}
};
}

它本质上与Guava谓词方法相同,只是不需要跟踪谓词对象,也不引入新的库依赖项。

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

public Iterable<File> getSurveyFiles() {
  return Iterables.filter(files, Predicates.compose(
      Predicates.not(Predicates.containsPattern("\\.asp$")),
      Functions.toStringFunction()));
}
public Iterable getSurveyFiles(){
返回Iterables.filter(文件、谓词.compose(
谓词.not(谓词.containsPattern(“\\.asp$”),
Functions.toString函数());
}

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

files.stream().filter(f -> !f.getName().endsWith(".asp")).forEachOrdered(f -> {
    //process file f
});
如果只在少数位置执行此筛选,则这比编写返回筛选副本或视图的方法更简洁,并使筛选操作靠近使用筛选列表的位置。如果您在许多地方执行此筛选,并且以后可能希望以不同的方式筛选列表,那么编写一个方法可能会更好,但它可以是一个返回流的方法:

public Stream<File> getSurveyFiles() {
    return files.stream().filter(f -> !f.getName().endsWith(".asp"));
}
公共流getSurveyFiles(){
返回文件.stream().filter(f->!f.getName().endsWith(“.asp”);
}

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

是否在适当的上下文中迭代文件?是否可以在填充原始列表本身时添加筛选器?我的意思是,原始列表填充是通过您的代码完成的,或者您正在接收预填充的列表?您也不需要使用Guava跟踪谓词对象。顺便说一句,
remove()
不应该在过滤的
迭代器上受支持:在调用
hasNext()
之后,
remove()
将不再像指定的那样删除上次调用
next()
返回的对象。它甚至可以删除迭代器不“包含”的元素(迭代器跳过了该元素)。@ColinD完全同意。实际上,我在考虑hasNext()->remove()场景,但决定让我的代码保持我使用它的方式,因为(至少在我的例子中)我从未遇到过这样的情况:我删除了一个我没有首先使用next()检查的对象。但是我在代码中添加了一条注释,因为您完全正确。@ColinD:为了完整起见,您认为使用两个并行运行的迭代器可以使remove()方法正常工作吗