In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article mainly explains "summing up a List object deduplication failure leads to thinking about distinct () in Java8". The content of the article is simple and clear, and it is easy to learn and understand. Now please follow the editor's train of thought to slowly deepen, together to study and study "summarize a List object deduplication failure caused by Java8 distinct () thinking" bar!
Another conjecture from list to map
Java8 uses lambda expressions for functional programming to make it very convenient to manipulate collections. A more common operation is to convert list to map, which is generally done using the toMap () method of Collectors. A common problem is that when the list contains the same element, if you don't specify which one to take, an exception will be thrown. Therefore, this designation is necessary. Java interview Treasure Book PDF full version
Of course, using another overloaded method of toMap (), you can specify it directly. Here, we want to discuss another method: can you use distinct () to filter out the repeating elements of list before converting to map, and then you don't have to think about repeating elements when converting to map.
Use distinct () to list to reuse and directly use distinct (), failed package example.mystream;import lombok.AllArgsConstructor;import lombok.Getter;import lombok.NoArgsConstructor;import lombok.ToString;import java.util.Arrays;import java.util.List;import java.util.Map;import java.util.stream.Collectors;public class ListToMap {@ AllArgsConstructor @ NoArgsConstructor @ ToString private static class VideoInfo {@ Getter String id; int width; int height } public static void main (String [] args) {List list = Arrays.asList (new VideoInfo ("123,1,2), new VideoInfo (" 456,4,5), new VideoInfo ("123,1,2) / / preferred: handle duplicated data when toMap () Map id2VideoInfo = list.stream (). Collect (Collectors.toMap (VideoInfo::getId, x-> x, (oldValue, newValue)-> newValue)); System.out.println ("No Duplicated1:"); id2VideoInfo.forEach ((x, y)-> System.out.println ("")) / / handle duplicated data using distinct (), before toMap () Map id2VideoInfo2 = list.stream (). Distinct (). Collect (Collectors.toMap (VideoInfo::getId, x-> x)); System.out.println ("No Duplicated2:"); id2VideoInfo2.forEach ((x, y)-> System.out.println ("));}}
There are a total of three elements in list, two of which we think are duplicated. The first transformation is to use toMap () to directly specify the handling of duplicate key, so it can be converted to map normally. The second conversion is to de-weight the list and then convert it to map, which still fails and throws an IllegalStateException, so distinct () should have failed.
No Duplicated1: Exception in thread "main" java.lang.IllegalStateException: Duplicate key ListToMap.VideoInfo (id=123, width=1 Height=2) at java.util.stream.Collectors.lambda$throwingMerger$0 (Collectors.java:133) at java.util.HashMap.merge (HashMap.java:1253) at java.util.stream.Collectors.lambda$toMap$58 (Collectors.java:1320) at java.util.stream.ReduceOps$3ReducingSink.accept (ReduceOps.java:169) at java.util.stream.DistinctOps$1 $2.accept (DistinctOps.java:175) at java.util.Spliterators$ArraySpliterator.forEachRemaining (Spliterators.java:948) at java.util.stream .AbstractPipeline.copyInto (AbstractPipeline.java:481) at java.util.stream.AbstractPipeline.wrapAndCopyInto (AbstractPipeline.java:471) at java.util.stream.ReduceOps$ReduceOp.evaluateSequential (ReduceOps.java:708) at java.util.stream.AbstractPipeline.evaluate (AbstractPipeline.java:234) at java.util.stream.ReferencePipeline.collect (ReferencePipeline.java:499) at example.mystream.ListToMap.main (ListToMap.java:79) reason: distinct () depends on equals ()
Looking at the API of distinct (), you can see the following introduction:
Returns a stream consisting of the distinct elements (according to {@ link Object#equals (Object)}) of this stream.
Obviously, when distinct () removes the weight of an object, it is handled according to the object's equals () method. If our VideoInfo class does not overrride the equals () method of the superclass Object, we will use Object.
But the equals () method of Object returns true only if the two objects are exactly the same. What we want is to assume that two videoInfo objects are the same as long as the id/width/height of the VideoInfo is the same. So for example, we override the equals () method that belongs to videoInfo.
Considerations for overriding equals ()
The equals () of our design VideoInfo is as follows:
Overridepublic boolean equals (Object obj) {if (! (obj instanceof VideoInfo)) {return false;} VideoInfo vi = (VideoInfo) obj; return this.id.equals (vi.id) & & this.width = = vi.width & & this.height = = vi.height;}
In this way, as long as the three properties of the two videoInfo objects are the same, the two objects are the same. Happy to run the program, still failed! Why?
Effective Java is such a good book that even James Gosling, the father of Java, says it is a Java tutorial that even he needs. In this book, the author points out that if you rewrite the equals () method of a class, you must rewrite its hashCode () method together! Must! There is no room for negotiation!
The rewritten equals () must meet the following conditions:
Comparing according to equals (), if two objects are equal, the value of hashCode () must also be the same.
Compared according to equals (), the values of hashCode () can be the same or different for two objects that are not equal.
Because this is the rule of Java, violating these rules will cause the Java program to no longer work properly.
For more details, I suggest you read the original book, which will certainly benefit a lot. Highly recommended!
Finally, I designed the hashCode () method of VideoInfo according to the guidance of the divine book as follows:
@ Overridepublic int hashCode () {int n = 31; n = n * 31 + this.id.hashCode (); n = n * 31 + this.height; n = n * 31 + this.width; return n;}
Finally, distinct () successfully filters the repeating elements in list, so it's okay to use two kinds of toMap () to convert list to map:
No Duplicated1: No Duplicated2: extension
Since distinct () is called equals () for comparison, as I understand it, the three elements of list need to be compared at least three times. Did you just call equals () three times?
Add a print sentence to equals () so that you can know. The added equals () is as follows:
@ Override public boolean equals (Object obj) {if (! (obj instanceof VideoInfo) {return false;} VideoInfo vi = (VideoInfo) obj; System.out.println ("Invoke equals () = = >" + this.toString () + "vs." + vi.toString ()); return this.id.equals (vi.id) & & this.width = = vi.width & & this.height = = vi.height;}
Results:
No Duplicated1: Invoke equals () = > ListToMap.VideoInfo (id=123, width=1, height=2) vs. ListToMap.VideoInfo (id=123, width=1, height=2) No Duplicated2:
It turns out that equals () was called only once. Why not three times? If you think about it, compared with hashCode (), hashCode () will be the same once, that is, the first and third elements of list (both VideoInfo (id=123, width=1, height=2) will have the same hashCode ().
So can we guess that only when the hashCode returned by hashCode () is the same will equals () be called for further judgment. If the hashCode returned by hashCode () is different, then you can assume that the two objects must be different!
Verify the conjecture:
Change hashCode () as follows:
@ Overridepublic int hashCode () {return 1;}
In this way, the return value of hashCode () is the same for all objects. Of course, this is in line with the Java specification, because Java only states that the hashCode of objects with the same equals () must be the same, but the hashCode of different objects may not be different.
Results:
No Duplicated1: Invoke equals () = > ListToMap.VideoInfo (id=456, width=4, height=5) vs. ListToMap.VideoInfo (id=123, width=1, height=2) Invoke equals () = > ListToMap.VideoInfo (id=456, width=4, height=5) vs. ListToMap.VideoInfo (id=123, width=1, height=2) Invoke equals () = > ListToMap.VideoInfo (id=123, width=1, height=2) vs. ListToMap.VideoInfo (id=123, width=1, height=2) No Duplicated2:
Sure enough, equals () was called three times! It does seem that equal () is called only if the hashCode is the same; if the hashCode is not the same, the two objects are obviously different. The guess is correct.
Conclusion
From list to map, toMap () is recommended, and the trade-off rules after repetition should be specified regardless of whether there will be repeated problems or not, which is effortless but of great benefit.
Using distinct () for a custom class, remember to override the equals () method
Overwrite equals (), be sure to overwrite hashCode ()
Although designing a hashCode () can simply let it return 1, this does not violate the Java rules, but doing so can lead to a lot of consequences. For example, when such objects are stored in hashMap, the hashCode of all objects is the same, and eventually all objects are stored in the same bucket of hashMap, which directly deteriorates the hashMap into a linked list. As a result, the complexity of O (1) is reduced to O (n), and the performance of O (1) decreases greatly.
Good books are the ladder of progress for programmers. Gorky. Such as "Effecctive Java".
Final reference procedure:
Package example.mystream;import lombok.AllArgsConstructor;import lombok.Getter;import lombok.NoArgsConstructor;import lombok.ToString;import java.util.Arrays;import java.util.List;import java.util.Map;import java.util.stream.Collectors;public class ListToMap {@ AllArgsConstructor @ NoArgsConstructor @ ToString private static class VideoInfo {@ Getter String id; int width; int height Public static void main (String [] args) {System.out.println (new VideoInfo (123,1,2) .equals (new VideoInfo (123,1,2));} @ Override public boolean equals (Object obj) {if (! (obj instanceof VideoInfo)) {return false;} VideoInfo vi = (VideoInfo) obj Return this.id.equals (vi.id) & & this.width = = vi.width & & this.height = = vi.height;} / * * If equals () is override, hashCode () must be override, too. * 1. If an equals b, they must have the same hashCode; * 2. If a doesn't equals b, they may have the same hashCode; * 3. HashCode written in this way can be affected by sequence of the fields; * 3.2 ^ 5-1 = 31. So 31 will be faster when do the multiplication, * because it can be replaced by bit-shifting: 31 * I = (I x, (oldValue, newValue)-> newValue); System.out.println ("No Duplicated1:"); id2VideoInfo.forEach ((x, y)-> System.out.println ("")) / / handle duplicated data using distinct (), before toMap () / / Note that distinct () relies on equals () in the object / / if you override equals (), hashCode () must be override together Map id2VideoInfo2 = list.stream () .distinct () .collect (Collectors.toMap (VideoInfo::getId, x-> x)); System.out.println ("No Duplicated2:") Id2VideoInfo2.forEach ((x, y)-> System.out.println ("");}} further extend the assumption that the class is someone else's and cannot be modified.
Above, VideoInfo makes us write our own classes, to which we can add equals () and hashCode () methods. If VideoInfo is one of the dependencies we refer to and we don't have the right to modify it, is it impossible to use distinct () to customize the filtering of objects based on whether some elements are the same?
Use wrapper
On one of stackoverflow's answers, we can find a feasible way: use wrapper.
Suppose that in a dependency (we have no right to modify this class), the definition of VideoInfo is as follows:
@ AllArgsConstructor@NoArgsConstructor@ToStringpublic class VideoInfo {@ Getter String id; int width; int height;}
Using the wrapper idea just now, write the program as follows (of course, for the sake of the operational nature of the program, VideoInfo is put in, assuming that it cannot be modified and no methods can be added to it):
Package example.mystream;import lombok.AllArgsConstructor;import lombok.Getter;import lombok.NoArgsConstructor;import lombok.ToString;import java.util.Arrays;import java.util.List;import java.util.Map;import java.util.stream.Collectors;public class DistinctByWrapper {private static class VideoInfoWrapper {private final VideoInfo videoInfo; public VideoInfoWrapper (VideoInfo videoInfo) {this.videoInfo = videoInfo;} public VideoInfo unwrap () {return videoInfo } @ Override public boolean equals (Object obj) {if (! (obj instanceof VideoInfo)) {return false;} VideoInfo vi = (VideoInfo) obj; return videoInfo.id.equals (vi.id) & & videoInfo.width = = vi.width & & videoInfo.height = = vi.height } @ Override public int hashCode () {int n = 31; n = n * 31 + videoInfo.id.hashCode (); n = n * 31 + videoInfo.height; n = n * 31 + videoInfo.width; return n }} public static void main (String [] args) {List list = Arrays.asList (new VideoInfo ("123,1,2), new VideoInfo (" 456,4,5), new VideoInfo ("123,1,2) / / VideoInfo-- map ()-- > VideoInfoWrapper-- > distinct (): VideoInfoWrapper-- map ()-- > VideoInfo Map id2VideoInfo = list.stream () .map (VideoInfoWrapper::new). Distinct (). Map (VideoInfoWrapper::unwrap) .requests (Collectors.toMap (VideoInfo::getId, x-> x, (oldValue) NewValue)-> newValue)) Id2VideoInfo.forEach ((x, y)-> System.out.println ("));}} / * Assume that VideoInfo is a class that we can't modify * / @ AllArgsConstructor@NoArgsConstructor@ToStringclass VideoInfo {@ Getter String id; int width; int height;}
The whole idea of wrapper is to construct another class VideoInfoWrapper and add hashCode () and equals () to wrapper so that wrapper objects can be customized and filtered according to custom rules.
Search the official account of Java bosom friend, reply to "back-end interview" and send you a treasure book of Java interview questions. Pdf
We can't customize filtering VideoInfo, but we can customize filtering VideoInfoWrapper!
What you need to do after that is to convert all VideoInfo to VideoInfoWrapper, then filter out some VideoInfoWrapper, and then transfer the remaining VideoInfoWrapper back to VideoInfo, so as to achieve the purpose of filtering VideoInfo. Very ingenious!
Replace distinct () with "filter () + custom function"
A more subtle implementation is to customize a function:
Private static Predicate distinctByKey (Function
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.