Java 8 函数式编程避免使用条件语句

huangapple go评论89阅读模式
英文:

Java 8 Functional Programming avoid if conditional

问题

public class Playground {

    private static Optional<Map<String, Integer>> transform(List<Tuple<String, Optional<Integer>>> input) {
        boolean anyEmpty = input.stream().anyMatch(t -> t.second.isEmpty());
        if (anyEmpty) return Optional.empty();

        Map<String, Integer> theMap = input.stream()
                .map(t -> new Tuple<>(t.first, t.second.get()))
                .collect(Collectors.groupingBy(
                        t1 -> t1.first,
                        Collectors.mapping(t2 -> t2.second, toSingle())));
        return Optional.of(theMap);
    }

    @Test
    public void collect()  {
        List<Tuple<String, Optional<Integer>>> input1 = new ArrayList<>();
        input1.add(new Tuple<>("foo", Optional.of(1)));
        input1.add(new Tuple<>("bar", Optional.empty()));

        Optional<Map<String, Integer>> result1 = transform(input1);

        assertTrue(result1.isEmpty());

        List<Tuple<String, Optional<Integer>>> input2 = new ArrayList<>();
        input2.add(new Tuple<>("foo", Optional.of(1)));
        input2.add(new Tuple<>("bar", Optional.of(2)));

        Optional<Map<String, Integer>> result2 = transform(input2);

        assertTrue(result2.isPresent());
        assertEquals((int)1, (int)result2.get().get("foo"));
        assertEquals((int)2, (int)result2.get().get("bar"));
    }

    private static class Tuple<T1, T2> {
        public T1 first;
        public T2 second;
        public Tuple(T1 first, T2 second) {
            this.first = first;
            this.second = second;
        }
    }

    public static <T> Collector<T, ?, T> toSingle() {
        return Collectors.collectingAndThen(
                Collectors.toList(),
                list ->  list.get(0)
        );
    }
}
英文:

How do you do the equivalent of the following transform() method using pure functional programming (without the if-conditional).

Meta: I'd appreciate a title edit, I'm not sure how to word this question in "functionalese"

public class Playground {
private static Optional&lt;Map&lt;String,Integer&gt;&gt; transform(List&lt;Tuple&lt;String,Optional&lt;Integer&gt;&gt;&gt; input) {
if (input.stream().anyMatch(t-&gt;t.second.isEmpty())) return Optional.empty();
Map&lt;String, Integer&gt; theMap = input.stream()
.map(t -&gt; new Tuple&lt;&gt;(t.first, t.second.get()))
.collect(Collectors.groupingBy(
t1 -&gt; t1.first,
Collectors.mapping(t2 -&gt; t2.second, toSingle())));
return Optional.of(theMap);
}
@Test
public void collect()  {
List&lt;Tuple&lt;String,Optional&lt;Integer&gt;&gt;&gt; input1 = new ArrayList&lt;&gt;();
input1.add(new Tuple&lt;&gt;(&quot;foo&quot;, Optional.of(1)));
input1.add(new Tuple&lt;&gt;(&quot;bar&quot;, Optional.empty()));
Optional&lt;Map&lt;String,Integer&gt;&gt; result1 = transform(input1);
assertTrue(result1.isEmpty());
List&lt;Tuple&lt;String,Optional&lt;Integer&gt;&gt;&gt; input2 = new ArrayList&lt;&gt;();
input2.add(new Tuple&lt;&gt;(&quot;foo&quot;, Optional.of(1)));
input2.add(new Tuple&lt;&gt;(&quot;bar&quot;, Optional.of(2)));
Optional&lt;Map&lt;String,Integer&gt;&gt; result2 = transform(input2);
assertTrue(result2.isPresent());
assertEquals((int)1, (int)result2.get().get(&quot;foo&quot;));
assertEquals((int)2, (int)result2.get().get(&quot;bar&quot;));
}
private static class Tuple&lt;T1,T2&gt; {
public T1 first;
public T2 second;
public Tuple(T1 first, T2 second) {
this.first = first;
this.second = second;
}
}
public static &lt;T&gt; Collector&lt;T, ?, T&gt; toSingle() {
return Collectors.collectingAndThen(
Collectors.toList(),
list -&gt;  list.get(0)
);
}
}

答案1

得分: 1

虽然我的解决方案不符合您的要求,但我可以提供一个使用三元运算符的解决方案。

private static Map<String, Integer> transform(List<Tuple<String, Optional<Integer>>> input) {
    return input.stream().anyMatch(t -> t.second.isEmpty()) ? Collections.emptyMap() :
            input.stream()
                    .map(t -> new Tuple<>(t.first, t.second.get()))
                    .collect(Collectors.groupingBy(
                            t1 -> t1.first,
                            Collectors.mapping(t2 -> t2.second, toSingle())));
}

请注意,代码部分不会被翻译,我已经保留了原始的代码内容。

英文:

Although my solution does not satisfy your result, I can offer a solution with the ternary operator

private static Map&lt;String, Integer&gt; transform(List&lt;Tuple&lt;String, Optional&lt;Integer&gt;&gt;&gt; input) {
return input.stream().anyMatch(t -&gt; t.second.isEmpty()) ? Collections.emptyMap() :
input.stream()
.map(t -&gt; new Tuple&lt;&gt;(t.first, t.second.get()))
.collect(Collectors.groupingBy(
t1 -&gt; t1.first,
Collectors.mapping(t2 -&gt; t2.second, toSingle())));
}

答案2

得分: 1

这可能适用于您:

  private static Optional<Map<String, Integer>> transform(
      List<Tuple<String, Optional<Integer>>> input) {
    return Optional.of(input)
        .filter(t -> t.stream().allMatch(a -> a.second.isPresent()))
        .map(
            in ->
                in.stream()
                    .filter(t -> t.second.isPresent())
                    .map(t -> new Tuple<>(t.first, t.second.get()))
                    .collect(
                        Collectors.groupingBy(
                            t1 -> t1.first, Collectors.mapping(t2 -> t2.second, toSingle()))));
  }
英文:

This might work for you:

  private static Optional&lt;Map&lt;String, Integer&gt;&gt; transform(
      List&lt;Tuple&lt;String, Optional&lt;Integer&gt;&gt;&gt; input) {
    return Optional.of(input)
        .filter(t -&gt; t.stream().allMatch(a -&gt; a.second.isPresent()))
        .map(
            in -&gt;
                in.stream()
                    .filter(t -&gt; t.second.isPresent())
                    .map(t -&gt; new Tuple&lt;&gt;(t.first, t.second.get()))
                    .collect(
                        Collectors.groupingBy(
                            t1 -&gt; t1.first, Collectors.mapping(t2 -&gt; t2.second, toSingle()))));
  }

答案3

得分: 1

"纯函数式编程" 不一定是质量的标志,也不是一个独立的目标。

如果您想让代码更简单和更高效,这可能包括消除 if 条件语句,特别是因为它会对源数据进行第二次迭代,您可以通过多种方式实现。例如:

private static <K,V> Optional<Map<K,V>> transform(List<Tuple<K,Optional<V>>> input) {
    final class AbsentValue extends RuntimeException {
        AbsentValue() { super(null, null, false, false); }
    }

    try {
        return Optional.of(input.stream().collect(Collectors.toMap(
            t1 -> t1.first,
            t2 -> t2.second.orElseThrow(AbsentValue::new),
            (first,next) -> first)));
    } catch(AbsentValue av) {
        return Optional.empty();
    }
}

当空的 Optional 真正是异常情况时,您可以通过异常来标记,作为方法的一部分,例如:

public static class AbsentValueException extends RuntimeException {

}
private static <K,V> Map<K,V> transform(List<Tuple<K,Optional<V>>> input)
    throws AbsentValueException {

    return input.stream().collect(Collectors.toMap(
        t1 -> t1.first,
        t2 -> t2.second.orElseThrow(AbsentValueException::new),
        (first,next) -> first));
}
@Test(expected = AbsentValueException.class)
public void collect1() {
    List<Tuple<String,Optional<Integer>>> input1 = new ArrayList<>();
    input1.add(new Tuple<>("foo", Optional.of(1)));
    input1.add(new Tuple<>("bar", Optional.empty()));

    Map<String,Integer> result1 = transform(input1);
}

@Test
public void collect2() {
    List<Tuple<String,Optional<Integer>>> input2 = new ArrayList<>();
    input2.add(new Tuple<>("foo", Optional.of(1)));
    input2.add(new Tuple<>("bar", Optional.of(2)));

    Map<String,Integer> result2 = transform(input2);

    assertEquals((int)1, (int)result2.get("foo"));
    assertEquals((int)2, (int)result2.get("bar"));
}

更好的做法是从一开始就不要将 Optionals 放入元组的列表中。

英文:

“pure functional programming” is not necessarily a sign of quality and not an end in itself.

If you want to make the code simpler and more efficient, which may include getting rid of the if-conditional, especially as it bears a second iteration over the source data, you can do it in various ways. E.g.

private static &lt;K,V&gt; Optional&lt;Map&lt;K,V&gt;&gt; transform(List&lt;Tuple&lt;K,Optional&lt;V&gt;&gt;&gt; input) {
    final class AbsentValue extends RuntimeException {
        AbsentValue() { super(null, null, false, false); }
    }

    try {
        return Optional.of(input.stream().collect(Collectors.toMap(
            t1 -&gt; t1.first,
            t2 -&gt; t2.second.orElseThrow(AbsentValue::new),
            (first,next) -&gt; first)));
    } catch(AbsentValue av) {
        return Optional.empty();
    }
}

When empty optionals are truly the exceptional case, you can make flagging via exception part of the method’s contract, e.g.

public static class AbsentValueException extends RuntimeException {

}
private static &lt;K,V&gt; Map&lt;K,V&gt; transform(List&lt;Tuple&lt;K,Optional&lt;V&gt;&gt;&gt; input)
    throws AbsentValueException {

    return input.stream().collect(Collectors.toMap(
        t1 -&gt; t1.first,
        t2 -&gt; t2.second.orElseThrow(AbsentValueException::new),
        (first,next)-&gt;first));
}
@Test(expected = AbsentValueException.class)
public void collect1() {
    List&lt;Tuple&lt;String,Optional&lt;Integer&gt;&gt;&gt; input1 = new ArrayList&lt;&gt;();
    input1.add(new Tuple&lt;&gt;(&quot;foo&quot;, Optional.of(1)));
    input1.add(new Tuple&lt;&gt;(&quot;bar&quot;, Optional.empty()));

    Map&lt;String,Integer&gt; result1 = transform(input1);
}

@Test
public void collect2() {
    List&lt;Tuple&lt;String,Optional&lt;Integer&gt;&gt;&gt; input2 = new ArrayList&lt;&gt;();
    input2.add(new Tuple&lt;&gt;(&quot;foo&quot;, Optional.of(1)));
    input2.add(new Tuple&lt;&gt;(&quot;bar&quot;, Optional.of(2)));

    Map&lt;String,Integer&gt; result2 = transform(input2);

    assertEquals((int)1, (int)result2.get(&quot;foo&quot;));
    assertEquals((int)2, (int)result2.get(&quot;bar&quot;));
}

Even better would be not to put optionals into the list of tuples in the first place.

huangapple
  • 本文由 发表于 2020年9月25日 03:19:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/64053043.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定