Java 8流中的arg max?

Kon*_*ner 9 java java-8 argmax java-stream

我经常需要根据产生double或int值的标准的最大化来收集集合的最大元素.Streams有max()函数,需要我实现一个比较器,我觉得很麻烦.是否有更简洁的语法,例如names.stream().argmax(String::length)在以下示例中?

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

public class ArgMax
{
    public static void main(String[] args)
    {
        List<String> names = Arrays.asList("John","Joe","Marilyn");
        String longestName = names.stream().max((String s,String t)->(Integer.compare(s.length(),t.length()))).get();
        System.out.println(longestName);
    }
}
Run Code Online (Sandbox Code Playgroud)

Sot*_*lis 13

使用

String longestName = names.stream().max(Comparator.comparing(String::length)).get();
Run Code Online (Sandbox Code Playgroud)

比较一些属性上的元素(可能比这更复杂,但不必).

正如Brian在评论中建议的那样,Optional#get()如果有可能Stream是空的话,使用这样的方法是不安全的.您更适合使用其中一种更安全的检索方法,例如,Optional#orElse(Object)如果没有最大值,它将为您提供一些默认值.

  • 尔加!不要在结尾处使用原始`get()`,否则如果流为空则抛出NSEE.使用像`orElse`,`orElseThrow`或`ifPresent`这样的安全方法. (6认同)
  • @BrianGoetz我想我们发现了Brian的一个过敏症.每当我写"Optional.get()"时他都会打喷嚏. (5认同)
  • @zygimantus:如果你能接受第二次线性搜索,是的.否则,使用`IntStream.range(0,names.length()).boxed().max(Comparator.comparingInt(ix - > names.get(..length())`来首先搜索索引. (3认同)
  • @StuartMarks确实如此.我们应该从来没有称它为"得到".我们应该称之为`getOrThrow()`或`orElseThrow()`或`getUnsafely()`或者可能只是`getOrThrowNoSuchElementExcpetionIfThisOptionalIsNotPresent()`. (2认同)

Map*_*pio 5

我认为应该考虑的是,虽然max/min是独一无二的,但这当然不能保证argMax/ argMin; 这特别意味着减少的类型应该是一个集合,例如 a List。这需要比上面建议的更多的工作。

下面的ArgMaxCollector<T>类提供了这种减少的简单实现。在main示出了这样的类的应用程序来计算argMax/argMin琴弦组的

one two three four five six seven
Run Code Online (Sandbox Code Playgroud)

按长度排序。输出(分别报告收集器argMaxargMin收集器的结果)应该是

[three, seven]
[one, two, six]
Run Code Online (Sandbox Code Playgroud)

分别是最长的两个字符串和三个最短的字符串。

这是我第一次尝试使用新的 Java 8 流 API,因此非常欢迎任何评论!

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collector;

class ArgMaxCollector<T> {

    private T max = null;
    private ArrayList<T> argMax = new ArrayList<T>();
    private Comparator<? super T> comparator;

    private ArgMaxCollector( Comparator<? super T> comparator ) {
        this.comparator = comparator;
    }

    public void accept( T element ) {
        int cmp = max == null ? -1 : comparator.compare( max, element );
        if ( cmp < 0 ) {
            max = element;
            argMax.clear();
            argMax.add( element );
        } else if ( cmp == 0 )
            argMax.add( element );
    }

    public void combine( ArgMaxCollector<T> other ) {
        int cmp = comparator.compare( max, other.max );
        if ( cmp < 0 ) {
            max = other.max;
            argMax = other.argMax;
        } else if ( cmp == 0 ) {
            argMax.addAll( other.argMax );
        }
    }

    public List<T> get() {
        return argMax;
    }

    public static <T> Collector<T, ArgMaxCollector<T>, List<T>> collector( Comparator<? super T> comparator ) {
        return Collector.of(
            () -> new ArgMaxCollector<T>( comparator ),
            ( a, b ) -> a.accept( b ),
            ( a, b ) ->{ a.combine(b); return a; },
            a -> a.get() 
        );
    }
}

public class ArgMax {

    public static void main( String[] args ) {

        List<String> names = Arrays.asList( new String[] { "one", "two", "three", "four", "five", "six", "seven" } );

        Collector<String, ArgMaxCollector<String>, List<String>> argMax = ArgMaxCollector.collector( Comparator.comparing( String::length ) );
        Collector<String, ArgMaxCollector<String>, List<String>> argMin = ArgMaxCollector.collector( Comparator.comparing( String::length ).reversed() );

        System.out.println( names.stream().collect( argMax ) );
        System.out.println( names.stream().collect( argMin ) );

    }

}
Run Code Online (Sandbox Code Playgroud)