Элегантные минимальные / максимальные абсолютные значения в Java - PullRequest
2 голосов
/ 15 марта 2010

Мне нужно получить минимальное и максимальное значения в коллекции значений с плавающей запятой после применения преобразования абсолютных значений. В Python я бы сделал это

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

как я могу выполнить ту же операцию в Java наиболее элегантным способом?

Ответы [ 5 ]

4 голосов
/ 15 марта 2010

Вы можете сделать что-то сумасшедшее, но самое простое решение - это:

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 );

Коллекции и Массивы бесконечно полезны.

3 голосов
/ 15 марта 2010

Вот еще один способ сделать это.Этот подход не требует создания второй копии массива, поскольку Arrays.asList просто создает представление данного массива на основе массива.Другим потенциальным преимуществом является тот факт, что знаки минимума и максимума сохраняются.В приведенном ниже примере минимум отображается как 1,333, а максимум - -9,43.

Это сложнее, чем решение Стефана, но, в зависимости от ситуации, оно может быть правильным для вас.

    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" я решил провести простой тест, чтобы увидеть, как работает мое решение по сравнению с решением Стефана Кендалла.,Для небольших массивов, состоящих менее чем из десяти тысяч элементов, два решения работают почти одинаково.Тем не менее, для больших массивов мое решение обычно будет работать лучше.

Подход Стефана совершенно верен, но он использует примерно вдвое больше памяти, чем мой, потому что он должен создать копию исходного массива для храненияабсолютное значение каждого элемента.Что касается сложности времени, я обнаружил, что мой подход выполнялся где-то между 4X и 7X быстрее, в основном из-за того, что подход Стефана требует копирования массива.Помните, что эти тесты проводились на одной машине (MacBook Pro, 4 ГБ, Core2 Duo @ 2.53) и результаты будут различаться в зависимости от конфигурации вашего компьютера и JVM.

Подход Стефана, безусловно, более прост,и мой может работать лучше в определенных ситуациях.Таким образом, в основном каждое решение является действительным, хотя одно или другое может быть предпочтительным в зависимости от ситуации.

2 голосов
/ 15 марта 2010

В категории "что-то сумасшедшее":

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 потребовала Итератор или был простой способ преобразовать Итератор в Коллекцию. Это может быть еще более разумным с Java 7 замыканиями .

РЕДАКТИРОВАТЬ: Это чище с Гуава / Google Collections. Я использую 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);
                 }
               })));
// ...
1 голос
/ 15 марта 2010

Создание другого (более прагматичного) ответа, чтобы разделить два подхода (см. Комментарий Кевина Дея):

Это можно сделать за один проход массива (sort - 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;
0 голосов
/ 05 мая 2015

Я бы предложил использовать Компаратор. Например, если ваш список удваивается, у вас есть следующие опции:

Получите максимальное абсолютное значение:

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 на Integer

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...