过滤Java集合的最佳方法是什么?



Answers

假设你使用的是Java 1.5 ,并且你不能添加Google Collections ,那么我会做一些与Google员工非常相似的事情。 Jon的评论略有不同。

首先将此接口添加到您的代码库。

public interface IPredicate<T> { boolean apply(T type); }

它的实现者可以在某个谓词对某种类型是真的时候做出回答。 例如,如果TUserAuthorizedUserPredicate<User>实现IPredicate<T> ,则AuthorizedUserPredicate#apply将返回是否允许传入的User

然后在一些实用课堂上,你可以说

public static <T> Collection<T> filter(Collection<T> target, IPredicate<T> predicate) {
    Collection<T> result = new ArrayList<T>();
    for (T element: target) {
        if (predicate.apply(element)) {
            result.add(element);
        }
    }
    return result;
}

所以,假设你有上面的使用可能

Predicate<User> isAuthorized = new Predicate<User>() {
    public boolean apply(User user) {
        // binds a boolean method in User to a reference
        return user.isAuthorized();
    }
};
// allUsers is a Collection<User>
Collection<User> authorizedUsers = filter(allUsers, isAuthorized);

如果关注线性检查的性能,那么我可能想要一个具有目标集合的域对象。 具有目标集合的域对象将具有用于初始化,添加和设置目标集合的方法的过滤逻辑。

更新:

在实用类(比方说谓词)中,当谓词没有返回期望值时,我添加了一个选择方法,其中包含默认值的选项,还有一个静态属性,用于在新的IPredicate中使用参数。

public class Predicate {
    public static Object predicateParams;

    public static <T> Collection<T> filter(Collection<T> target, IPredicate<T> predicate) {
        Collection<T> result = new ArrayList<T>();
        for (T element : target) {
            if (predicate.apply(element)) {
                result.add(element);
            }
        }
        return result;
    }

    public static <T> T select(Collection<T> target, IPredicate<T> predicate) {
        T result = null;
        for (T element : target) {
            if (!predicate.apply(element))
                continue;
            result = element;
            break;
        }
        return result;
    }

    public static <T> T select(Collection<T> target, IPredicate<T> predicate, T defaultValue) {
        T result = defaultValue;
        for (T element : target) {
            if (!predicate.apply(element))
                continue;
            result = element;
            break;
        }
        return result;
    }
}

以下示例查找集合之间的缺失对象:

List<MyTypeA> missingObjects = (List<MyTypeA>) Predicate.filter(myCollectionOfA,
    new IPredicate<MyTypeA>() {
        public boolean apply(MyTypeA objectOfA) {
            Predicate.predicateParams = objectOfA.getName();
            return Predicate.select(myCollectionB, new IPredicate<MyTypeB>() {
                public boolean apply(MyTypeB objectOfB) {
                    return objectOfB.getName().equals(Predicate.predicateParams.toString());
                }
            }) == null;
        }
    });

以下示例在集合中查找实例,并在找不到实例时将集合的第一个元素作为默认值返回:

MyType myObject = Predicate.select(collectionOfMyType, new IPredicate<MyType>() {
public boolean apply(MyType objectOfMyType) {
    return objectOfMyType.isDefault();
}}, collectionOfMyType.get(0));

更新(在Java 8发布后):

我(Alan)第一次发布这个答案已经有好几年了,我仍然不相信我正在为这个答案收集SO点数。 无论如何,现在Java 8已经引入了闭包语言,现在我的答案会大不相同,而且更简单。 对于Java 8,不需要独特的静态工具类。 所以如果你想找到与你的谓词相匹配的第一个元素。

final UserService userService = ... // perhaps injected IoC
final Optional<UserModel> userOption = userCollection.stream().filter(u -> {
    boolean isAuthorized = userService.isAuthorized(u);
    return isAuthorized;
}).findFirst();

可选项的JDK 8 API可以使用get()isPresent()orElse(defaultUser)orElseGet(userSupplier)orElseThrow(exceptionSupplier)以及其他“monadic”函数,如mapflatMapfilter

如果您只想收集与谓词匹配的所有用户,则使用Collectors来终止所需集合中的流。

final UserService userService = ... // perhaps injected IoC
final List<UserModel> userOption = userCollection.stream().filter(u -> {
    boolean isAuthorized = userService.isAuthorized(u);
    return isAuthorized;
}).collect(Collectors.toList());

有关Java 8数据流如何工作的更多示例,请参阅here

Question

我想过滤一个基于谓词的java.util.Collection




JFilter http://code.google.com/p/jfilter/最适合您的要求。

JFilter是一个简单且高性能的开源库,用于查询Java bean的集合。

主要特征

  • 支持collection(java.util.Collection,java.util.Map和Array)属性。
  • 支持任意深度收藏内藏。
  • 支持内部查询。
  • 支持参数化查询。
  • 可以在几百毫秒内过滤100万条记录。
  • 过滤器(查询)以简单的json格式给出,就像Mangodb查询。 以下是一些例子。
  • {“id”:{“$ le”:“10”}
    • 对象id属性小于等于10。
  • {“id”:{“$ in”:[“0”,“100”]}}
    • 对象id属性是0或100。
  • { “了LineItem”:{ “lineAmount”: “1”}}
    • 其中参数化类型的lineItems集合属性的lineAmount等于1。
  • {“$ and”:[{“id”:“0”},{“billingAddress”:{“city”:“DEL”}}]}
    • 其中id属性为0,而billingAddress.city属性为DEL。
  • {“lineItems”:{“taxes”:{“key”:{“code”:“GST”},“value”:{“$ gt”:“1.01”}}}}
    • 其中具有参数化类型属性的参数化类型的lineItems集合属性具有代码等于大于1.01的GST值。
  • {'$ or':[{'code':'10'},{'skus':{'$ and':[{'price':{'$ in':['20','40']} },{'code':'RedApple'}]}}]}
    • 选择产品代码为10或sku价格分别为20和40且sku代码为“RedApple”的所有产品。



“最好”的方式是太宽泛的要求。 它是“最短”吗? “最快的”? “读”? 过滤到位或进入另一个集合?

最简单(但不是最可读)的方法是迭代它并使用Iterator.remove()方法:

Iterator<Foo> it = col.iterator();
while( it.hasNext() ) {
  Foo foo = it.next();
  if( !condition(foo) ) it.remove();
}

现在,为了使其更具可读性,您可以将其包装到实用程序方法中。 然后创建一个IPredicate接口,创建该接口的匿名实现并执行如下操作:

CollectionUtils.filterInPlace(col,
  new IPredicate<Foo>(){
    public boolean keepIt(Foo foo) {
      return foo.isBar();
    }
  });

filterInPlace()迭代集合并调用Predicate.keepIt()以了解实例是否保存在集合中。

我真的没有看到为此任务引入第三方库的理由。




这里有一些真正伟大的答案。 我,我想尽可能保持简洁和可读性:

public abstract class AbstractFilter<T> {

    /**
     * Method that returns whether an item is to be included or not.
     * @param item an item from the given collection.
     * @return true if this item is to be included in the collection, false in case it has to be removed.
     */
    protected abstract boolean excludeItem(T item);

    public void filter(Collection<T> collection) {
        if (CollectionUtils.isNotEmpty(collection)) {
            Iterator<T> iterator = collection.iterator();
            while (iterator.hasNext()) {
                if (excludeItem(iterator.next())) {
                    iterator.remove();
                }
            }
        }
    }
}



https://code.google.com/p/joquery/

支持不同的可能性,

鉴于收集,

Collection<Dto> testList = new ArrayList<>();

类型,

class Dto
{
    private int id;
    private String text;

    public int getId()
    {
        return id;
    }

    public int getText()
    {
        return text;
    }
}

过滤

Java 7

Filter<Dto> query = CQ.<Dto>filter(testList)
    .where()
    .property("id").eq().value(1);
Collection<Dto> filtered = query.list();

Java 8

Filter<Dto> query = CQ.<Dto>filter(testList)
    .where()
    .property(Dto::getId)
    .eq().value(1);
Collection<Dto> filtered = query.list();

也,

Filter<Dto> query = CQ.<Dto>filter()
        .from(testList)
        .where()
        .property(Dto::getId).between().value(1).value(2)
        .and()
        .property(Dto::grtText).in().value(new string[]{"a","b"});

排序 (也可用于Java 7)

Filter<Dto> query = CQ.<Dto>filter(testList)
        .orderBy()
        .property(Dto::getId)
        .property(Dto::getName)
    Collection<Dto> sorted = query.list();

分组 (也可用于Java 7)

GroupQuery<Integer,Dto> query = CQ.<Dto,Dto>query(testList)
        .group()
        .groupBy(Dto::getId)
    Collection<Grouping<Integer,Dto>> grouped = query.list();

联接 (也可用于Java 7)

鉴于,

class LeftDto
{
    private int id;
    private String text;

    public int getId()
    {
        return id;
    }

    public int getText()
    {
        return text;
    }
}

class RightDto
{
    private int id;
    private int leftId;
    private String text;

    public int getId()
    {
        return id;
    }

    public int getLeftId()
        {
            return leftId;
        }

    public int getText()
    {
        return text;
    }
}

class JoinedDto
{
    private int leftId;
    private int rightId;
    private String text;

    public JoinedDto(int leftId,int rightId,String text)
    {
        this.leftId = leftId;
        this.rightId = rightId;
        this.text = text;
    }

    public int getLeftId()
    {
        return leftId;
    }

    public int getRightId()
        {
            return rightId;
        }

    public int getText()
    {
        return text;
    }
}

Collection<LeftDto> leftList = new ArrayList<>();

Collection<RightDto> rightList = new ArrayList<>();

可以加入像,

Collection<JoinedDto> results = CQ.<LeftDto, LeftDto>query().from(leftList)
                .<RightDto, JoinedDto>innerJoin(CQ.<RightDto, RightDto>query().from(rightList))
                .on(LeftFyo::getId, RightDto::getLeftId)
                .transformDirect(selection ->  new JoinedDto(selection.getLeft().getText()
                                                     , selection.getLeft().getId()
                                                     , selection.getRight().getId())
                                 )
                .list();

表达式

Filter<Dto> query = CQ.<Dto>filter()
    .from(testList)
    .where()
    .exec(s -> s.getId() + 1).eq().value(2);



我会把RxJava扔到戒指中,这也可以在Android 。 RxJava可能并不总是最好的选择,但如果您希望在集合中添加更多转换或在过滤时处理错误,则它会为您提供更大的灵活性。

Observable.from(Arrays.asList(1, 2, 3, 4, 5))
    .filter(new Func1<Integer, Boolean>() {
        public Boolean call(Integer i) {
            return i % 2 != 0;
        }
    })
    .subscribe(new Action1<Integer>() {
        public void call(Integer i) {
            System.out.println(i);
        }
    });

输出:

1
3
5

关于RxJava的filter更多细节可以在here找到。




番石榴:

Collection<Integer> collection = Lists.newArrayList(1, 2, 3, 4, 5);

Iterators.removeIf(collection.iterator(), new Predicate<Integer>() {
    @Override
    public boolean apply(Integer i) {
        return i % 2 == 0;
    }
});

System.out.println(collection); // Prints 1, 3, 5



如何一些简单而直接的Java

 List<Customer> list ...;
 List<Customer> newList = new ArrayList<>();
 for (Customer c : list){
    if (c.getName().equals("dd")) newList.add(c);
 }

简单,易读和简单(并且可以在Android中运行!)但是,如果您使用的是Java 8,则可以通过以下方式实现:

List<Customer> newList = list.stream().filter(c -> c.getName().equals("dd")).collect(toList());

请注意,toList()是静态导入的







我们来看看如何使用Eclipse Collections (以前称为GS Collections )过滤内置的JDK List和MutableList

List<Integer> jdkList = Arrays.asList(1, 2, 3, 4, 5);
MutableList<Integer> ecList = Lists.mutable.with(1, 2, 3, 4, 5);

如果你想过滤小于3的数字,你会期望以下输出。

List<Integer> selected = Lists.mutable.with(1, 2);
List<Integer> rejected = Lists.mutable.with(3, 4, 5);

以下是如何使用匿名内部类作为Predicate进行过滤。

Predicate<Integer> lessThan3 = new Predicate<Integer>()
{
    public boolean accept(Integer each)
    {
        return each < 3;
    }
};

Assert.assertEquals(selected, Iterate.select(jdkList, lessThan3));

Assert.assertEquals(selected, ecList.select(lessThan3));

以下是使用Predicates工厂过滤JDK列表和Eclipse Collections MutableLists的一些替代方法。

Assert.assertEquals(selected, Iterate.select(jdkList, Predicates.lessThan(3)));

Assert.assertEquals(selected, ecList.select(Predicates.lessThan(3)));

这是一个不为predicate分配对象的版本,通过使用Predicates2工厂而不是使用带Predicates2selectWith方法。

Assert.assertEquals(
    selected, ecList.selectWith(Predicates2.<Integer>lessThan(), 3));

有时你想过滤负面情况。 Eclipse Collections中有一个特殊的方法用于reject

Assert.assertEquals(rejected, Iterate.reject(jdkList, lessThan3));

Assert.assertEquals(rejected, ecList.reject(lessThan3));

以下介绍如何使用Java 8 lambda作为Predicate进行过滤。

Assert.assertEquals(selected, Iterate.select(jdkList, each -> each < 3));
Assert.assertEquals(rejected, Iterate.reject(jdkList, each -> each < 3));

Assert.assertEquals(selected, gscList.select(each -> each < 3));
Assert.assertEquals(rejected, gscList.reject(each -> each < 3));

方法partition将返回两个集合,其中包含由Predicate选择和拒绝的元素。

PartitionIterable<Integer> jdkPartitioned = Iterate.partition(jdkList, lessThan3);
Assert.assertEquals(selected, jdkPartitioned.getSelected());
Assert.assertEquals(rejected, jdkPartitioned.getRejected());

PartitionList<Integer> ecPartitioned = gscList.partition(lessThan3);
Assert.assertEquals(selected, ecPartitioned.getSelected());
Assert.assertEquals(rejected, ecPartitioned.getRejected());

注意:我是Eclipse集合的提交者。




使用集合查询引擎(CQEngine) 。 这是迄今为止最快的方式。

另请参阅: 如何查询Java中的对象集合(Criteria / SQL-like)?




等待Java 8:

List<Person> olderThan30 = 
  //Create a Stream from the personList
  personList.stream().
  //filter the element to select only those with age >= 30
  filter(p -> p.age >= 30).
  //put those filtered elements into a new List.
  collect(Collectors.toList());



Links