Reflections on the Distinct () in Java8 __java

Source: Internet
Author: User
Tags static class wrapper

Another conjecture of the list's turn map uses distinct to give the list a direct use of the distinct failure reason distinct dependent on equals overriding equals points of attention Extension conclusion extension assumption class is someone else's can not modify the use of wrapper Replace distinct with filter custom function

another conjecture of the list's turn map

Java8 uses lambda expressions for functional programming to make it easy to manipulate collections. A more common operation is to convert a list into a map, typically using the collectors Tomap () method for conversion. A more common problem is that when the list contains the same elements, an exception is thrown if you do not specify which one to take. Therefore, this designation is required. Of course, another overloaded method that uses Tomap () can be specified directly. Here, we want to discuss another approach: before you do a map, you can use distinct () to filter out the repeating elements of the list, and then turn to the map without having to consider the problem of repeating elements. What is not well known about the lambda can be poked here; the Tomap () method of the list-map is not very well known to poke here; a specific example of the Tomap () of the list-turn map can be poked here. use DISTINCT () to weigh the list Direct use of distinct (), failure

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; /** * @author Liuhaibo on 2017/11/16/public class Listtomap {@AllArgsConstructor @NoArgsConstructor @To
        String private static Class Videoinfo {@Getter string id;
        int width;
    int height; public static void Main (String [] args) {list<videoinfo> 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<string, videoinfo> id2videoinfo = List.stream (). Collec T (Collectors.tomap (Videoinfo::getid, x-> x, (OldValue, newvalue)-> newval

        UE)); System.out.println ("No DuplicaTed1: ");

        Id2videoinfo.foreach ((x, y)-> System.out.println ("<" + x + "," + y + ">")); Handle duplicated data using distinct (), before Tomap () map<string, videoinfo> Id2videoinfo2 = List.strea

        M (). Distinct (). Collect (Collectors.tomap (Videoinfo::getid, x-> x));
        System.out.println ("No Duplicated2:");
    Id2videoinfo2.foreach ((x, y)-> System.out.println ("<" + x + "," + y + ">")); }
}

There are a total of three elements in the

list, two of which we think are duplicates. The first conversion uses TOMAP () to directly specify the processing of the duplicate key, so it can be converted to map as normal. The second conversion is to go to the list before the weight, and then converted to map, the result is still failed, throw the illegalstateexception, so distinct () should be failed.

No Duplicated1: <123, Listtomap.videoinfo (id=123, Width=1, height=2) > <456, Listtomap.videoinfo (id=456, Width =4, height=5) > 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.HashMa P.merge (hashmap.java:1253) at java.util.stream.collectors.lambda$tomap$58 (collectors.java:1320) at Java.util.strea M.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.Abstr Actpipeline.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.u Til.stream.AbstractPipeline.evaluate (Abstractpipeline.java:234) at Java.util.stream.ReferencePipeline.collect (referencepipeline.java:499) at Example.mystream.ListToMa P.main (listtomap.java:79)
reason: Distinct () dependent on equals ()

To view the distinct () API, you can see the following introduction:

Returns a stream consisting of the distinct elements (according to {@link object#equals (Object)}) is this stream.

Obviously, the distinct () is handled according to the Equals () method of the object when the object is being weighed. If our Videoinfo class does not overrride the Equals () method of the Superclass object, the object is used. But the Equals () method of object returns true only if two objects are exactly the same. And the effect we want is that as long as the Videoinfo id/width/height are the same, the two Videoinfo objects are considered the same. So we rewrite the Equals () method that belongs to Videoinfo. Considerations for Overriding equals ()

We designed the Videoinfo equals () as follows:

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

As a result, the two objects are identical as long as the three properties of the two Videoinfo objects are the same. Joy to run the program, still fail. Why

"Effective Java" is a good book, and even the father of Java, James Gosling, says this 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. Have to. There is no room for negotiation.

The overridden equals () must be made to meet the following criteria:
1. The value of the hashcode () must be the same for two objects equal to the Equals () comparison;
2. According to Equals () to compare, the unequal two objects, hashcode () can be the same value, but also can be different;
Because this is the Java rule, violating these rules will cause the Java program to run no longer normal.

Specific more details, we suggest that you read the original book, will benefit greatly. Highly recommended. Of course, if you do not have this book, you can first take a look at this chapter on the original book of the relevant section of the blog excerpt summary.

In the end, I designed the Hashcode () method of Videoinfo according to the Guide of God's book as follows:

        @Override public
        int hashcode () {
            int n =;
            n = n * + this.id.hashCode ();
            n = n * + this.height;
            n = n * + this.width;
            return n;
        }

Finally, distinct () successfully filtered the repeating elements in the list, and using both Tomap () to convert the list to map is fine:

No Duplicated1: 
<123, Listtomap.videoinfo (id=123, Width=1, height=2) >
<456, listtomap.videoinfo (ID =456, width=4, height=5) >
No Duplicated2: 
<123, Listtomap.videoinfo (id=123, Width=1, height=2) >
<456, Listtomap.videoinfo (id=456, width=4, height=5) >
Extended

Since distinct () is a comparison of calling Equals (), it is my understanding that the list's 3 elements need to be compared at least 3 times. That is not the call of equals () 3 times.

Add a sentence to the Equals () so you know. The added Equals () are 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: 
<123, Listtomap.videoinfo (id=123, Width=1, height=2) >
<456, listtomap.videoinfo (ID =456, width=4, height=5) >
<===> Invoke equals () ==> listtomap.videoinfo (id=123, width=1, height=2) vs. Listtomap.videoinfo (id=123, Width=1, height=2)
No Duplicated2: 
<123, Listtomap.videoinfo (id=123, width=1 , height=2) >
<456, Listtomap.videoinfo (id=456, width=4, height=5) >

Only one Equals () was called at the end of the discovery. Why not 3 times? Think carefully, depending on hashcode (), Hashcode () is the same case once, and the first and third elements of the list (both Videoinfo (id=123, Width=1, height=2) appear hashcode () The same situation.

So is it possible to assume that only when hashcode () returns the same hashcode, Equals () is invoked to make further judgments. If even hashcode () return hashcode are different, then you can assume that the two objects must be different.

Verification conjecture:

Change Hashcode () as follows:

        @Override public
        int hashcode () {return
            1;
        }

As a result, the hashcode () return values of all objects are the same. Of course, this is in line with the Java specification, because Java only stipulates that Equals () the same objects must have the same hashcode, but the hashcode of different objects may not be different.

Results:

No Duplicated1: 
<123, Listtomap.videoinfo (id=123, Width=1, height=2) >
<456, listtomap.videoinfo (ID =456, width=4, height=5) >
<===> 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: 
<123, Listtomap.videoinfo (id=123, Width=1, height=2) > <456, Listtomap.videoinfo (id=456, width=4,
HEIGHT=5) >

Sure enough, equals () was called three times. It seems true that only when the hashcode is the same will the equal () be called to further determine whether two objects are the same, and if Hashcode is not the same, two objects are clearly different. conjecture to be correct. conclusion The list transfer map recommends the use of Tomap (), and regardless of whether there will be duplication of problems, to specify a repeat rule of choice, without effort but benefit from infinity; use distinct () for a custom class, remembering to overwrite 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 many consequences. For example, when such an object is deposited into the HashMap, all objects are hashcode the same, and eventually all objects are stored in the same bucket of HashMap, which directly worsens the hashmap into a linked list. Thus the complexity of O (1) is rounded to O (n), and the performance is naturally greatly reduced. A good book is the ladder of the progress of an ape. --Gorky. Like "Effecctive Java".

Final Reference Program:

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; /** * @author Liuhaibo on 2017/11/16/public class Listtomap {@AllArgsConstructor @NoArgsConstructor @To
        String 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.hei
      Ght = = Vi.height;  /** * If equals () is override, Hashcode () must be override, too. * 1.
         If a 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 is affected by sequence of the fields; * 3. 2^5-1 = 31. So would be faster the multiplication, * because it can is replaced by bit-shifting:31 * i = (I &
         lt;< 5)-I.
            * @return */@Override public int hashcode () {int n = 31;
            n = n * + this.id.hashCode ();
            n = n * + this.height;
            n = n * + this.width;
        return n; The public static void main (String [] args) {list<videoinfo> List = arrays.aslist (New Videoinfo ("1

        1, 2), New Videoinfo ("456", 4, 5), New Videoinfo ("123", 1, 2));
       Preferred:handle duplicated data when Tomap () map<string, videoinfo> 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 ("<" + x + "," + y + ">")); Handle duplicated data using distinct (), before Tomap ()//Note this distinct () relies on equals () in the OBJEC T//If you override Equals (), hashcode () must is override together Map<string, videoinfo> Id2videoi

        Nfo2 = List.stream (). Distinct (). Collect (Collectors.tomap (Videoinfo::getid, x-> x));
        System.out.println ("No Duplicated2:");
    Id2videoinfo2.foreach ((x, y)-> System.out.println ("<" + x + "," + y + ">")); }
}
further expansion Suppose the class is someone else's and cannot be modified

Above, Videoinfo allows us to write our own classes, we can add equals () and Hashcode () methods. If Videoinfo is a class of the dependencies we are referencing, and we have no right to modify it, then is there no way to use distinct () to filter the objects according to the same elements? using wrapper

In one answer to StackOverflow, we can find a workable method: using wrapper.

Assuming that in a dependency (we do not have the right to modify the Class), the Videoinfo definition is as follows:

@AllArgsConstructor
@NoArgsConstructor
@ToString public
class Videoinfo {
    @Getter
    String ID;
    int width;
    int height;
}

Use just the wrapper idea, write the program as follows (of course, for the program's operational, or put the videoinfo in, assuming it is not modified, can not add any method):

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;

        /** * @author Liuhaibo on 2017/11/17/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 &&amp ;
        Videoinfo.height = = Vi.height; } @Override PubLIC int hashcode () {int n = 31;
            n = n * + videoInfo.id.hashCode ();
            n = n * + videoinfo.height;
            n = n * + videoinfo.width;
        return n; The public static void main (String [] args) {list<videoinfo> List = arrays.aslist (New Videoinfo ("1

        1, 2), New Videoinfo ("456", 4, 5), New Videoinfo ("123", 1, 2)); Videoinfo--map ()--> videoinfowrapper----> Distinct (): Videoinfowrapper--map ()--> videoinfo Ma

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.