question
De nos jours, la technologie Internet est mature et de plus en plus tend à être décentralisée, distribuée et en calcul de flux, ce qui a mis beaucoup de choses qui ont été faites du côté de la base de données du côté Java. Aujourd'hui, quelqu'un a demandé, si le champ de la base de données n'a pas d'index, comment devrait-il être déducteur en fonction du champ? Tout le monde accepte d'utiliser Java pour le faire, mais comment le faire?
répondre
Soudain, je me suis souvenu de l'article que j'ai écrit dans la liste pour éliminer les poids lourds auparavant, et je l'ai trouvé et lu. La méthode consiste à réécrire le HashCode et égal aux méthodes de l'objet dans la liste, à la jeter dans le hashset, puis à le retirer. C'est la réponse que j'ai notée comme un dictionnaire lorsque j'ai appris Java pour la première fois. Par exemple, lors de l'interview, des personnes qui sont en Java depuis 3 ans, ils peuvent mémoriser la différence entre l'ensemble et le hashmap, mais ils ne savent pas comment le mettre en œuvre. En d'autres termes, les débutants ne mémorisent que les caractéristiques. Mais lorsque vous l'utilisez réellement dans un projet, vous devez vous assurer que c'est vrai. Parce que l'approbation est inutile, je ne peux que croire au résultat. Vous devez savoir comment Hashset peut m'aider à me débarrasser de la charge lourde. Si vous y pensez, pouvez-vous supprimer la charge lourde sans hashset? Le moyen le plus simple et le plus direct est de le comparer à chaque fois des données historiques et de l'insérer dans la queue de la file d'attente si elle est différente. Et Hashset accélère simplement ce processus.
Tout d'abord, donnez à l'utilisateur de l'objet à trier
@ Data @ builder @ allargsconstructorpublic classe utilisateur {ID entier privé; Nom de la chaîne privée;} list <utilisateur> utilisateurs = listS.NewArrayList (nouvel utilisateur (1, "A"), nouvel utilisateur (1, "B"), nouvel utilisateur (2, "B"), nouvel utilisateur (1, "A"));L'objectif est de retirer l'utilisateur sans ID en double. Afin d'empêcher la querelle, je donne une règle. Il suffit de retirer des données avec des identifiants uniques à volonté et de ne pas avoir à être consciencieux sur lequel est calculé lorsque l'ID est le même.
Utilisez la méthode la plus intuitive
Cette méthode consiste à utiliser une liste vide pour stocker les données traversées.
@TestPublic void dis1 () {list <utilisateur> result = new LinkedList <> (); pour (utilisateur utilisateur: utilisateurs) {boolean b = result.stream (). anyMatch (u -> u.getId (). equals (user.getId ())); if (! b) {result.add (utilisateur); }} System.out.println (résultat);}Utiliser Hashset
Quiconque a mémorisé les fonctionnalités sait que HashSet peut éliminer les poids lourds, alors comment éliminer les poids lourds? Mémorisez-le un peu plus profondément et selon les méthodes HashCode et égal. Alors, comment est-ce basé sur ces deux-là? Les personnes qui n'ont pas lu le code source ne peuvent pas continuer et l'interview se termine ici.
En fait, HashSet est implémenté par HashMap (je n'ai jamais vu le code source et j'ai toujours pensé intuitivement que la clé de HashMap est implémentée par HashSet, ce qui est exactement le contraire). Je n'élargirai pas la description ici, il suffit de regarder la méthode de construction et d'ajouter la méthode de hashset pour comprendre.
public hashset () {map = new hashmap <> ();} / *** évidemment, s'il existe, il renvoie false, s'il n'existe pas, il renvoie true * / public boolean add (e e) {return map.put (e, présent) == null;}Ensuite, on peut également voir à partir de cela que la répétition de Hashset est implémentée sur la base de HashMAP, et l'implémentation de HashMAP repose complètement sur les méthodes HashCode et est égal. Maintenant, il est complètement ouvert. Si vous souhaitez utiliser HashSet, vous devez être optimiste quant à vos deux méthodes.
Dans cette question, nous devons déduir en fonction de l'ID, donc notre base de comparaison est ID. Les modifications sont les suivantes:
@OverridePublic Boolean equals (objet o) {if (this == o) {return true; } if (o == null || getClass ()! = o.getClass ()) {return false; } Utilisateur utilisateur = (utilisateur) o; return object.equals (id, user.id);} @ overRidepublic int hashcode () {return objets.hash (id);} // hashCodeResult = 31 * result + (élément == null? 0: element.hashcode ());Parmi eux, les objets appellent HashCode de Arrays, et le contenu est comme indiqué ci-dessus. Multiply par 31 est égal à x << 5-x.
La mise en œuvre finale est la suivante:
@TestPublic void Dis2 () {set <User> result = new HashSet <> (utilisateurs); System.out.println (résultat);}Utilisez Java Stream pour déduir
Pour en revenir à la question initiale, la raison de la pose de cette question est que si vous souhaitez recueilli le côté de la base de données du côté Java, la quantité de données peut être relativement importante, comme 100 000 pièces. Pour les mégadonnées, l'utilisation de fonctions liées au flux est la plus simple. Tout comme Stream fournit également la fonction distincte. Alors, comment devrait-il être utilisé?
users.ParallelStream (). Distinct (). ForEach (System.out :: println);
Je n'ai pas vu Lambda comme un paramètre, c'est-à-dire qu'aucune condition personnalisée n'a été fournie. Heureusement, Javadoc a marqué la norme de déduplication:
Renvoie un flux composé des éléments distincts (selon {@Link Object # equals (Object)}) de ce flux.Nous savons que nous devons également mémoriser ce principe: lorsque l'égalité est renvoyé vrai, la valeur de retour de HashCode doit être la même. C'est un peu confus logiquement lors de la mémorisation, mais tant que nous comprenons la méthode d'implémentation de Hashmap, nous ne nous sentirons pas difficiles à parler. HashMap se situe d'abord en fonction de la méthode HashCode, puis compare la méthode égale.
Par conséquent, pour utiliser Distinct pour atteindre la déduplication, vous devez remplacer le HashCode et égal aux méthodes à moins que vous n'utilisiez la par défaut.
Alors, pourquoi faites-vous cela? Cliquez et jetez un œil à l'implémentation.
<p_in> nœud <T> Réduire (pipelineHelper <T> helper, Spliterator <p_in> Splitorator) {// Si le flux est trié, il doit également être commandé, de sorte que ce qui suit va // préserve le tri-Order Terminalop <T, LinkedHashset <T>> Réduir = réduction. LinkedHashSet :: Add, LinkedHashSet :: addall); RETOUR NODES.NODE (ReducePOP.EVALUETALALLE (Helper, Splitterator));}L'interne est implémenté en utilisant la réduction. Lorsque vous pensez à réduire, vous pensez instantanément à une méthode pour mettre en œuvre vous-même distinct. J'ai juste besoin d'utiliser la réduction, et la partie de calcul consiste à comparer les éléments de flux avec un hashmap intégré, à les sauter s'il y en a, et à les mettre s'il y en a non. En fait, l'idée est la méthode la plus simple au début.
@TestPublic void dis3 () {Users.ParallelStream (). Filter (distinctBykey (user :: getID)) .ForEach (System.out :: println);} public static <T> Predicat <T> distinct (function <? Super T ,? return t -> voir.add (keyExtractor.Apply (t));}Bien sûr, s'il s'agit d'un flux parallèle, celui qui est pris n'est pas nécessairement le premier, mais est aléatoire.
La méthode ci-dessus est la plus trouvée et n'est pas invasive. Mais si vous devez utiliser distinct. Vous ne pouvez réécrire que HashCode et égal comme la méthode HashSet.
résumé
Vous ne pouvez vous entraîner que si vous pouvez utiliser ces choses vous-même. Sinon, il sera difficile de les retirer tout de suite lorsque vous voudrez vraiment les utiliser, ou vous prenez le risque. Et si vous voulez vraiment l'utiliser avec audace, il est également nécessaire de comprendre les règles et les principes de mise en œuvre. Par exemple, en quoi les implémentations de LinkedHashSet et HashSet sont-elles différentes?
Joint avec le code source LinkedHashSet simple:
Classe publique LinkedHashSet <E> étend HashSet <E> Implémentez Set <e>, Clonable, Java.io.Serializable {private static final SerialVersionUID = -2851667679971038690L; public LinkedHashSet (int initialCapacity, float loadFactor) {super (initialCapacity, loadFactor, true); } public LinkedHashSet (int initialCapacity) {super (initialCapacity, .75f, true); } public LinkedHashSet () {super (16, .75f, true); } public LinkedHashSet (collection <? étend e> c) {super (math.max (2 * c.size (), 11), .75f, true); addall (c); } @Override public Spliterator <E> Spliterator () {return Spliterators.Spliterator (This, Spliterator.Distinct | Spliterator.Ordered); }}Remplir:
Méthode pour supprimer les données en double de la collecte de listes en Java
1. Faire une boucle de tous les éléments dans la liste, puis supprimez des doublons
public static list supprimeuplicate (list list) {for (int i = 0; i <list.size () - 1; i ++) {for (int j = list.size () - 1; j> i; j -) {if (list.get (j) .equals (list.get (i))) {list.remove (j); }}} liste de retour; } 2. Découvrez les éléments en double via Hashset
Public Static List supprimeplicate (List list) {hashSet h = new HashSet (list); list.clear (); list.addall (h); Liste de retour; }3. Supprimer les éléments en double dans ArrayList pour garder la commande
// Supprimer les éléments en double dans ArrayList, conserver l'ordre public static void supprimé uplicatewith (list list) {set set = new hashSet (); List newlist = new ArrayList (); for (iterator iter = list.iterator (); iter.hasnext ();) {objet élément = iter.next (); if (set.add (élément)) newList.add (élément); } list.clear (); list.addall (newlist); System.out.println ("supprimer leplicate" + liste); }4. Iréatiser l'objet dans la liste, utiliser list.Contain (), et s'il n'existe pas, mettez-le dans une autre collection de liste.
Public Static List supprimeplicate (List list) {list listTemp = new ArrayList (); pour (int i = 0; i <list.size (); i ++) {if (! listTemp.Contains (list.get (i))) {listTemp.add (list.get (i)); }} return listTemp; }