I wrote a couple of interview questions a few days ago, including why overwriting equals usually Overrides HashCode? , some friends may say that this kind of interview questions are “the interview makes the rocket, the job turns the screw”. Admittedly, this is true of some interview questions.

But today, because understand the knowledge in this article unexpectedly in front of the big guy show a, help big guy solve the question, but also in return for a tomorrow please eat “oral check”, haha ~~

Here’s a look at some of the weirdest problems that big Guy encountered and how they were solved.

Big man’s doubts

The big guy wrote a piece of code in the project that looked something like this:

List<ProjectId> list = new ArrayList<>(); // omit add data operation List<DeviceModel> models = list.stream().map(ProjectId::getDeviceModel).distinct().collect(Collectors.toList()); System.out.println(models);Copy the code

As a result, the distinct() method in this code does not work and does not achieve the desired de-duplication.

But the big guy didn’t give up. First, he checked the method’s documentation:

Returns a stream consisting of the distinct elements (according to Object.equals(Object)) of this stream.
For ordered streams, the selection of distinct elements is stable (for duplicated elements, the element appearing first in the encounter order is preserved.) For unordered streams, no stability guarantees are made.

This is a stateful intermediate operation.
Copy the code

= = = = = = = = = = = = = =

Big guy to solve the problem is worth us to learn a wave of first, before the big guy decided to give up finally, send me a message, ask interested to have a look. With such a strange phenomenon, how could we not investigate it?

solution

According to part of the code and implementation ideas sent by the boss, the whole simulated test program is supplemented and completed, two entity classes ProjectId and DeviceModel are created, and the equals method is rewritten (after communicating with the boss, he overwrites the equals method, and it is effective to use it alone).

DeviceModel entity class that simply overrides equals to compare field no for equality.

@Data
public class DeviceModel {

    private String no;

    @Override
    public String toString(){
        return no;
    }

    @Override
    public boolean equals(Object other) {

        if (this == other) {
            return true;
        }
        if (other == null || getClass() != other.getClass()) {
            return false;
        }

        return this.toString().equals(other.toString());
    }
}
Copy the code

ProjectId entity class that overrides equals,

@Data
public class ProjectId {

    private int id;

    private DeviceModel deviceModel;
}
Copy the code

Then, the test class is built:

public class Test { public static void main(String[] args) { List<ProjectId> list = new ArrayList<>(); DeviceModel device1 = new DeviceModel(); device1.setNo("1"); ProjectId projectId1 = new ProjectId(); projectId1.setDeviceModel(device1); projectId1.setId(1); list.add(projectId1); DeviceModel device2 = new DeviceModel(); device2.setNo("1"); ProjectId projectId2 = new ProjectId(); projectId2.setDeviceModel(device2); projectId2.setId(1); list.add(projectId2); DeviceModel device3 = new DeviceModel(); device3.setNo("2"); ProjectId projectId3 = new ProjectId(); projectId3.setDeviceModel(device3); projectId3.setId(2); list.add(projectId3); List<DeviceModel> models = list.stream().map(ProjectId::getDeviceModel).distinct().collect(Collectors.toList()); System.out.println(models); }}Copy the code

We first build a set of data, then override the equals method by making Device1 the same as device2’s no property. In theory, they should be equal. The Device3 object is used for comparison.

Execute the above program and the console prints the following:

[1, 1, 2]
Copy the code

It does restore the big guy bug and wonder why it happened. But now that the bug has resurfaced, solving it is a relatively simple matter.

It’s ok to use the for loop:

List<DeviceModel> results = new ArrayList<>(); for (DeviceModel deviceModel : list.stream().map(ProjectId::getDeviceModel).collect(Collectors.toList())) { if (! results.contains(deviceModel)) { results.add(deviceModel); } } System.out.println(results);Copy the code

This implementation also happens to serve as a comparison.

Troubleshoot problems

Debug was also the first thing I thought about when I was trying to troubleshoot the problem, but again I didn’t go to equals.

A closer look at the code shows that the map operation is used during Stream processing. As mentioned in the previous article, to determine whether an object already exists in Map, the hash value of the key is used to locate the corresponding array subscript. If the Entry in this position has no value, it is directly saved. If the values already exist, compare them using equals.

Did you override equals instead of hashCode? So we added the hashCode method to the DeviceModel class:

@override public int hashCode() {return objects.hash (no); }Copy the code

Execute again, test the method, and find that it can be successfully removed. Apparently, the big guy’s mistake was to override equals by violating the principle that if a class’s equals methods are equal, then their Hashcode methods must be equal. The failure to override the HashCode method results in a violation of this principle. As a result, puzzling problems arise when using maps implicitly.

subsequent

After all this trouble, the problem was finally solved. And you can see why overwriting equals means overwriting hashCode. Why does the list. Contains method not contain this problem?

Because the underlying structure of a List is an array, unlike a Map, keys are first compared through hash processing to improve efficiency. Take a quick look at the core implementation of the Contains method in ArrayList:

public int indexOf(Object o) {
    if (o == null) {
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}
Copy the code

You can see that if the object is not null, the equals method is called through the loop.

summary

Through this article, I told a story to help the boss locate the problem. Thanks to the boss for giving me a good writing material. During this period, there are many contents worth learning and drawing lessons from. From the side, some interview questions do have merit. If you think you’re just building a plane, chances are you’re not jumping into a pit in practice.

Finally, the big guy is because he did not have a good look at the public number of the last article, just fell into the pit of [wu face][wu face][wu face]. Therefore, indirectly to explain the contents of the public number can still provide some help to you, interested in paying attention to it. You are also welcome to add your wechat friends directly to discuss some interesting technical issues.

The interview series

  • Interview questions: Talk about TCP sticky, unpack, and Solutions
  • Interview question: Why does overriding equals usually override hashCode?
  • Interviewer: How to find the longest string in a string without repetition?
  • Don’t Understand Java generics? Just use this article to make sure you have a good interview answer.
  • Interview Question: 8 Ways to Invert a string. What can you Think of?

Program new horizon

\

The public account “program new vision”, a platform for simultaneous improvement of soft power and hard technology, provides massive information