Java 8 lambda obtenir et retirer l'élément de la liste
Donné une liste d'éléments, je veux récupérer l'élément avec une propriété donnée et supprimer de la liste. La meilleure solution que j'ai trouvé est:
ProducerDTO p = producersProcedureActive
.stream()
.filter(producer -> producer.getPod().equals(pod))
.findFirst()
.get();
producersProcedureActive.remove(p);
Est-il possible de combiner les obtenir et de les supprimer en une expression lambda?
- Cela semble vraiment comme un cas classique de quand il suffit d'utiliser une boucle et itérateur à la place.
- J'ai gentiment fait d'accord 😉 Nous sommes tellement habitués à la programmation impérative, que de toute autre façon dont les sons trop exotiques. Imaginez si la réalité est à l'inverse: nous sommes très utilisé pour la programmation fonctionnelle, et un nouveau paradigme impératif est ajoutée à Java. Diriez-vous que ce serait le cas classique pour les cours d'eau, les prédicats et les options?
- Ne l'appelez pas
get()
ici! Vous n'avez aucune idée de savoir si son vide ou pas. Vous allez lancer une exception si l'élément n'était pas là. Au lieu de cela, utilisez l'une des méthodes sûres comme ifPresent, orElse, orElseGet, ou orElseThrow. - Probablement une question de goût. Mais je voudrais aussi proposer de ne pas combiner les deux. Quand je vois certaines de code obtenir une valeur en utilisant le flux j'ai l'habitude de supposer que les opérations dans les flux sont exempts d'effets secondaires. Le mélange dans la suppression de l'élément peut entraîner un code qui peut être missleading pour les lecteurs.
- Juste pour clarifier: voulez-vous supprimer tous les éléments dans le
list
pour qui laPredicate
est vrai ou seulement sur la première (d', éventuellement, un zéro, un ou plusieurs éléments)? - Il n'a rien à voir avec le style de préférence; je suis un fan de lambdas, Java 8 ou Groovy. Mais dans ce cas, l'itération de l'API fournit une exploitation efficace que ne peut pas être utilisé dans un cours d'eau. C'est juste une API verrue.
- Je suis d'accord avec vous 🙂 Dans ce cas, il est préférable de boucle et l'utilisation d'un itérateur pour supprimer l'élément. Mais vous pouvez également filtrer le flux de la négation du prédicat et de recueillir les résultats d'une nouvelle liste. Les deux sont valables, les deux ont des inconvénients et les avantages. De toute façon, j'utilise un itérateur.
- Mhaswade Le premier vous a dit: je veux supprimer les éléments correspondant au prédicat mais je sais aussi que la liste contient un seul élément avec un "pod".
- Eh bien, pas un seul, mais tout au plus une (et donc d'utiliser les
Optional
). Désolé d'insister pour être précis, mais il est nécessaire, de l'OMI. Dans ce cas, vous devriez prendre juste @chrylis conseils parce que la mutation d'une liste de choses n'est pas fonctionnelle. Ainsi, il est approprié d'utiliser l'existant "supprimer lors de l'itération" Java motif (en veillant à ce que l'Itérateur est favorable à l'élimination) plutôt que d'essayer de l'intégrer dans le Lambda/Flux régime.
Vous devez vous connecter pour publier un commentaire.
Pour Supprimer l'élément de la liste
par exemple:
De SORTIE:
Un
B
C
removeIf
est une solution élégante pour supprimer des éléments d'une collection, mais il ne retourne pas le retiré de l'élément.Bien que le fil est assez ancien, encore pensé à fournir la solution à l'aide de
Java8
.De rendre l'utilisation de
removeIf
fonction. Complexité temporelle estO(n)
De référence de l'API: removeIf docs
Hypothèse:
producersProcedureActive
est unList
REMARQUE: Avec cette approche, vous ne serez pas en mesure d'obtenir le maintien de l'élément supprimé.
Envisager d'utiliser de la vanille java des itérateurs pour effectuer la tâche:
Avantages:
Iterable
même sansstream()
soutien (au moins ceux la mise en œuvre deremove()
sur leur itérateur).Inconvénients:
Comme pour le
d'autres réponses montrent clairement qu'il est possible, mais vous devez être conscient de
ConcurrentModificationException
peut être levée lors de la suppression d'élément de la liste est itéréeremove()
méthodes que jeter de l'UOE. (Pas ceux pour JDK collections, bien sûr, mais je pense qu'il est injuste de dire "fonctionne sur n'importe quel Itérable".)default
mise en œuvre deremoveIf
fait la même hypothèse, mais, bien sûr, il est défini surCollection
plutôt queIterable
...La solution directe serait d'invoquer
ifPresent(consommateur)
sur l'Option retourné parfindFirst()
. Cette consommation sera invoqué lorsque l'option n'est pas vide. L'avantage aussi est qu'il ne lance pas d'exception si l'opération de recherche a renvoyé un vide en option, comme votre code; au lieu de cela, il ne se passera rien.Si vous voulez retourner à la disparition de la valeur, vous pouvez
map
laOptional
au résultat de l'appelremove
:Mais notez que le
remove(Object)
opération sera de nouveau parcourir la liste pour trouver l'élément à supprimer. Si vous avez une liste avec accès aléatoire, comme unArrayList
, il serait préférable de créer un Flux de plus de l'index de la liste et de trouver le premier indice correspondant au prédicat:Avec cette solution, les
remove(int)
opération s'opère directement sur l'index.int
de la valeur...LinkedList
peut-être vous ne devriez pas utiliser le flux API comme il n'y a pas de solution sans traversant au moins deux fois. Mais je ne sais pas du tout scénario de la vie réelle où l'enseignement de l'avantage d'une liste chaînée peut compenser ses frais généraux réels. Donc la solution la plus simple est de ne jamais utiliserLinkedList
.remove(Object)
ne renvoie uneboolean
dire si il y avait un élément à supprimer ou pas.Optional.map()
opération? Je sais que ça fonctionne, mais ne devrait pas les fonctions être utilisé pour produire un résultat à partir de son entrée?boxed()
méthode pour lancer le mappeur d'int?boxed()
vous obtenez uneOptionalInt
qui ne peutmap
deint
àint
. Contrairement àIntStream
, il n'y a pas demapToObj
méthode. Avecboxed()
, vous aurez uneOptional<Integer>
qui permet demap
à un objet arbitraire, c'est à dire laProducerDTO
retourné parremove(int)
. La distribution deInteger
àint
est nécessaire pour lever l'ambiguïté entreremove(int)
etremove(Object)
.Utiliser pouvez utiliser le filtre de Java 8, et de créer une autre liste si vous ne souhaitez pas changer de l'ancienne liste:
Je suis sûr que ce sera impopulaire réponse, mais il fonctionne...
p[0]
permettra de conserver l'élément trouvé ou nulle.Le "truc" c'est de contourner le "effectivement finale" du problème à l'aide d'un tableau de référence, qui est effectivement final, mais que la définition de son premier élément.
.orElse(null)
pour obtenir leProducerDTO
ounull
....orElse(null)
et ont uneif
, non?remove()
trop en utilisantorElse(null)
?if(p!=null) producersProcedureActive.remove(p);
c'est encore plus courte que la lambda expression dans votreifPresent
appel.if
déclaration.Avec Eclipse Collections vous pouvez utiliser
detectIndex
avecremove(int)
sur java.util.Liste.Si vous utilisez le
MutableList
type de Eclipse Collections, vous pouvez appeler ladetectIndex
méthode directement sur la liste.Remarque: je suis un committer pour Eclipse Collections
Comme d'autres l'ont suggéré, cela pourrait être un cas d'utilisation pour des boucles et des iterables. À mon avis, c'est l'approche la plus simple. Si vous souhaitez modifier la liste en place, il ne peut pas être considéré comme "réel" de la programmation fonctionnelle, de toute façon. Mais vous pouvez utiliser
Collectors.partitioningBy()
afin d'obtenir une nouvelle liste avec des éléments qui répondent à votre condition physique, et une nouvelle liste de ceux qui ne le sont pas. Bien sûr, avec cette approche, si vous avez plusieurs éléments satisfait à la condition, tous ceux qui seront dans la liste et pas seulement la première.Ci-dessous la logique est la solution sans modifier l'original de la liste
Combinant mon idée de départ et de vos réponses, j'ai atteint ce qui semble être la solution
à ma propre question:
Il permet de supprimer l'objet à l'aide
remove(int)
qui ne traversent pas encore laliste (comme suggéré par @Tunaki) et il permet le retour de l'supprimé l'objet d'
la fonction de l'appelant.
J'ai lu vos réponses qui me suggère de choisir des méthodes sûres comme
ifPresent
au lieu deget
mais je ne trouve pas le moyen de les utiliser dans ce scénario.Il un inconvénient important dans ce type de solution?
Modifier suivant @Holger conseils
Ce devrait être la fonction que j'ai besoin de
get
et d'intercepter l'exception. Ce n'est pas seulement mauvais style, mais peut aussi causer la mauvaise performance. Le propre de la solution est encore plus simple,return /* stream operation*/.findFirst() .map(i -> producersProcedureActive.remove((int)i)) .orElseGet(() -> { logger.error("No producer found with POD [" + pod + "]"); return null; });
Lorsque nous voulons obtenir plusieurs éléments d'une Liste dans une autre liste (filtre à l'aide d'un prédicat) et de les retirer de la liste existante, je ne pouvais pas trouver une réponse adéquate de n'importe où.
Ici est de savoir comment nous pouvons le faire à l'aide de Java API de diffusion en continu de partitionnement.
De cette façon, vous pouvez effectivement supprimer les éléments filtrés à partir de la liste d'origine et de les ajouter à une nouvelle liste.
Reportez-vous à la 5.2. Des collectionneurs.partitioningBy section de cet article.
la tâche est: ✶et✶ supprimer l'élément de la liste