将现有条件转换为Java8流

将现有条件转换为Java8流,java,java-8,java-stream,Java,Java 8,Java Stream,如何将以下条件转换为Java8流 List<String> name = Arrays.asList("A", "B", "C"); String id; if(name.contains("A")){ id = "123"; }else if(name.contains("B")){ id = "234"; }else if(name.contains("C")){ id = "345";

如何将以下条件转换为Java8流

    List<String> name = Arrays.asList("A", "B", "C");
    String id;
    if(name.contains("A")){
        id = "123";
    }else if(name.contains("B")){
        id = "234";
    }else if(name.contains("C")){
        id = "345";
    }
List name=Arrays.asList(“A”、“B”、“C”);
字符串id;
如果(名称包含(“A”)){
id=“123”;
}如果(名称包含(“B”)){
id=“234”;
}如果(名称包含(“C”)){
id=“345”;
}

我正在学习流,想知道如何转换这个流。我试过使用foreach、map、filter,但它没有达到目的

我不明白为什么必须将其转换为流。对我来说,这似乎不是流API的情况。 但如果您想轻松地添加新项并使代码更具可读性,我建议您使用map

private static final ImmutableMap<String, String> nameToId = new ImmutableMap.Builder<String, String>()
    .put("A", "123")
    .put("B", "234")
    .put("C", "345")
    .build();

您所描述的问题是如何从一个函数应用到两个输入集(输入值和映射)中获得一个值(id)

id = f(list,mappings)
所以基本上你的问题是,找到一个基于流的f(换句话说,返回列表的解决方案不能解决你的问题)

首先,最初的if-else-if-else构造混合了三个关注点:

  • 输入验证(仅考虑值集“A”、“B”、“C”)
  • 将输入值映射到输出值(“A”->“123”、“B”->“234”、“C”->“345”)
  • 根据输入值的自然顺序定义输入值的隐含优先级(不确定这是有意的还是无意的),“A”在“B”之前,在“C”之前
当您希望将其应用于输入值流时,必须使所有输入值都显式:

  • 一个过滤函数,它忽略所有没有映射的输入值
  • 映射器函数,将输入映射到id
  • Reduce函数(二进制运算符)执行if-else-if-else构造隐含的优先级逻辑
映射函数 映射器是一个离散函数,将输入值映射到输出值的一个元素流:

Function<String,Optional<String>> idMapper = s -> {
        if("A".equals(s)){
            return Optional.of("123");
        } else if("B".equals(s)){
            return Optional.of("234");
        } else if("C".equals(s)){
            return Optional.of("345");
        }
        return Optional.empty();
    } ;
最终结果 总而言之,对于您的特定问题,使用流和输入验证的最简洁版本(不首先定义函数)是:

//define the mapping in an immutable map (that's just one way to do it)
final Map<String,String> mapping = Collections.unmodifiableMap(
  new HashMap<String,String>(){{
      put("A", "123");
      put("B", "234");
      put("C", "345");
  }});

Optional<String> result = Arrays.asList("C", "D", "A", "B")
                                .stream()
                                .filter(mapping::containsKey) 
                                .min(Comparator.naturalOrder()) 
                                .flatMap(s -> Optional.ofNullable(mapping.get(s))); 
//在不可变映射中定义映射(这只是一种方法)
最终映射=集合。不可修改映射(
新HashMap(){{
将(“A”、“123”);
付诸表决(“B”、“234”);
付诸表决(“C”、“345”);
}});
可选结果=Arrays.asList(“C”、“D”、“A”、“B”)
.stream()
.filter(映射::containsKey)
.min(比较器.naturalOrder())
.flatMap(s->Optional.ofNullable(mapping.get(s));
以下哪一项是f:

双功能f=
(list,map)->list.stream()
.filter(映射::containsKey)
.min(比较器.naturalOrder())
.flatMap(s->Optional.ofNullable(mapping.get(s));
这种方法当然有一定的吸引力,但if-else方法的简洁性带来的优雅也不容否认;)

但为了完整性,让我们看看复杂性。假设映射的数量和输入值的数量相当大(否则就无关紧要了)

基于在地图上迭代并使用contains搜索的解决方案(如在if-else构造中):

  • 最佳情况:o(1)(if-else构造中的第一个分支,列表中的第一项)
  • 最坏情况:O(n^2)(if-else构造中的最后一个分支,列表中的最后一项)
对于使用reduce的流式解决方案,当映射查找为O(1)时,必须完全迭代输入列表(O(n))

  • 最佳案例:o(n)
  • 最坏情况:O(n)

Thx对Hamlezz提出了reduce思想,Holger指出将mapper函数直接应用于流不会产生相同的结果(因为第一场比赛获胜,而不是if else构造中的第一个条目)和min(Comparator.naturalOrder())选项。

受Serghey Bishyr使用地图的答案的启发,我也使用了地图(但有序)我宁愿通过地图的键而不是列表来找到合适的id。这当然可能不是最好的解决方案,但您可以这样玩
Stream
s;-)

Map nameToId=newlinkedhashmap();
//以下顺序反映了您的条件顺序!(如果您的第一个条件包含“B”,您将在第一个位置移动“B”)
nameToId.put(“A”、“123”);
nameToId.put(“B”,“234”);
nameToId.put(“C”、“345”);
列表名称=数组.asList(“A”、“B”、“C”);
String id=nameToId.keySet()
.stream()
.filter(名称::包含)
.findFirst()
.map(nameToId::get)
.orElse(空)
你真的一无所获。。。不要试图在过滤谓词或映射函数中添加太多内容,因为这样您的
解决方案可能不再那么可读。

另一个(但紧凑的)解决方案:

Arrays.asList("B", "C", "A", "D").stream()
        .map(s -> s.equals("A") ? new SimpleEntry<>(1, "123")
                : s.equals("B") ? new SimpleEntry<>(2, "234")
                : s.equals("C") ? new SimpleEntry<>(3, "345")
                : null)
        .filter(x -> x != null)
        .reduce((a, b) -> a.getKey() < b.getKey() ? a : b)
        .map(Entry::getValue)
        .ifPresent(System.out::println);
Arrays.asList(“B”、“C”、“A”、“D”).stream()
.map(s->s.equals(“A”)?新的SimpleEntry(1,“123”)
:s.equals(“B”)?新单纯形(2,“234”)
:s.equals(“C”)?新单纯形(3,“345”)
:null)
.filter(x->x!=null)
.reduce((a,b)->a.getKey()
我正在学习流,想知道如何转换这个流。我试过使用foreach、map、filter,但没有达到目的。您的实际代码并不清楚所需的行为是什么,因为它并不是简单地将一个值映射到另一个值,而是根据检查顺序以优先级检查列表中的元素。在我看来,这是一个糟糕的代码,它应该作为一个包含多个返回语句的方法来实现,而不是“else-if”链接。这里的整个上下文都在将代码转换为流。N
Predicate<String> filter = s -> mapping.containsKey(s);
BinaryOperator<String> prioritizer = (a, b) -> a.compareTo(b) < 0 ? a : b;
Optional<String> id = Arrays.asList("C", "B", "A")
                            .stream()
                            .filter(filter) //concern: input validation
                            .reduce(prioritizer) //concern: prioritization
                            .flatMap(idMapper); //concern: id-mapping
//define the mapping in an immutable map (that's just one way to do it)
final Map<String,String> mapping = Collections.unmodifiableMap(
  new HashMap<String,String>(){{
      put("A", "123");
      put("B", "234");
      put("C", "345");
  }});

Optional<String> result = Arrays.asList("C", "D", "A", "B")
                                .stream()
                                .filter(mapping::containsKey) 
                                .min(Comparator.naturalOrder()) 
                                .flatMap(s -> Optional.ofNullable(mapping.get(s))); 
BiFunction<List<String>,Map<String,String>,Optional<String>> f = 
   (list,map) -> list.stream()
                     .filter(map::containsKey) 
                     .min(Comparator.naturalOrder()) 
                     .flatMap(s -> Optional.ofNullable(mapping.get(s))); 
Map<String, String> nameToId = new LinkedHashMap<>();
// the following order reflects the order of your conditions! (if your first condition would contain "B", you would move "B" at the first position)
nameToId.put("A", "123");
nameToId.put("B", "234");
nameToId.put("C", "345");

List<String> name = Arrays.asList("A", "B", "C");
String id = nameToId.keySet()
                    .stream()
                    .filter(name::contains)
                    .findFirst()
                    .map(nameToId::get)
                    .orElse(null)
Arrays.asList("B", "C", "A", "D").stream()
        .map(s -> s.equals("A") ? new SimpleEntry<>(1, "123")
                : s.equals("B") ? new SimpleEntry<>(2, "234")
                : s.equals("C") ? new SimpleEntry<>(3, "345")
                : null)
        .filter(x -> x != null)
        .reduce((a, b) -> a.getKey() < b.getKey() ? a : b)
        .map(Entry::getValue)
        .ifPresent(System.out::println);