如何通过流中的三元条件 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()
方法 static
和 APPLES
设为 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);
}
}
我想 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()
方法 static
和 APPLES
设为 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);
}
}