Pourquoi java.util.Set n'a-t-il pas get (int index)?

237

Je suis sûr qu'il y a une bonne raison, mais quelqu'un pourrait-il expliquer pourquoi l' java.util.Setinterface manque get(int Index)ou toute autre get()méthode similaire ?

Il semble que les ensembles soient parfaits pour y mettre des choses, mais je ne trouve pas un moyen élégant de récupérer un seul élément de celui-ci.

Si je sais que je veux le premier élément, je peux l'utiliser set.iterator().next(), mais sinon, il semble que je doive effectuer un cast vers un tableau pour récupérer un élément à un index spécifique?

Quels sont les moyens appropriés pour récupérer des données d'un ensemble? (autre que l'utilisation d'un itérateur)

Je suis sûr que le fait qu'il soit exclu de l'API signifie qu'il y a une bonne raison de ne pas le faire - quelqu'un pourrait-il m'éclairer?

EDIT: Quelques réponses extrêmement excellentes ici, et quelques-unes disant "plus de contexte". Le scénario spécifique était un test dbUnit, où je pouvais raisonnablement affirmer que l'ensemble renvoyé d'une requête n'avait qu'un seul élément, et j'essayais d'accéder à cet élément.

Cependant, la question est plus valable sans le scénario, car elle reste plus ciblée:

Quelle est la différence entre set et list .

Merci à tous pour les réponses fantastiques ci-dessous.

Marty Pitt
la source
1
Pourquoi obtiendriez-vous un élément d'un ensemble par index? Essayez-vous d'utiliser un ensemble en tant que tableau trié?
MSN
L'instance particulière ici est un test dbUnit par rapport à un ensemble renvoyé par un appel d'hibernation. Dans mon test, il est raisonnable de supposer (parce que je l'affirme) que l'objet retourné est dans un ordre spécifique, en raison de mon IDataSet que j'ai utilisé pour le configurer. C'est un cas inhabituel, mais suscite ma curiosité à propos de l'API.
Marty Pitt
1
L'ajout d'éléments dans un ordre spécifique ne signifie pas qu'ils resteront tels quels, sauf si vous utilisez une implémentation d'ensemble personnalisée.
Michael Myers
1
"Si je sais que je veux le premier élément, je peux utiliser set.iterator (). Next ()" - Cette ligne n'a pas vraiment de sens. Vous dites vraiment "Si je sais que je veux le premier élément, par la définition de la mise en œuvre du premier élément, alors je peux ...". Le jeu lui-même n'est pas ordonné, donc l'accès indexé n'a pas de sens. Maintenant, s'il y avait un ArrayListSet, cela aurait plus de sens (il suffit de transtyper en "List" et d'être heureux). Peut-être pourriez-vous donner plus de contexte à la question?
jsight
L'ensemble n'est pas désordonné! Certaines implémentations le sont, mais certaines implémentations sont explicitement ordonnées d'une manière particulière.
reinierpost

Réponses:

176

Parce que les ensembles n'ont pas de commande. Certaines implémentations le font (en particulier celles implémentant l' java.util.SortedSetinterface), mais ce n'est pas une propriété générale des ensembles.

Si vous essayez d'utiliser des ensembles de cette façon, vous devriez plutôt utiliser une liste.

Michael Myers
la source
10
@matt b: Non, je pense qu'il devrait y réfléchir. Penser, c'est bien. ;)
Michael Myers
10
Considérez-le, puis faites-le.
Joe Phillips
21
"Considérez" est la formulation correcte. Il y a deux problèmes possibles (a) Il utilise un ensemble alors qu'il devrait utiliser autre chose, ou (b) Il essaie de faire des choses avec des ensembles qu'ils ne prennent pas en charge mais qu'il pourrait faire d'une manière différente. Il est bon de considérer lequel est le cas.
kenj0418
6
La réponse la plus simple est peut-être d'utiliser un ensemble trié. (Je suppose que l'unicité a joué un rôle dans le choix de l'ensemble). Mais j'ai une question, puisque SortedSet est commandé, pourquoi il n'y a pas de méthode get dans l'api.
uncaught_exceptions
5
@HDave: Non, le fait que plusieurs implémentations d'une structure de données partagent une propriété n'en fait pas une propriété de la structure de données elle-même. Deux des trois implémentations couramment utilisées de List (ArrayList et Vector) sont à accès aléatoire, mais cela ne fait pas de l'accès aléatoire une propriété de Lists.
Michael Myers
74

En fait, c'est une question récurrente lors de l'écriture d'applications JavaEE qui utilisent le mappage relationnel objet (par exemple avec Hibernate); et parmi toutes les personnes qui ont répondu ici, Andreas Petersson est le seul à avoir compris le vrai problème et à lui proposer la bonne réponse: il manque une UniqueList à Java! (ou vous pouvez également l'appeler OrderedSet ou IndexedSet).

Maxwing a mentionné ce cas d'utilisation (dans lequel vous avez besoin de données commandées ET uniques) et il a suggéré le SortedSet, mais ce n'est pas ce dont Marty Pitt avait vraiment besoin.

Cet "IndexedSet" n'est PAS le même qu'un SortedSet - dans un SortedSet les éléments sont triés en utilisant un comparateur (ou en utilisant leur ordre "naturel").

Mais au lieu de cela, il est plus proche d'un LinkedHashSet (que d'autres ont également suggéré), ou encore plus d'un "ArrayListSet" (également inexistant), car il garantit que les éléments sont retournés dans le même ordre qu'ils ont été insérés.

Mais le LinkedHashSet est une implémentation, pas une interface! Ce dont nous avons besoin, c'est d'une interface IndexedSet (ou ListSet, OrderedSet ou UniqueList)! Cela permettra au programmeur de spécifier qu'il a besoin d'une collection d'éléments qui ont un ordre spécifique et sans doublons, puis de l'instancier avec n'importe quelle implémentation (par exemple une implémentation fournie par Hibernate).

Étant donné que JDK est open-source, cette interface sera peut-être finalement incluse dans Java 7 ...

Sorin Postelnicu
la source
3
Excellente réponse dans la mesure où elle va, mais que faisons-nous en attendant?
Enregistrez
bien sûr que ça l'est. j'ai utilisé la liste comme beaucoup ORM et onetomany en veille prolongée avant. J'ai rencontré un problème (ou un défaut) lorsqu'une requête de jointure gauche impliquant plus de 3 entités liées, une exception a été levée. regardez ici pour plus de détails ( jroller.com/eyallupu/entry/… ). pour contourner ce problème, l'utilisation de set en tant que collection de mappage ORM est nécessaire. mais honnêtement, set n'est pas pratique pour accéder à la programmation, et aussi lorsque vous avez besoin d'une collection de commandes. ce dont nous avons vraiment besoin est un "indexedset" comme ce que Sorin Postelnicu a dit, SORT et UNIQUE
horaceman
2
Apache Commons Collections a ListOrderedSetce dont le PO avait besoin il y a 7 ans (et j'avais besoin aujourd'hui).
Paul
@Paul: C'est en effet quelque chose qui a l'air vraiment bien. Malheureusement, il a encore 3 inconvénients: 1) C'est une classe, pas une interface. 2) Ce n'est pas dans le JDK. 3) Ce n'est pas ce que les requêtes Hibernate renvoient.
Sorin Postelnicu
Oui, mais à part ces 3 inconvénients majeurs, c'est parfait! :) Rétrospectivement, j'aurais dû poster mon commentaire sur la question et non votre réponse - j'ai désactivé What is needed is an IndexedSet (or ListSet, or OrderedSet, or UniqueList)...et ignoré ...interface. Désolé pour ça!
Paul
29

J'ajoute juste un point qui n'a pas été mentionné dans la réponse de mmyers .

Si je sais que je veux le premier élément, je peux utiliser set.iterator (). Next (), mais sinon, il semble que je doive effectuer un cast vers un tableau pour récupérer un élément à un index spécifique?

Quels sont les moyens appropriés pour récupérer des données d'un ensemble? (autre que l'utilisation d'un itérateur)

Vous devez également vous familiariser avec l' SortedSetinterface (dont l'implémentation la plus courante est TreeSet).

Un SortedSet est un ensemble (c'est-à-dire que les éléments sont uniques) qui est maintenu ordonné par l' ordre naturel des éléments ou en utilisant certains Comparator. Vous pouvez facilement accéder aux premier et dernier éléments à l'aide des méthodes first()et last(). A SortedSetest utile de temps en temps, lorsque vous devez conserver votre collection à la fois sans doublon et commandée d'une certaine manière.

Edit : Si vous avez besoin d'un ensemble dont les éléments sont conservés dans l'ordre d'insertion (un peu comme une liste), jetez un œil à LinkedHashSet.

Jonik
la source
J'aime moi-même LinkedHashSet. Mais oui, c'est bon à mentionner. +1
Michael Myers
Merci, j'ai légèrement modifié la réponse. (Il semble que certains aspects de TreeSet soient confondus avec ceux de LinkedHashSet.)
Jonik
25

Ce type de conduit à la question de savoir quand vous devez utiliser un ensemble et quand vous devez utiliser une liste. Habituellement, le conseil va:

  1. Si vous avez besoin de données commandées, utilisez une liste
  2. Si vous avez besoin de données uniques, utilisez un ensemble
  3. Si vous avez besoin des deux, utilisez: un SortedSet (pour les données classées par comparateur) ou un OrderedSet / UniqueList (pour les données classées par insertion). Malheureusement, l'API Java n'a pas encore OrderedSet / UniqueList.

Un quatrième cas qui apparaît souvent est que vous n'avez besoin ni de l'un ni de l'autre. Dans ce cas, certains programmeurs utilisent des listes et d'autres des ensembles. Personnellement, je trouve très dangereux de voir l'ensemble sous forme de liste sans commander - car c'est vraiment une toute autre bête. Sauf si vous avez besoin de choses comme l'unicité ou l'égalité définies, privilégiez toujours les listes.

jaseur
la source
2
si vous n'êtes pas spécifique, acceptez Collection <T> ou même Iterable <T> et initialisez-la en tant que liste.
Andreas Petersson
Ce serait un sac ou un multiset. Mais Java ne les prend pas en charge; ils disent que vous devez simplement utiliser Collection <T> directement.
Escargot mécanique
4. vous avez besoin de données non uniques et ne vous souciez pas de la commande. Vous NE POUVEZ PAS utiliser un ensemble. Une liste, un sac ou un multiset fonctionnera.
Andrew Gallasch
17

Je ne sais pas si quelqu'un l'a expliqué exactement de cette façon, mais vous devez comprendre ce qui suit:

Il n'y a pas de "premier" élément dans un ensemble.

Parce que, comme d'autres l'ont dit, les ensembles n'ont pas de commande. Un ensemble est un concept mathématique qui n'inclut pas spécifiquement la commande.

Bien sûr, votre ordinateur ne peut pas vraiment garder une liste de choses qui ne sont pas commandées en mémoire. Il doit y avoir une certaine commande. En interne, c'est un tableau ou une liste chaînée ou quelque chose. Mais vous ne savez pas vraiment ce que c'est, et il n'a pas vraiment de premier élément; l'élément qui sort «premier» sort de cette façon par hasard, et pourrait ne pas être le premier la prochaine fois. Même si vous avez pris des mesures pour «garantir» un premier élément particulier, il sort toujours par hasard, parce que vous vous êtes avéré qu'il était juste de le faire pour une implémentation particulière d'un ensemble; une implémentation différente peut ne pas fonctionner de cette façon avec ce que vous avez fait. Et, en fait, vous ne connaissez peut-être pas aussi bien l'implémentation que vous utilisez.

Les gens se heurtent à tout cela. LES. TEMPS. avec les systèmes SGBDR et ne comprennent pas. Une requête SGBDR renvoie un ensemble d'enregistrements. Il s'agit du même type d'ensemble issu des mathématiques: une collection d'éléments non ordonnée, dans ce cas uniquement, les éléments sont des enregistrements. Un résultat de requête SGBDR n'a aucun ordre garanti du tout, sauf si vous utilisez la clause ORDER BY, mais tout le temps les gens le supposent, puis se déclenchent un jour lorsque la forme de leurs données ou de leur code change légèrement et déclenche l'optimiseur de requête pour fonctionner d'une manière différente et tout à coup les résultats ne sortent pas dans l'ordre qu'ils attendent. Ce sont généralement les personnes qui n'ont pas fait attention dans la classe de base de données (ou lors de la lecture de la documentation ou des didacticiels) lorsqu'il leur a été expliqué, à l'avance, que les résultats de la requête n'ont pas un ordre garanti.

skiphoppy
la source
Hé, et bien sûr, l'ordre change généralement juste après la mise en production du code, quand il est trop lent, ils ajoutent donc un index pour accélérer la requête. Maintenant, le code s'exécute rapidement, mais donne les mauvaises réponses. Et personne ne le remarque pendant trois ou quatre jours ... si vous avez de la chance. Si vous n'êtes pas chanceux, personne ne le remarque pendant un mois ...
TMN
Je ne pense pas qu'il ait raté cela (peut-être qu'il était bâclé avec la notation). Il ne veut pas du premier élément de l'ensemble, il veut un élément arbitraire de l'ensemble. Vous pouvez lui donner un élément arbitraire depuis l' Setest Iterable.
Elazar Leibovich
Vous parlez de get (index) par index. Qu'en est-il d'un get (Object) par égalité?
Kumar Manish
10

certaines structures de données manquent dans les collections Java standard.

Sac (comme l'ensemble mais peut contenir des éléments plusieurs fois)

UniqueList (liste ordonnée, ne peut contenir chaque élément qu'une seule fois)

semble que vous auriez besoin d'une liste unique dans ce cas

si vous avez besoin de structures de données flexibles, vous pourriez être intéressé par Google Collections

Andreas Petersson
la source
1
Guva fournit-il une "UniqueList"?
Mike Rylander
non, mais vous pouvez avoir un java.util.LinkedHashSet qui a des propriétés similaires.
Andreas Petersson
7

C'est vrai, les éléments de Set ne sont pas ordonnés, par définition de la collection Set. Ils ne peuvent donc pas être accessibles par un index.

Mais pourquoi n'avons-nous pas une méthode get (object), non pas en fournissant l'index comme paramètre, mais un objet qui est égal à celui que nous recherchons? De cette façon, nous pouvons accéder aux données de l'élément à l'intérieur de l'ensemble, simplement en connaissant ses attributs utilisés par la méthode égale.

des murs
la source
7

Si vous allez faire beaucoup d'accès aléatoires par index dans un ensemble, vous pouvez obtenir une vue tableau de ses éléments:

Object[] arrayView = mySet.toArray();
//do whatever you need with arrayView[i]

Il existe cependant deux inconvénients principaux:

  1. Ce n'est pas efficace en mémoire, car un tableau pour l'ensemble entier doit être créé.
  2. Si l'ensemble est modifié, la vue devient obsolète.
fortran
la source
5

En effet, Set garantit uniquement l'unicité, mais ne dit rien sur les modèles d'accès ou d'utilisation optimaux. C'est-à-dire qu'un ensemble peut être une liste ou une carte, chacune ayant des caractéristiques de récupération très différentes.

jsight
la source
5

La seule raison pour laquelle je peux penser à utiliser un index numérique dans un ensemble serait pour l'itération. Pour cela, utilisez

for(A a : set) { 
   visit(a); 
}
Hugo
la source
Ce n'est pas vrai, qu'en est-il de l'accès à un élément aléatoire?
Jeremy Salwen
Ha, ha. bon point :) mais ce serait très sujet à une mauvaise utilisation, j'en suis sûr.
Hugo
3

Je courus dans des situations où je voulais en fait un Sorted Set avec accès par index (je suis d' accord avec d' autres affiches que l' accès à un ensemble non triés avec un indice n'a pas de sens). Un exemple serait un arbre où je voulais que les enfants soient triés et les enfants en double n'étaient pas autorisés.

J'avais besoin de l'accès via un index pour les afficher et les attributs définis étaient utiles pour éliminer efficacement les doublons.

Ne trouvant aucune collection appropriée dans les collections java.util ou google, je l'ai trouvé simple à implémenter moi-même. L'idée de base est d'envelopper un SortedSet et de créer une liste lorsque l'accès via un index est requis (et d'oublier la liste lorsque le SortedSet est modifié). Bien entendu, cela ne fonctionne efficacement que lorsque vous modifiez le SortedSet encapsulé et que l'accès à la liste est séparé pendant la durée de vie de la collection. Sinon, il se comporte comme une liste triée souvent, c'est-à-dire trop lente.

Avec un grand nombre d'enfants, cette performance a été améliorée par rapport à une liste que j'ai gardée triée via Collections.sort.

buchweizen
la source
2

Veuillez noter que seules 2 structures de données de base sont accessibles via l'index.

  • La structure des données de la matrice est accessible via un index avec une O(1)complexité temporelle pour réaliser l' get(int index)opération.
  • La structure de données LinkedList est également accessible via un index, mais avec une O(n)complexité temporelle pour réaliser l' get(int index)opération.

En Java, ArrayListest implémenté à l'aide de la structure de données Array .

Bien que Définir la structure de données peut généralement être mis en œuvre par l' intermédiaire Hashtable / HashMap ou BalancedTree structure de données, pour détecter rapidement si un élément existe et ajoutez élément non existant, généralement un bien mis en œuvre ensemble peut atteindre la O(1)complexité du temps containsfonctionnement. En Java, HashSetest l'implémentation la plus couramment utilisée de Set , elle est implémentée en appelant l' HashMapAPI et HashMapest implémentée en utilisant un chaînage séparé avec des listes liées (une combinaison d' Array et LinkedList ).

Puisque Set peut être implémenté via une structure de données différente, il n'y a pas de get(int index)méthode pour cela.

coderz
la source
Les arbres à doigts (voir la Data.Sequence.lookupfonction de Haskell ) permettent également d'accéder via un index ( O(1)près des extrémités O(log n)près du milieu, plus précisément O(min(log(k), log(n-k)))), ainsi que les arbres binaires (voir la Data.Set.lookupIndexfonction de Haskell ). Votre affirmation initiale selon laquelle "Veuillez noter que seules 2 structures de données de base sont accessibles via l'index" n'est pas correcte.
point
1

La raison pour laquelle l' interface Set n'a pas d'appel get de type index ou même quelque chose de plus basique, comme first () ou last (), c'est parce que c'est une opération ambiguë, et donc potentiellement dangereuse. Si une méthode retourne un Set, et que vous appelez, disons la méthode first () dessus, quel est le résultat attendu, étant donné qu'un Set générique ne fait aucune garantie sur la commande? L'objet résultant pourrait très bien varier entre chaque appel de la méthode, ou il pourrait ne pas vous endormir dans un faux sentiment de sécurité, jusqu'à ce que la bibliothèque que vous utilisez modifie l'implémentation ci-dessous et maintenant vous constatez que tout votre code se casse pour sans raison particulière.

Les suggestions de solutions de contournement répertoriées ici sont bonnes. Si vous avez besoin d'un accès indexé, utilisez une liste. Soyez prudent avec l'utilisation d'itérateurs ou toArray avec un ensemble générique, car a) il n'y a aucune garantie sur l'ordre et b) il n'y a aucune garantie que l'ordre ne changera pas avec les invocations suivantes ou avec différentes implémentations sous-jacentes. Si vous avez besoin de quelque chose entre les deux, un SortedSet ou un LinkedHashSet est ce que vous voulez.

// Je souhaite cependant que l'interface Set ait un élément get-random-element.

Dan
la source
1

java.util.Setest une collection d'articles non commandés. Cela n'a aucun sens si l'ensemble a un get (int index), car l'ensemble n'a pas d'index et vous ne pouvez que deviner la valeur.

Si vous le voulez vraiment, codez une méthode pour obtenir un élément aléatoire de Set.

Résultats de recherche Résultats Web Pi
la source
0

Tu peux faire new ArrayList<T>(set).get(index)

Janus Troelsen
la source
Cela renvoie une liste d'ensembles et get (index) renvoie un ensemble. Au lieu de cela, j'ai utilisé: new ArrayList<T>(t).get(0) je pense qu'il y a une opposition valable à l'idée d'obtenir un élément particulier d'un ensemble par un index. Mais ce serait bien si Set avait une fonction membre only () qui, pour les Sets de taille 1, permettait d'accéder facilement au seul élément du Set. Cela sauverait ce qui précède new ArrayListoufor (Foo foo : foos) { return foo; }
Doug Moscrop
0

Si cela ne vous dérange pas que l'ensemble soit trié, vous serez peut-être intéressé par le projet d' index-tree-map .

TreeSet / TreeMap amélioré permet d'accéder aux éléments par index ou en obtenant l'index d'un élément. Et l'implémentation est basée sur la mise à jour des poids des nœuds dans l'arborescence RB. Donc pas d'itération ni de sauvegarde par une liste ici.

Vitaly Sazanovich
la source
0

Set est une interface et certaines de ses classes d'implémentation sont HashSet, TreeSet et LinkedHashSet. Il utilise HashMap sous le capot pour stocker les valeurs. Étant donné que HashMap ne conserve pas l'ordre, il n'est pas possible d'obtenir la valeur par index.

Vous devez maintenant penser à la façon dont Set utilise HashMap car HashMap stocke une paire clé / valeur, mais pas Set. question valable. lorsque vous ajoutez un élément dans Set, en interne, il conserve un HashMap où la clé est l'élément que vous souhaitez entrer dans Set et la valeur est la constante fictive. Vous trouverez ci-dessous une implémentation interne de la fonction d'ajout. Par conséquent, toutes les clés du HashMap auront la même valeur constante.

// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}
magnonymes
la source
Toutes Setles implémentations s utilisent HashMapsous le capot pour stocker des valeurs pouvez-vous justifier cette affirmation TreeSet?
Graybeard
1
the keys in the HashMap will have the same constant value les clés du HashMaptestament correspondent à une seule et même immuableObject
barbe grise
-3

Pour obtenir un élément dans un ensemble, j'utilise le suivant:

public T getElement(Set<T> set, T element) {
T result = null;
if (set instanceof TreeSet<?>) {
    T floor = ((TreeSet<T>) set).floor(element);
    if (floor != null && floor.equals(element))
    result = floor;
} else {
    boolean found = false;
    for (Iterator<T> it = set.iterator(); !found && it.hasNext();) {
    if (true) {
        T current = it.next();
        if (current.equals(element)) {
        result = current;
        found = true;
        }
    }
    }
}
return result;
}
lala
la source
la fonction n'est pas ce que la question demandait. nous avons besoin de l'indice, pas de la valeur. quelle est votre fonction de toute façon? on dirait qu'il retourne juste l'élément s'il était égal à un élément à l'intérieur. qu'est-ce que cela fait qui ne contient pas ()?
Janus Troelsen
Où est le Tdéfini? Pourquoi if (true)?
quantum