Optimizing your equals() methods with Pattern Matching - JEP Cafe #21
Estimated read time: 1:20
Summary
In this episode of JEP Cafe, the focus is on optimizing the equals() methods in Java using pattern matching. The creator delves into the pitfalls of relying solely on IDE-generated methods, and how potentially better-performing patterns exist that developers may not be aware of. The session explains the importance of well-designed equals() and hashCode() methods, especially when considering performance impacts. Pattern matching, instance checking, and predictive branching are explored as strategies for optimizing these methods. Various examples and benchmarks show that, in many cases, traditional practices might lead to performance hits, and a more nuanced approach could yield better results. The creator emphasizes measuring performance accurately for specific use cases as crucial for making the right optimization choices.
Highlights
Using pattern matching can lead to more efficient equals() methods. 📊
Traditional instance checks can sometimes slow down performance. ⏳
Understanding and leveraging JEP 394 can optimize code significantly. 🤓
Measure your specific performance needs before deciding on an optimization strategy. ⚖️
Predictive branching can cause unexpected performance hits if not accounted for. 🚦
Key Takeaways
Don't just rely on your IDE for equals() and hashCode() methods, explore pattern matching for optimization! 🌟
A well-designed hashCode() is crucial for performance, especially avoiding common pitfalls like returning constant values. 🚀
Instance checking isn't always beneficial; in some cases, it can degrade performance, especially with modern setups. 🎯
Predictive branching impacts performance; understanding it can help optimize equals() designs. 🔍
Pattern matching can outperform traditional approaches, especially with the recent Java advancements. ☕
Overview
In this engaging episode of JEP Cafe, the creator takes us through the intricacies of optimizing equals() methods in Java, shedding light on the often-overlooked performance impacts that poorly designed methods can have. The journey begins with a deeper look into the equals() and hashCode() methods — the foundational companions to any Java class.
The discussion highlights the power of pattern matching introduced in JEP 394 and how it can be leveraged effectively to write performant Java code. We're guided through practical examples, comparing traditional instance checks with the modern, streamlined approaches pattern matching offers. The benchmarks provided show compelling evidence for reconsidering the default code we might take for granted.
Amidst the Java jibber-jabber, the creator brings out a critical message: the importance of measuring performance based on real-world use cases. As technology evolves, so too should our approaches. The episode wraps up with a call to rethink old patterns and embrace more efficient options, like those available in Java 21 with pattern matching, to ensure your Java applications run smoother and faster.
Chapters
00:00 - 01:00: Introduction to equals() Methods The chapter titled 'Introduction to equals() Methods' begins with an engaging opening where the speaker humorously asks if the audience is interested in a coffee break. This sets a casual and relatable tone for the discussion that follows. The chapter is focused on the design aspects of equals() methods in programming. It emphasizes their pervasive use in software development, pointing out that these methods are often utilized even without developers being fully aware of it. This introduction sets the stage for a deeper exploration into best practices and considerations when implementing equals() methods.
01:00 - 03:00: Generating equals() and hashCode() in IDEs The chapter discusses the practice of using IDEs (Integrated Development Environments) to generate the equals() and hashCode() methods in programming. It argues against manually writing these methods due to the tediousness and suggests that IDEs can automate this task effectively. The chapter further explores whether IDEs produce optimal code patterns or if there are better alternatives. It promises to address these questions and more, including concepts like performance optimization, instance checking, pattern matching with instanceof, and predictive branching, using JMH benchmarks.
03:00 - 05:00: Understanding HashSet and HashCode The chapter titled 'Understanding HashSet and HashCode' focuses on the significance and implementation of the equals() and hashCode() methods in programming. It emphasizes the importance of understanding these methods for designing simple and efficient equals() methods. The chapter also touches upon the common practice of generating these methods using an Integrated Development Environment (IDE), alongside the toString() method. While automatic generation is beneficial, especially for the hashCode() method, having a deep understanding of their workings can greatly enhance code performance.
05:00 - 09:00: Performance Issues with equals() Methods The chapter discusses the challenges of writing effective hashCode() methods. It emphasizes the complexity involved in creating a good hashCode() and warns about the unexpected negative impacts a poorly implemented hashCode() can have on application performance. The chapter advises using generated hashCode() methods unless the user is experienced enough to handle custom implementations and begins with a practical example to illustrate these points.
09:00 - 14:30: Impact of Predictive Branching This chapter discusses the potential pitfalls of implementing a poorly designed hashCode() function in programming. The example given illustrates a point record with a default hashCode() provided by JDK and contrasts it with a 'dumb' implementation that always returns 0. This highlights the importance of designing efficient and sensible hashing methods.
14:30 - 29:00: Testing equals() Methods with Various Data Sets In this chapter, the focus is on testing the equals() method across various data sets. It highlights the often overlooked but essential rule in Java that if two objects are considered equal by the equals() method, they must have the same hash code. This ensures consistent behavior when these objects are stored in hash-based collections like HashSet. The chapter further explores the practical implementation of this rule by using Java 19's newHashSet() factory method, which simplifies the creation of HashSet by determining the necessary initial capacity to store all incoming objects efficiently. This discussion serves as a reminder that while certain implementation tricks might appear unconventional or redundant, they are crucial for aligning with Java's specifications and ensuring robust application performance.
29:00 - 32:00: Conclusion: Importance of Measuring Performance In the concluding chapter titled 'Conclusion: Importance of Measuring Performance', the importance of evaluating performance in programming is highlighted. The discussion involves measuring the execution time of a piece of code involving a `Point` class with a default `hashCode()` implementation compared to a poorly implemented one. The results demonstrate a significant performance deterioration, epitomized by a 20-fold performance decline when using a poorly crafted hash code, underscoring the necessity of efficient coding practices.
32:00 - 35:00: Evaluating IDE-Generated Code The chapter discusses the inner workings of a HashSet, specifically how it wraps around a HashMap. It explains that a HashMap begins by computing the hash code for each key to determine the storage bucket. The chapter also covers the issue of hash code collisions, which can result in a linked list that may evolve into a red-black tree if it becomes excessively long.
35:00 - 33:00: Final Thoughts on Pattern Matching in Java 21 The chapter emphasizes the complexity and performance considerations involved in managing code, particularly when using records and classes in Java 21. It advises on the best practices such as avoiding manual alteration of records and relying on integrated development environments (IDEs) to automatically generate hashCode() methods for plain classes to ensure safety and efficiency.
Optimizing your equals() methods with Pattern Matching - JEP Cafe #21 Transcription
00:00 - 00:30 Do you fell like a coffee break? So do I!
What about we talk about the design of your equals() methods. Methods that you use
all the time, even when you don't know it,
00:30 - 01:00 and that you never write yourself. First because
it's boring, and second because your IDE can do it for you. Is your IDE good at generating this
method? Are there better patterns of code than the one it gives you? These are the questions
I would like to answer in this JEP Café.
So, you will learn about performance, instance
checking, pattern matching for instanceof, predictive branching, all these
good things with JMH benchmarks,
01:00 - 01:30 that, once again, will help you design
simple and performant equals() methods.
The equals() and the hashCode() methods
are probably the first methods you learn to generate in your IDE, along with
the toString() method, of course.
And, dont get me wrong, generating them is a
good thing, especially for the hashCode() method,
01:30 - 02:00 because writing a good hashCode()
method is actually not that easy.
And because it is used in places
where you may not expect it, a bad hashCode() implementation can lead to
major performance hits in your application.
So yes, you should definitely use
this generated hashCode() method, unless you know what you are doing.
I just would like to start with a first example
02:00 - 02:30 to show you what could go wrong with a dumb
hashCode() implementation. And when I say dumb...
Suppose you have a point record, plain and simple.
You do not touch this class, so the hashCode() implementation is provided by the JDK. Good.
And then, let us write this completely dumb implementation of a hashCode()
method, that always return 0.
02:30 - 03:00 I know, it's completely stupid, never do that
in your application, but this implementation is actually OK with the spec: two points that
are equal, do have the same hash code.
Let us try to put these points in a HashSet,
along with the following code. And bytheway, we are using this factory method newHashSet(),
that was added in Java 19, so, that you can use it in 21. It creates a large enough hash
set to hold all your values, without having to
03:00 - 03:30 resize it on the fly. Neat, I love this one.
So let us measure the time it takes to run this code in both cases: the Point with
the default hashCode() implementation, and the dumb Point that has this stupid one.
And as you can see, the performance hit is quite hard! That's a factor of 20 between
a good hash code and a stupid hash code.
03:30 - 04:00 Why is that? Well, as you probably know, a
HashSet is actually wrapping a HashMap. And a HashMap first computes the hash code of your
key to decide in which bucket it will store your entry. If all the hash codes are the same,
then you will get collisions, leading to the construction of linked list, that will become
a red black tree, if this list is too long.
04:00 - 04:30 This is a code that is much more complex and
costly to run than the simple storing of an entry in an array. Thus the performance hit.
So I hope this example convinced you: if you are using records, do not touch
anything, and for your plain classes, let your IDE generate your hashCode() method
for you. It's much safer, most of the time.
04:30 - 05:00 Let us go back to our initial subject,
and talk about the equals() method.
There is actually a number of
generators that you can use, and I would like to first take a look at the
code they give you, to compare these patterns.
In all the rest, we are going to use
our good friend the record Point, with two components x and y, of type int.
This is a very simple example, and comparing two instances of this record is quite fast:
it's only about comparing two int values,
05:00 - 05:30 which is, yes, fast. You may have objects in
your application, with more complex states, that will be more costly to compare.
First, we have the default generators of the Eclipse IDE and the IntelliJ
IDE, which are slightly different.
Here is the generated code by both of them.
And, as you can see, it's mostly the same,
05:30 - 06:00 organized in a different way.
It starts with the comparison of this and other with ==, to quickly exit if the objects
are actually the same. This is an instance check, and is seen as an optimization that almost all
the generators are using. Then it compares the classes, also with ==, because you know that there
is only one instance of a given class in your application. And then the cast and the comparison
of the internal state of your objects.
06:00 - 06:30 Then you can also use the Guava
generator, which is also very similar, just organized in a different way.
And you can see that all these generated codes compare the instances with ==,
then the classes, then they do a cast, and compare the internal state of your objects.
And then you can check the Apache Commons Lang 3 generator. The code is organized in the
same way: first compare the instances,
06:30 - 07:00 then the class, and then the internal state of
your object, but this time using a builder.
On the one hand, I find this code nice and
readable, but on the other hand, I suspect that it will not be great performance wise.
Creating this builder is costly, much more costly than comparing primitive types with ==.
If performance is not an issue for you, and you want to favor the readability of your code, this
is definitely a solution that you can consider.
07:00 - 07:30 Other than that, I'm not sure that
this solution is very interesting.
And because we also need to check your basic
101 equals() method, we are going to a plain instanceof to the lot, and the same, with
this instance check optimization first.
And because this is Java 21 we
are going to add two more.
07:30 - 08:00 First the plain equals() method that is
present in your records. You do not see this implementation because it is added for
you by the compiler, but it's still there.
And then the implementation advertised
in the JEP about Pattern Matching for instanceof. This is the JEP 394, delivered
in the JDK 16, and written by Gavin Bierman.
We can take a look at this implementation
now, because it's really interesting.
08:00 - 08:30 As you can see, there is one major difference
between this implementation and all the others, including bytheway the default implementation
you currently get when you create a record, which is: it does not check if the instances
are the same or not with ==. And it does not check if the classes are the same or not
neither. It directly checks for the pattern.
08:30 - 09:00 Could this be a mistake? A missing
optimization, overlooked by Gavin Bierman that wrote the JEP? Or is it actually
the right way to write your equals() method?
So let us add this method from the JEP, and also
modify it by adding this instance check before, to see if this is an optimization or not.
And, bytheway, we are going to use the Record Pattern, because this is what we have in 21,
and because our Point class is a record.
09:00 - 09:30 The most important question is: is this
instance check with == an optimization or not?
And if it is really an optimization,
why does the code in the JEP doesn't use it? Why would it give up on what
looks like an obvious optimization?
Well, you know the drill, right? When it
comes to performance, there is only one principle to follow. Measure, don't guess!
Before we move on, and examine all this, I
09:30 - 10:00 would like to talk about a very important point in
computing, which is called predictive branching.
You may be wondering why we are taking about that
now. In fact, it turns out that it actually plays a key role in all this, so we are going to
spend a few minutes to explain what it is.
Predictive branching is a nifty feature
that may make the execution of your tests
10:00 - 10:30 much faster. It's not that it doesn't
execute your test, because it does, it needs to execute it. It's more that, it makes a
bet on the outcome of this test, starts to execute one of the branch of your if statement, and if
the bet is a win, carries on with your code.
Which is great, because if it works, then
it can execute your code much faster.
10:30 - 11:00 But if it doesn't, then it becomes a
performance hit, because it needs to rollback what it did and take the other branch.
So let us see some benches on this topic.
First, let us run this code. We are just
summing all the int values of an array of size 400. There is no test in it, and
it runs in about 72ns "on my machine".
Then we are going to suppose that all the
elements of this array are even integers,
11:00 - 11:30 and we just add a test on that. Note that
the result of this test is always true.
This gives us an idea of the
cost of testing each element.
On the overall, running this code
takes 110ns "on my machine", which is about 50% more than not running the test.
Now we are going to run three more benches, to evaluate the cost of a failing test.
Let us make the last element of this array
11:30 - 12:00 an odd integer. So the last test of the series is
failing, when all the other tests are a success.
And you can see that having just one test
that is failing, when all the others are successful, is actually quite costly.
You could expect that the performance should be the same, because it is the same test, just a
different outcome. And we are executing 400 tests
12:00 - 12:30 in 110ns, so the throughput of this code is about
0,27ns per operation. But because the result is not what the optimizer expected, the performance
hit for this single failing test is about 7ns.
Which is quite a big performance hit. This
single failing test is about 30 times slower
12:30 - 13:00 than all the others. This is the cost
of a failing predictive branching.
Now, let us move this failing test from the end of
the array to the middle of the array. And you can see that the performance hit this time is almost
doubled. It is now about 13ns instead of 7.
It looks like we are paying for the
transition from one state to the other.
13:00 - 13:30 The first state being: the expected outcome of
the next test is true, and the second state being: the expected outcome is false.
We can see that by running the same bench with two consecutive odd integers at
the middle of the array instead of one.
If we measure this performance, we can see
that it is the same as the one where we have only one odd integer, about 123ns. That
confirms it: what is coslty is to fail the
13:30 - 14:00 predictive branching optimization, that is,
to transition from one state to the other.
Now we can try one last test: having
two odd integers spread apart in our array. One at the first third, and
another one at the second third.
And you can see that the performance hit this
time is even higher. Not as high as expected,
14:00 - 14:30 if we were paying for transitions the
performance hit would have been 138ns, and it's only 128ns, but still higher
than having two odd consecutive integers.
So this gives you an idea of the cost of
having a test with an outcome that is not what this "predictive branching"
feature would have expected.
14:30 - 15:00 Actually testing elements and having an
outcome that is completely random is costly, you should try to avoid being in
such a situation, when you can.
Now the question is: how is this related to
the design of your equals() methods? Well, bear with me, this is what you
are going to see, right now.
How can we see if predictive branching has an
impact on the performance of an equals() method.
15:00 - 15:30 Well, there is only one way to find
out, create the right set of data, and run all the equals() methods we have on it.
All the use cases we are going to see are the same: we have a list of 400 Point instances.
Point being the record we created earlier. We just loop over this list, and check if a given
object is equal to the objects of this list.
15:30 - 16:00 This is the code we are running,
on different sets of data.
Bytheway this blackhole object is given to you
by JMH, and you can use it to make sure that all the steps of your computation are actually
executed, that HotSpot will not "optimize" things and remove some of your code because it
finds that it's actually useless, which is always the case when you are running benchmarks.
You absolutely want to avoid that situation,
16:00 - 16:30 this blackhole trick is very useful for that.
The first use case runs on a list of points that are all the same instance of the Point
record. So yes, they are all the same object. I'm not sure that you will see that a lot in
applications, but... we need to start somewhere.
And you can see that the result is what we
expected: all the methods that first checked
16:30 - 17:00 for the instance equality perform better, and
the pattern matching one is lagging behind. So in that case, performing this instance check is
a good idea: you will get better performance.
Let us move on to our next use case: we now have
a list of objects, still 400, and this time they are all different instances, but they are all
carrying the same state. So they are all equal.
17:00 - 17:30 And the object used to for the comparison is also
equal to all of them, but a different instance.
All the calls to this == are now false, the
class comparisons are true, so all the methods are executing the code that is comparing the
internal state of the objects. And because the state is the same, they have to execute all
the instructions of this state comparison.
17:30 - 18:00 And this time the result is different. The
Commons Lang pattern is lagging behind, as expected, obviously the goal of the design
proposed by Commons Lang is not performance but readability, which is perfectly OK.
All the others are doing roughly the same.
But what's interesting is two things. First, using
a simple pattern matching, without this instance check before, actually performs better than
the rest, by about 40%, which is significant.
18:00 - 18:30 Even if you compare the plain instanceof with
pattern matching, you can see that it is slower by about 10%, which is also significant.
So pattern matching is actually doing very well in that situation.
And because neither instanceof nor the pattern matching solutions are executing
the instance check, they save on this test, which is executed by all the others.
So in the case of a list of objects that are
18:30 - 19:00 different but that carry the same state, calling
this instance check does not optimize anything, it is actually a performance hit.
For the next use case we are going to use a list of objects of the same type, once
again, but this time they are all different, and the object we compare is also different.
We expect a result that is similar to the
19:00 - 19:30 previous one: the instance check is always
false, the class check is always true, so the optimization there is in fact an
overhead. And indeed, the results are similar.
There is one result that is interesting in this
table, which the instanceof versus the pattern matching one. Instanceof is better, with 142ns,
and Pattern Matching is behind with 178ns.
19:30 - 20:00 You may be wondering why the result is different.
Well, we are using a record pattern in this bench, that creates the two pattern variables
x and y, by calling the accessors of this record. These accessors need to be
called to create the pattern variables, because there may be some code in it that needs to
be executed. Like a defensive copy for instance.
So not calling the accessors would
actually be a backdoor to access
20:00 - 20:30 the internal state carried by your records,
without any security checks or validations.
Then we use x for the comparison,
that is failing, so y is not used.
When you are just checking
for the type with instanceof, no pattern variable is created. You create them
lazily. In that case, you create x, and not y.
20:30 - 21:00 So in the case where you have
accessors that are costly to execute, using a plain type pattern instead of a record
pattern may actually be more efficient.
For the last use case of this first series,
we are going to use a list of other objects, of a different type. So the instance
check is always false, the class check is also always false, and the rest of the
code is not executed. So, on the one hand,
21:00 - 21:30 we are executing this code, and on the other
hand, the instanceof is also always false, and the pattern variables are not created.
So which one will perform better?
Well, the benches give us a clear win for
pattern matching, with a factor of almost 2: 180ns for the instance check solutions, and
95ns for the pattern matching and instanceof
21:30 - 22:00 ones. Checking for the instance equality is always
false, so it's an overhead, adding the check for the class equality makes it more expensive than
calling instanceof or using pattern matching.
And as we can expect, adding this instance
check before executing an instanceof or a pattern matching, actually degrades your
performance, instead of improving them.
22:00 - 22:30 At this point, you can see that there is only one
use case where conducting an instance check is an optimization, which is, you are comparing
objects that are all the same instance.
I'm not sure that this is the most common use
case you'll come across in your applications. But so far, we have'nt seen any perdictive branching
hit, because all the tests had the same results.
Now, what we are going to do, it take these
data sets again, and add some glitches in them:
22:30 - 23:00 objects that are different, to see the results.
So let us take a look at the variations. We are going to check more use cases, still with
a list of 400 elements, but we are going to add different objects in it, glitches, to trigger
failures in the predictive branching algorithm.
What is a glitch? Well, in a list where all the
elements are the same as the one you compare
23:00 - 23:30 them to, a glitch is a different object. In
a list where all the elements are different, a glitch is an element that is the same.
Is it the same instance, or does it just carry the same state? Well, we need to check
both, and this is what we are going to do.
How are we going to distribute
these glitches in a list?
We are going to distribute them evenly,
in the middle of the list. No glitch
23:30 - 24:00 at the beginning, nor at the end.
So one glitch makes it in the middle, two glitches are a one third and two third,
and 10 glitches, well, it looks like that.
Now the use cases we are going
to consider are the following.
First use case: a list with objects that
are all the same instance, and the glitch is a different object of the same type.
Second use case: a list with objects that
24:00 - 24:30 are all different instances, but carrying
the same state, so they are equal.
And the glitch is also an object that
is different, and of the same type.
Third case, we are going to put different
objects in your list, of the same type.
And we are going to add two
different kind of glitches.
First, an object that is the same
instance, and second, an object that is equal, but a different instance.
And the last case is the same, but with a list
24:30 - 25:00 that contains objects of different types.
That makes four use cases to look at, with two variations for two of them.
When you have a list with instances that are all the same, adding glitches just makes the situation
even worse for pattern matching. Checking for the instance gives you the result immediately. And
even if the glitches degrade the performance,
25:00 - 25:30 it still much better than not doing it.
So that situation is a clear win for all the solutions that are doing the instance
check. Now the real question is: do you see this use case that often in
your applications? Well, I'm not so sure.
In the next use case, the instances are
all different, but equal. And the glitch is a different object, of the same type.
Pattern matching was performing better with
25:30 - 26:00 no glitch, and it is still the case, even if
processing this data takes a little longer.
This use case is a win for pattern
matching and plain instanceof.
What I find interesting in this bench, is that you
can very clearly see that adding an instance check on the pattern matching or the plain instanceof
solutions actually degrades your performance. You may be thinking that this simple instance
check is almost free. Well, clearly it's not.
26:00 - 26:30 In the two next series, we have a list with
objects of the same type, but different. And in this one the glitch is an object that
is the always the same instance, and the same instance as the object we check it against.
That should favor the solutions that are doing the instance check, right? Well, it does not. And the
reason if that predictive branching is messing up
26:30 - 27:00 with this. On the one hand, you have an instance
check that is mostly failing, and sometimes it's a success. And you saw on the predictive
branching examples, that it is a situation that you do not want to find yourself into.
And on the other hand, you have a pattern matching or an instanceof, with a type
check somewhere, that is never failing. A situation that is much better.
So in this series, where you may be
27:00 - 27:30 thinking that doing the instance check would be
an optimization, it is actually something that, once again degrades your performance.
And if you check the pattern matching and the instanceof lines, and compare them with
the same solution with an instance check, you can see that doing this instance check is again
a performance hit, by a factor of about 50%.
27:30 - 28:00 The following series is the same as the previous
one, the only difference is that the glitch is a different instance that carries the same state.
That is an object that is equal. The most interesting thing is that if you compare these
numbers with the previous series, you can see that the instance check solutions are actually
performing better. Which may seems unexpected, because this instance check is always failing in
this series. Well, it's better, because predictive
28:00 - 28:30 branching is playing with you in this case, and
it was playing against you in the previous one.
The performance of pattern matching and plain
instanceof is the same as the previous series, which is also expected, and much better than
all the other solutions, by more that 50%.
And the degradation due to the instance check
is also roughly the same on the pattern matching and the plain instanceof solutions.
The last two series are about comparing
28:30 - 29:00 objects of different types, with
a glitch that is the same object, and then a different object that is equal.
So for this first case, the instance check is mostly failing, and sometimes
a success, which is not good.
Pattern matching is mostly performing
better, and adding an instance check is always a degradation of your performance.
When the amount of glitches becomes higher, some solutions are doing better than pattern
matching, that is slowly degrading over
29:00 - 29:30 time. But, actually up to a certain point.
Be careful, because if you continue to increase the amount of glitches, pattern matching and plain
instanceof are actually performing better again.
So just be aware of that, and carefully
bench your application with the actual data your are working on.
And the last series is the
29:30 - 30:00 same as the previous one, but instead of
having an identical instance, the instances are different, still carrying the same state.
It does not make much difference, the results are roughly the same as the previous series.
The biggest difference is actually the degradation of the record case.
And, once again, you can see that adding the instance check degrades the performance
for both pattern matching and instanceof, that becomes better again as the
number of glitches increases.
30:00 - 30:30 So, what can you make of all this? How all
this can help you write a better code?
If there is one conclusion you should draw,
it is the following: measure, don't guess.
You saw that there are many cases where checking
for the instance of an object actually degrades your performances. It's actually what is
happenning in most of the cases I showed you.
30:30 - 31:00 But as usual, choosing the right solution is
really about understanding what are your most common use cases. What kind of objects are you
comparing? Are they all of the same type? Are they all different, or sometimes you are comparing
identical instances? What it is the amount of failing tests you are expecting? Are they mostly
failing, mostly successful, or evenly balanced?
31:00 - 31:30 In short: what is your application doing, how is
it working? What kind of data are you processing?
As a rule of thumb, in most of the cases
I just showed you, doing this instance check is actually a performance hit.
The only use case we saw where it is performing better is when all your objects
are the same instance. In all the others,
31:30 - 32:00 not doing it is probably your best bet.
Should you rely on the code your IDE can generate for you?
Well, to be honest I have mixed feelings about it. These patterns
haven't changed much in years. Like 15 years, maybe even more. And, what was true perfomance
wise 15 or 20 years ago might have changed now.
32:00 - 32:30 Maybe thinking about these patterns
of code again could be a good idea.
But if you are lucky enough to have
an application running on Java 21, pattern matching is definitely your best choice.
If you have complex accessors, type pattern is probably your best choice, and if not, record
pattern will give you a simpler solution, with a more readable code, and
very good performance wise.
32:30 - 33:00 And with this I'm out of coffee, so,
that's it for today, talk to you soon!