如何通过流中的三元条件 return 值?

How to return value by ternary condition in a stream?

我想 return 基于条件的流值。以下仅作为示例,我想将任何苹果映射到 Food.APPLE:

public enum Food {
    APPLE, APPLE2, APPLE3, BANANA, PINEAPPLE, CUCUMBER;

    private static final Food[] APPLES = new Food[] {APPLE, APPLE2, APPLE3};

    //java7
    public Food fromValue(String value) {
        for (Food type : Food.values()) {
            if (type.name().equalsIgnoreCase(value)) {
                return ArrayUtils.contains(APPLES, type) ? APPLE : type;
            }
        }
        return null;
    }

    //java8: how to include the array check for APPLES?
    public Food fromValue(String value) {
        return Arrays.stream(Food.values()).
            filter(type -> type.name().equalsIgnoreCase(value))
            .findFirst()
            .orElse(null);
    }
}

如何在流中包含三元条件?

按照 Alexis 的建议,您可以使用地图操作

public Food fromValue_v8(String value) {
    return Arrays.stream(Food.values())
        .filter(type-> type.name().equalsIgnoreCase(value))
        .map(type -> ArrayUtils.contains(APPLES, type) ? APPLE : type)
        .findFirst()
        .orElse(null);
}

三元运算符没有什么特别之处。所以你可以简单地将这个映射操作添加到 Stream

public Food fromValue(String value) {
    return Arrays.stream(Food.values())
        .filter(type -> type.name().equalsIgnoreCase(value))
        .map(type -> ArrayUtils.contains(APPLES, type)? APPLE: type)
        .findFirst()
        .orElse(null);
}

然而,这些线性搜索都不是真正必要的。使用 Map:

public enum Food {
    APPLE, APPLE2, APPLE3, BANANA, PINEAPPLE, CUCUMBER;

    private static final Map<String,Food> MAP
        = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
    static {
       EnumSet<Food> apples=EnumSet.of(APPLE, APPLE2, APPLE3);
       apples.forEach(apple->MAP.put(apple.name(), APPLE));
       EnumSet.complementOf(apples).forEach(e->MAP.put(e.name(), e));
    }
    public static Food fromValue(String value) {
        return MAP.get(value);
    }
}

它将根据需要执行不区分大小写的查找,并首先初始化为 return APPLE 替代项,因此不需要额外的比较。

你可以这样做:

import static java.util.AbstractMap.SimpleImmutableEntry;

...

enum Food {
    APPLE, APPLE2, APPLE3, BANANA, PINEAPPLE, CUCUMBER;

    private static final Map<String, Food> MAP = Stream.concat(
                Stream.of(APPLE, APPLE2, APPLE3).map(e -> new SimpleImmutableEntry<>(e.name().toLowerCase(), APPLE)),
                Stream.of(BANANA, PINEAPPLE, CUCUMBER).map(e -> new SimpleImmutableEntry<>(e.name().toLowerCase(), e)))
            .collect(toMap(SimpleImmutableEntry::getKey, SimpleImmutableEntry::getValue));

    public static Food fromValue(String value) {
        return MAP.get(value.toLowerCase());
    }
}

地图中的查找将是 O(1)

正如其他人所建议的那样,使用 Map 会更好:

import java.util.EnumSet;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class TernaryCondition {

    public enum Food {
        APPLE, APPLE2, APPLE3, BANANA, PINEAPPLE, CUCUMBER;

        private static final EnumSet<Food> APPLES = EnumSet.of(APPLE, APPLE2, APPLE3);

        private static final Map<String, Food> MAP = Stream.of(
            Food.values()).collect(
            Collectors.toMap(
                f -> f.name().toLowerCase(), 
                f -> APPLES.contains(f) ? APPLE : f));

        public static Food fromValue(String value) {
            return MAP.get(value.toLowerCase());
        }
    }

    public static void main(String[] args) {
        Food f = Food.fromValue("apple2");

        System.out.println(f); // APPLE
    }
}

我还会将 fromValue() 方法 staticAPPLES 设为 EnumSet。虽然我意识到这个答案与@Holger 的非常相似,但我只是想展示另一种构建地图的方法。

根据之前的回答,特别是来自@Alexis 的回答,我编写了一些代码来检查 booth 方法(来自 Java 7 和 Java 8)。也许这对 Java 8 上的新用户有用。

所以,我对原来的答案做了一些修改。首先,我进行了一些单元测试,并添加了两个 wrapping 方法 verifyNames()contains().其次,我们可以在发生意外操作时使用 默认行为 ,在这种情况下,当 appleApproachTwo.fromValueJava8()使用 null 不存在的 枚举值调用。

最后,最后的更改使用了 java.util.Optional 对象的潜在用途。在这种情况下,我们可以保护环境因空对象不一致而崩溃。在 Default Values and Actions

有更多关于默认值、可选和 orElse() 方法的讨论
    public enum Food {
    APPLE, APPLE2, APPLE3, BANANA, PINEAPPLE, CUCUMBER, NONE;

    private static final Food[] APPLES = new Food[] {APPLE, APPLE2, APPLE3};

    // approach one
    // java7: conventional use
    public Food fromValueJava7(String value) {
        for (Food type : Food.values()) {
            if (verifyNames(type, value)) {
                return contains(Food.APPLES, type) ? Food.APPLE : type;
            }
        }
        return null;
    }


    // approach two
    // java8: how to include the array check for APPLES?
    public Food fromValueJava8(String value) {
        return Arrays.stream(Food.values())
                .filter(type-> verifyNames(type, value))
                .map(type -> contains(Food.APPLES, type) ? Food.APPLE : type)
                .findFirst()
                .orElse(Food.NONE);
    }

    private boolean contains(Food[] apples, Food type) {
        return ArrayUtils.contains(apples, type);
    }

    private boolean verifyNames(Food type,String other) {
        return type.name().equalsIgnoreCase(other);
    }
    }

    //   FoodTest
    //   
    public class FoodTest {
    @Test
    public void foodTest(){
        Food appleApproachOne  = Food.APPLE;

        // from approach one
        assertEquals( appleApproachOne.fromValueJava7("APPLE"),   Food.APPLE);
        assertEquals( appleApproachOne.fromValueJava7("APPLE2"),  Food.APPLE);
        assertEquals( appleApproachOne.fromValueJava7("APPLE3"),  Food.APPLE);
        assertEquals( appleApproachOne.fromValueJava7("apple3"),  Food.APPLE);
        assertNull  ( appleApproachOne.fromValueJava7("apple4") );
        assertNull  ( appleApproachOne.fromValueJava7(null) );

        Food appleApproachTwo  = Food.APPLE;

        //from approach two
        assertEquals( appleApproachTwo.fromValueJava8("APPLE"),   Food.APPLE);
        assertEquals( appleApproachTwo.fromValueJava8("APPLE2"),  Food.APPLE);
        assertEquals( appleApproachTwo.fromValueJava8("APPLE3"),  Food.APPLE);
        assertEquals( appleApproachTwo.fromValueJava8("apple3"),  Food.APPLE);
        assertEquals( appleApproachOne.fromValueJava8("apple4"),  Food.NONE);
        assertEquals( appleApproachTwo.fromValueJava8(null),      Food.NONE);
    }
}