繁体   English   中英

java中优雅的最小/最大绝对值

[英]Elegant min/max absolute values in java

在应用绝对值转换后,我需要在浮点值集合中获取最小值和最大值。 在python我会这样做

min(map(abs, [-5, -7, 10, 2]))

如何以最优雅的方式在java中执行相同的操作?

你可能会做一些疯狂的事情,但最直接的解决方案是:

List<Integer> x = new ArrayList<Integer>(Arrays.asList( {-5,-7,10,2} } );
for( int i = 0; i < x.size(); i++ ){
   x.set( i, Math.abs(x.get(i)) );
}

return Collections.max( x );

集合数组非常有用。

这是另一种方法。 这种方法不需要创建数组的第二个副本,因为Arrays.asList只生成给定数组的数组支持视图 另一个潜在的优点是保留了最小值和最大值的符号。 在下面的示例中,最小值显示为1.333,最大值显示为-9.43。

它比Stefan的解决方案更复杂,但根据具体情况,它可能适合您。

    Float numbers[] = {-9.43f, 2.3f, -8.2f, 1.333f};

    // Calculate the minimum
    Float min = Collections.min(Arrays.asList(numbers), 
            new Comparator<Float>() {
                public int compare(Float o1, Float o2) {
                    Float result = Math.abs(o1) - Math.abs(o2);
                    return result > 0 ? 1 : result < 0 ? -1 : 0;
                }
            }
    );

    // Calculate the maximum
    Float max = Collections.min(Arrays.asList(numbers), 
            new Comparator<Float>() {
                public int compare(Float o1, Float o2) {
                    Float result = Math.abs(o2) - Math.abs(o1);
                    return result > 0 ? 1 : result < 0 ? -1 : 0;
                }
            }
    );

    System.out.println("Min = " + min);
    System.out.println("Max = " + max);

输出:

Min = 1.333
Max = -9.43

编辑:为了回应评论中的“hatchetman82”,我决定运行一个简单的基准测试,看看我的解决方案与Stefan Kendall的比较。 对于少于一万个元素的小阵列,这两种解决方案几乎完全相同。 但是,对于较大的阵列,我的解决方案通常会表现更好。

Stefan的方法完全有效,但它使用的内存大约是我的两倍,因为它必须创建原始数组的副本才能存储每个元素的绝对值。 至于时间复杂度,我发现我的方法执行速度提高了4到7倍,主要是因为Stefan的方法需要复制数组。 请记住,这些基准测试是在一台机器(MacBook Pro,4GB,Core2 Duo @ 2.53)上执行的,结果将根据您的计算机和JVM的配置而有所不同。

Stefan的方法肯定更直接,在某些情况下我的表现更好。 所以基本上每个解决方案都是有效的,但根据具体情况,一个或另一个可能更好。

在“疯狂的东西”类别中:

import java.util.*;
import org.apache.commons.collections.iterators.IteratorEnumeration;
import org.apache.commons.functor.core.collection.TransformedIterator;
import org.apache.commons.functor.UnaryFunction;
//...
int commonsMin = Collections.min(
                 Collections.list((Enumeration<Integer>)
                 new IteratorEnumeration(
                 new TransformedIterator<Integer, Integer>(
                 Arrays.asList(3, -5, -7, 10, -2, 4).iterator(), new UnaryFunction<Integer, Integer>(){
                   public Integer evaluate(Integer a)
                   {
                     return Math.abs(a);
                   }
                 }))));
//...

如果其中一个Collections.min重载使用了Iterator,或者有一种简单的方法将Iterator转换为Collection,那么这真的不会那么疯狂。 Java 7闭包可能会更加明智。

编辑:这与番石榴 /谷歌收藏更干净。 我使用HashSet因为我们真的不关心顺序:

import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;

// ...
int guavaMin = Collections.min(
               Sets.newHashSet(
               Iterables.transform(
               Sets.newHashSet(3, -5, -7, 10, -2, 4), new Function<Integer, Integer>(){
                 public Integer apply(Integer a)
                 {
                   return Math.abs(a);
                 }
               })));
// ...

创建另一个(更实用的)答案,分开两种方法(参见Kevin Day的评论):

这可以在一个数组传递中完成(排序为O(log n)),而不创建任何新列表。 只是不要使用Collections.max。 相反,只需计算出绝对值并具有最小/最大运行:

List<Integer> l = Arrays.asList(3, -5, -7, 10, -2, 4);
int iterMin = Integer.MAX_VALUE;
for(Integer i : l)
  if(Math.abs(i) < iterMin)
    iterMin = Math.abs(i);

如果要保留原始值,可以轻松地执行此操作:

int iterMinOrig = Integer.MAX_VALUE;
for(Integer i : l)
  if(Math.abs(i) < Math.abs(iterMinOrig))
    iterMinOrig = i;

我建议使用比较器。 例如,如果您的列表是双打,那么您有以下选项:

获得最大绝对值:

double maxAbs = Collections.max( myList, new Comparator<Double>() {
        @Override
        public int compare(Double x, Double y) {
            return Math.abs(x) < Math.abs(y) ? -1 : 1;
        }

    });

获得最小绝对值:

double minAbs = Collections.max( myList, new Comparator<Double>() {
        @Override
        public int compare(Double x, Double y) {
            return Math.abs(x) > Math.abs(y) ? -1 : 1;
        }

    });

按升序对列表进行排序(相对于绝对值):

Collections.sort( myList, new Comparator<Double>() {
            @Override
            public int compare(Double x, Double y) {
                return Math.abs(x) < Math.abs(y) ? -1 : 1;
            }

        });

按降序对列表进行排序(相对于绝对值):

Collections.sort( myList, new Comparator<Double>() {
            @Override
            public int compare(Double x, Double y) {
                return Math.abs(x) > Math.abs(y) ? -1 : 1;
            }

        });

如果你有其他类型的整数,只需用整数替换Double

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM