2010-03-15 4 views
2

J'ai besoin d'obtenir les valeurs minimum et maximum dans une collection de valeurs à virgule flottante, après avoir appliqué une conversion de valeur absolue. En python je le feraisValeurs absolues min/max élégantes dans Java

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

Comment puis-je effectuer la même opération dans Java de la manière la plus élégante?

+0

en python lambda n'est pas nécessaire, il suffit de faire carte (abs, ...) –

+0

droite ... la privation de sommeil prend encore une fois son péage. –

+1

Java ne peut pas obtenir des fermetures assez rapidement. –

Répondre

4

Vous pourriez être en mesure de faire quelque chose de fou, mais la solution la plus simple est la suivante:

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

Collections et Arrays sont infiniment utiles.

+0

ah ok. Je suis obligé de créer une liste temporaire. –

+1

Excuses si cela est pédant, mais ne retourne pas "Collections.max (x);" mandat retournant seulement la valeur max dans la liste? – Everyone

+0

Droite droite. Je n'ai pas vu la partie "min" et max. Je pense qu'il est assez facile de deviner que Collections.min existe à partir de Collections.max, cependant, et j'ai même lié à l'API Java pour la confirmation de tels: P. –

3

Voici une autre façon de le faire. Cette approche ne nécessite pas de créer une deuxième copie du tableau, puisque Arrays.asList produit simplement une vue supportée par le tableau du tableau donné. Un autre avantage potentiel est le fait que les signes du minimum et du maximum sont préservés. Dans l'exemple ci-dessous, le minimum est affiché à 1,333 et le maximum à -9,43.

Il est plus complexe que la solution de Stefan, mais en fonction de la situation, il pourrait être bon pour vous.

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

Sortie:

Min = 1.333 
Max = -9.43 

Edit: En réponse à "hatchetman82" dans les commentaires, j'ai décidé de lancer une référence simple pour voir comment ma solution par rapport à Stefan réalise Kendall. Pour les petits réseaux de moins de dix mille éléments, les deux solutions fonctionnent presque de la même manière. Cependant, pour les réseaux plus grands, ma solution fonctionnera généralement mieux.

approche de Stefan est tout à fait valable, mais il utilise environ deux fois plus de mémoire que la mienne, car il doit créer une copie du tableau d'origine afin de stocker la valeur absolue de chaque élément. En ce qui concerne la complexité temporelle, j'ai trouvé que mon approche se situait entre 4X et 7X plus vite, principalement en raison de l'approche de Stefan pour copier le tableau. Gardez à l'esprit que ces tests ont été effectués sur une seule machine (un MacBook Pro, 4 Go, Core2 Duo @ 2.53) et que les résultats varient en fonction des configurations de votre ordinateur et de la JVM.

L'approche de Stefan est certainement plus directe, et la mienne peut mieux fonctionner dans certaines situations. Donc, fondamentalement, chaque solution est valide, bien que l'un ou l'autre puisse être préférable selon la situation.

+0

deux fois les appels à Math.abs() si ... – radai

+0

Ces appels à 'Math.abs()' s'avèrent être bon marché, et avoir à copier l'ensemble du tableau s'avère très coûteux. J'ai effectué quelques benchmarks simples pour déterminer la différence. La méthode que j'ai décrite est entre 4X et 7X plus rapide pour une liste d'un million de nombres à virgule flottante. Il utilise aussi moitié moins de mémoire que l'approche de Stefan puisque le tableau original n'est jamais copié. Je vais ajouter les résultats de référence à ma réponse pour ceux qui sont intéressés. –

2

Dans la catégorie « quelque chose de fou »:

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); 
        } 
       })))); 
//... 

Ce ne serait vraiment pas ce fou si l'un des Collections.min a pris une surcharge Iterator, ou il y avait un moyen facile de convertir un Iterator à une collection. Il peut être encore plus sain avec Java 7 closures.

EDIT: Ceci est propre avec Guava/Collections Google.J'utilise HashSet parce que nous avons vraiment ne se soucient pas de l'ordre:

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); 
       } 
       }))); 
// ... 
+0

@Matthew: Je vous recommande de supprimer le premier morceau de code de cette réponse - ce que vous écrivez en bas est vraiment la meilleure réponse proposée jusqu'à présent, et j'ai presque sauté sur b/c j'ai vu l'approche orientée objet à la Haut. OO n'est pas toujours la meilleure solution! –

+0

Kevin, j'ai séparé les deux approches. –

1

Création d'une autre (plus pragmatique) réponse, pour séparer deux approches (voir le commentaire de Kevin Day):

Cela peut être fait dans un tableau pass (le tri est O (log n)), sans créer de nouvelle liste. N'utilisez pas Collections.max. Au lieu de cela, juste calculer la valeur absolue que vous allez avoir un minimum de fonctionnement/maximum:

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

Si vous souhaitez conserver la valeur d'origine, vous pouvez facilement faire:

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

je suggère en utilisant un comparateur. Par exemple, si votre liste est double, vous avez les options suivantes:

Obtenez la valeur absolue maximale:

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

    }); 

Obtenez la valeur minimum:

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

    }); 

Trier votre liste Ordre croissant (par rapport à la valeur absolue):

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

     }); 

Trier votre liste dans des cending ordre (par rapport à la valeur absolue):

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

     }); 

Si vous avez un autre type comme des entiers, il suffit de remplacer Double avec Entier

Questions connexes