[英]How to return value by ternary condition in a stream?
I want to return a value of a stream based on a condition. 我想根据条件返回流的值。 Take the following as an example only, where I want to map any apple to
Food.APPLE
: 以下面的示例为例,我想将任何苹果映射到
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);
}
}
How can I include the ternary condition in a stream? 如何在流中包含三元条件?
You could do it like this: 你可以这样做:
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());
}
}
The lookup in the map will be O(1)
. 地图中的查找将是
O(1)
。
As Alexis suggested, you can use a map operation 正如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);
}
There is nothing special to the ternary operator. 三元运算符没有什么特别之处。 So you can simply add this mapping operation to the
Stream
因此,您只需将此映射操作添加到
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);
}
However, neither of these linear searches is really necessary. 但是,这些线性搜索都不是必需的。 Use a
Map
: 使用
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);
}
}
It will perform a case insensitive lookup as desired and it is initialized to return the APPLE
substitute in the first place so no additional comparison is required. 它将根据需要执行不区分大小写的查找,并将其初始化为首先返回
APPLE
替换,因此不需要进行其他比较。
According to previous answers, in particular from @Alexis I wrote some code to check booth approach (from Java 7 and Java 8). 根据之前的答案,特别是来自@Alexis,我写了一些代码来检查booth方法(来自Java 7和Java 8)。 Maybe this can be useful for new users on Java 8.
也许这对Java 8上的新用户有用。
So, I've made some changes in original answer. 所以,我在原始答案中做了一些改动。 First of all, I put some unit test and I added two wrapping methods verifyNames() and contains() .
首先,我进行了一些单元测试,并添加了两个包装方法verifyNames()和contains() 。 Second, we can use a default behavior when unexpected action occurs, in this case, when the appleApproachTwo.fromValueJava8() was called with null or an not existing enum value.
其次,我们可以在发生意外操作时使用默认行为 ,在这种情况下,当使用null或不存在的枚举值调用appleApproachTwo.fromValueJava8()时。
Finally, the last change uses the potential uses for java.util.Optional objects. 最后,最后一次更改使用了java.util.Optional对象的潜在用途。 In this case, we can protect the environment to crash due to inconsistency to null objects.
在这种情况下,我们可以保护环境因为与null对象不一致而崩溃。 There are more discussion about Default Values, Optional and orElse() method at 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);
}
}
As others have suggested, using a Map
would be better: 正如其他人所建议的那样,使用
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
}
}
I'd also make fromValue()
method static
and APPLES
an EnumSet
. 我还将
fromValue()
方法fromValue()
static
并将APPLES
fromValue()
EnumSet
。 While I realise this answer is very similar to @Holger's, I just wanted to show another approach to build the map. 虽然我意识到这个答案与@ Holger非常相似,但我只是想展示另一种构建地图的方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.