Java 根据优先级从列表中筛选值

Java 根据优先级从列表中筛选值,java,java-8,Java,Java 8,我有一个类型的有效值列表: Set<String> validTypes = ImmutableSet.of("TypeA", "TypeB", "TypeC"); 有更好的方法吗 按类型筛选列表,按类型排序,收集到列表,然后只获取第一个元素 List<A> collect = classAList.stream() .filter(a -> validTypes.contains(a.getType()))

我有一个类型的有效值列表:

Set<String> validTypes = ImmutableSet.of("TypeA", "TypeB", "TypeC");

有更好的方法吗

按类型筛选列表,按类型排序,收集到列表,然后只获取第一个元素

List<A> collect = classAList.stream()
                  .filter(a -> validTypes.contains(a.getType()))
                  .sorted(Comparator.comparing(A::getType))
                  .collect(Collectors.toList());
System.out.println(collect.get(0));
List collect=classAList.stream()
.filter(a->validTypes.contains(a.getType()))
.sorted(Comparator.comparing(A::getType))
.collect(Collectors.toList());
System.out.println(collect.get(0));

您可以使用自定义比较器,如:

Comparator<A> comparator = (o1, o2) -> {
    if (preferredValidTypes.contains(o1.getType()) && !preferredValidTypes.contains(o2.getType())) {
        return 1;
    } else if (!preferredValidTypes.contains(o1.getType()) && preferredValidTypes.contains(o2.getType())) {
        return -1;
    } else {
        return 0;
    }
};
比较器比较器=(o1,o2)->{ if(preferredValidTypes.contains(o1.getType())&&!preferredValidTypes.contains(o2.getType())){ 返回1; }如果(!preferredValidTypes.contains(o1.getType())和&preferredValidTypes.contains(o2.getType()),则为else{ 返回-1; }否则{ 返回0; } };
对列表进行排序,然后根据您的条件从列表中首先查找。

我不喜欢已经给出的使用Comparator的答案。分拣是一项昂贵的操作。你只需浏览一下列表就可以做到这一点。一旦你找到了一个首选值,你就可以跳出,否则你就继续到最后去寻找一个有效的

在这种情况下,anyMatch可以提供从流处理中断的可能性:

MyVerifier verifier=new MyVerifier(validTypes,preferredValidTypes);
classAList.stream()
    .anyMatch(verifier);
System.out.println("Preferred found:"+verifier.preferred);
System.out.println("Valid found:"+verifier.valid);

public static class MyVerifier implements Predicate<A> {
    private Set<String> validTypes;
    private Set<String> preferredValidTypes;
    A preferred=null;
    A valid=null;

    public MyVerifier(Set<String> validTypes, Set<String> preferredValidTypes) {
        super();
        this.validTypes = validTypes;
        this.preferredValidTypes = preferredValidTypes;
    }

    @Override
    public boolean test(A a) {
        if(preferred==null && preferredValidTypes.contains(a.getType())) {
            preferred=a;
            // we can stop because we found the first preferred
            return true;
        } else if(valid==null && validTypes.contains(a.getType())) {
            valid=a;
        }
        return false;
    }

}
MyVerifier verifier=新的MyVerifier(有效类型,首选有效类型);
classAList.stream()
.anyMatch(验证者);
System.out.println(“首选查找:”+验证器.Preferred);
System.out.println(“找到的有效:“+verifier.Valid”);
公共静态类MyVerifier实现谓词{
私有集有效类型;
私有集首选有效类型;
A首选=空;
A有效=空;
公共MyVerifier(设置有效类型,设置首选有效类型){
超级();
this.validTypes=有效类型;
this.preferredValidTypes=preferredValidTypes;
}
@凌驾
公共布尔测试(A){
if(preferred==null&&preferredValidTypes.contains(a.getType())){
首选=a;
//我们可以停下来,因为我们找到了第一个
返回true;
}else if(valid==null&&validTypes.contains(a.getType())){
有效=a;
}
返回false;
}
}

您必须注意排序是稳定的,即相等的元素将以与初始源相同的顺序出现-您需要这样才能正确地从
列表中获得满足您要求的第一个元素,因此:

String priorityType = "TypeB";

Stream.of(new A("TypeA", "A"),
          new A("TypeB", "B"),
          new A("TypeC", "C"))
      .sorted(Comparator.comparing(A::getType, Comparator.comparing(priorityType::equals)).reversed())
      .filter(x -> validTypes.contains(priorityType))
      .findFirst()
      .orElseThrow(RuntimeException::new);

当然,可以定义两个列表,一个包含所有有效类型,另一个包含首选类型

然而,这里有另一种方法。定义一个列表,或者实际上是一个
映射
,键是有效类型,布尔值是类型是否首选

Map<String, Boolean> validTypes = ImmutableMap.of(
    "TypeA", false,
    "TypeB", false,
    "TypeC", true
);
AtomicReference
现在包含首选的
a
(如果可用),或另一个有效的
a
,或者如果流为空,则它包含
null
。如果发现具有首选类型的
A
,则此流操作短路

此选项的缺点是会产生副作用

使用
distinct()
另一项建议如下。它使用相同的映射结构,使用布尔值指示首选的值。但是,它不会产生副作用

Map<Boolean, A> map = listOfAs.stream()
    .filter(t -> validTypes.containsKey(t.getType()))
    .map(t -> new Carrier<>(validTypes.get(t.getType()), t))
    .distinct()
    .limit(2)
    .collect(Collectors.toMap(Carrier::getKey, Carrier::getValue));

如果您在其他集合中有首选的有效类型,则可以遵循此代码

Map<String,A> groupByType = classAList
           .stream()
            /* additional filter to grouping by valid types.*/
          //.filter(a->validTypes.contains(a.getType())) 
           .collect(Collectors.toMap(A::getType, Function.identity(),(v1, v2)->v1));
或者只按首选有效类型分组

 A result2 = classAList
            .stream()
            .filter(a -> preferredValidTypes.contains(a.getType()))
            .collect(Collectors.toMap(A::getType, Function.identity(), (v1, v2) -> v1))
            .entrySet()
            .stream()
            .findFirst()
            .map(Map.Entry::getValue)
            .orElseThrow(RuntimeException::new);

Java8中,您可以使用流:

public static Carnet findByCodeIsIn(Collection<Carnet> listCarnet, String codeIsIn) {
    return listCarnet.stream().filter(carnet -> codeIsIn.equals(carnet.getCodeIsin())).findFirst().orElse(null);
}

想想看。如果你想在存在多个可能匹配项的情况下进行优先级排序,你必须扫描整个列表,直到找到最高优先级元素的匹配项。我意识到这应该是一个答案,但答案是否定的。你的方法是高效、干净、简单、容易,只是正确的方法。这行不通。如果其中一个包含“TypeA”,而另一个包含“TypeB”,它将返回0(相等的元素),因为preferredValidTypes同时包含这两个元素。@PaulLemarchand这不是必需的,将
preferredValidTypes
优先于其他类型吗?至少我理解的是,“TypeA”应该优先于“TypeB”等等。@PaulLemarchand注意有两种不同的类型,即
有效类型
首选有效类型
。。。我想优先考虑TypeA,也就是说,如果类主义者有TypeA和TypeB,我希望有typeAI的对象可能误解了这个问题。如果这是他真正想要的,那么你的答案是好的。为什么
collect
只是为了得到第一个元素<代码>排序(…)。findFirst
您还假设
TypeA
TypeB
之前-虽然OP示例中为true,但这可能过于简化,而不是OP实际拥有的功能。这里的排序也是基于字母顺序。我认为明确提到这个名字会更安全,因为如果我想让typeB成为首选的话,这是行不通的type@user1692342还有一个问题,你说你只关心A型的优先权,其余的呢?假设
TypeA
不存在-您想选择哪一种?无论什么从
集合中按遭遇顺序排列的那些?或者甚至可能是你心目中的其他顺序?@Eugene…和
排序(X)。findFirst()
min(X)
的一个低效变体。我建议这不是一种方法,但一般来说,不是
。takeWhile(t->!条件)。对于每个(/*实际上是意外操作*/)
,你可以使用
。任意匹配(t->条件)
…@Holger你说得对,更新了。我很好奇你为什么不推荐这种方法。它是关于风格?性能?行为参数的副作用,如谓词,一般不鼓励。在这种特定情况下,它会起作用,但可能会在其他情况下产生适得其反的习惯。而且,嗯,在奥米克雷
A a = map.getOrDefault(true, map.get(false)); // null if not found
Map<String,A> groupByType = classAList
           .stream()
            /* additional filter to grouping by valid types.*/
          //.filter(a->validTypes.contains(a.getType())) 
           .collect(Collectors.toMap(A::getType, Function.identity(),(v1, v2)->v1));
A result = preferredValidTypes
            .stream()
            .map(groupByType::get)
            .findFirst()
            .orElseThrow(RuntimeException::new);
 A result2 = classAList
            .stream()
            .filter(a -> preferredValidTypes.contains(a.getType()))
            .collect(Collectors.toMap(A::getType, Function.identity(), (v1, v2) -> v1))
            .entrySet()
            .stream()
            .findFirst()
            .map(Map.Entry::getValue)
            .orElseThrow(RuntimeException::new);
public static Carnet findByCodeIsIn(Collection<Carnet> listCarnet, String codeIsIn) {
    return listCarnet.stream().filter(carnet -> codeIsIn.equals(carnet.getCodeIsin())).findFirst().orElse(null);
}
public final class FindUtils {
    public static <T> T findByProperty(Collection<T> col, Predicate<T> filter) {
        return col.stream().filter(filter).findFirst().orElse(null);
    }
}

public final class CarnetUtils {
    public static Carnet findByCodeTitre(Collection<Carnet> listCarnet, String codeTitre) {
        return FindUtils.findByProperty(listCarnet, carnet -> codeTitre.equals(carnet.getCodeTitre()));
    }

    public static Carnet findByNomTitre(Collection<Carnet> listCarnet, String nomTitre) {
        return FindUtils.findByProperty(listCarnet, carnet -> nomTitre.equals(carnet.getNomTitre()));
    }

    public static Carnet findByCodeIsIn(Collection<Carnet> listCarnet, String codeIsin) {
        return FindUtils.findByProperty(listCarnet, carnet -> codeIsin.equals(carnet.getCodeIsin()));
    }
}