【问题标题】:How to return value by ternary condition in a stream?如何在流中按三元条件返回值?
【发布时间】:2015-03-25 12:51:09
【问题描述】:

我想根据条件返回流的值。仅以以下为例,我想将任何苹果映射到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);
    }
}

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

【问题讨论】:

  • 你可以使用map 里面的条件。
  • @AlexisC。好的,谢谢,您的意思可能是.map(type -> ArrayUtils.contains(APPLES, type) ? APPLE : type)?如果是这样,并且这是最好的解决方案,您可以将其添加为答案。
  • 那么像private static final HashSet<String> APPLES = new HashSet<>(Arrays.asList("apple", "apple2", "apple3"));public static Food fromValue(String value) { return APPLES.contains(value.toLowerCase()) ? APPLE : null; } 这样的东西呢?恒定的复杂性时间和更少的代码编写。
  • 啊,不完全一样。但是您的问题可以简化为:stackoverflow.com/questions/27807232/…。我的建议是在那里使用地图。

标签: java enums java-8 java-stream


【解决方案1】:

你可以这样做:

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)

【讨论】:

  • 您不需要getOrDefault 来获取空值。您可以简单地调用get(),因为它是普通Map.get 方法的合约,如果没有映射,则返回null。顺便提一句。对于 ASCII 字符,使用小写可能就足够了,但对于不区分大小写的比较来说,这不是通用的解决方案。
【解决方案2】:

正如 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);
}

【讨论】:

    【解决方案3】:

    三元运算符没有什么特别之处。所以你可以简单地将这个映射操作添加到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);
        }
    }
    

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

    【讨论】:

      【解决方案4】:

      根据以前的答案,特别是来自@Alexis,我编写了一些代码来检查展位方法(来自 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);
          }
      }
      

      【讨论】:

        【解决方案5】:

        正如其他人所建议的,使用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() 方法staticAPPLESEnumSet。虽然我意识到这个答案与@Holger 的答案非常相似,但我只是想展示另一种构建地图的方法。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-09-21
          • 2016-08-03
          • 2016-03-09
          • 2012-01-22
          • 2020-02-09
          • 2023-03-07
          相关资源
          最近更新 更多