当前位置:网站首页>Equals and hashcode
Equals and hashcode
2022-06-21 09:34:00 【tragically unhappy】
Java Medium equals and hashCode
Preface
Suppose you have a container that uses a hash function , When you create an object and put it in this container , You have to define hashCode Functions and equals Method . These two functions are used together for query operations in the hash container .
One 、equals standard
When you create a class , It automatically inherits from Object class , If you don't overwrite equals(), You will get object Object's equals() function . By default , This function compares the address of the object . So only when you compare the same object , To get true.
public class DefaultComparison {
private int i, j, k;
DefaultComparison(int i, int j, int k) {
this.i = i;
this.j = j;
this.k = k;
}
public static void main(String[] args) {
DefaultComparison
a = new DefaultComparison(1, 2, 3),
b = new DefaultComparison(1, 2, 3);
System.out.println(a == a);
System.out.println(a == b);
}
}
/* output: true false */
In this way equals Obviously not in line with our expectations , So we need to rewrite this method .
Java 7
The following example compares different types of Equality class . To avoid duplicate code , We used Factory function design pattern To achieve .
public interface EqualityFactory {
Equality make(int i, String s, double d);
}
EqualityFactory Interface provide make() Function to generate a Equality object , This is different EqualityFactory Can generate Equality Different subclasses .
Now define Equality , It contains three fields and a equals The function is used to satisfy the above conditions .
public class Equality {
protected int i;
protected String s;
protected double d;
public Equality(int i, String s, double d) {
this.i = i;
this.s = s;
this.d = d;
System.out.println("made 'Equality'");
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!(o instanceof Equality)) return false;
Equality equality = (Equality) o;
if (!Objects.equals(i, equality.i)) {
return false;
}
if (!Objects.equals(s, equality.s)) {
return false;
}
if (!Objects.equals(d, equality.d)) {
return false;
}
// return i == equality.i && Double.compare(equality.d, d) == 0 && Objects.equals(s, equality.s);
return true;
}
@Override
public int hashCode() {
return Objects.hash(i, s, d);
}
public void test(String descr, String expected, Object o) {
System.out.format("----Testing %s --%n" + "%s instanceof Equality: %s%n" +
"Excepted %s, got %s%n",
descr, descr, o instanceof Equality, expected, equals(o));
}
public static void testAll(EqualityFactory eqf) {
Equality
e = eqf.make(1, "Monty", 3.14),
eq = eqf.make(1, "Monty", 3.14),
neq = eqf.make(99, "Bob", 1.618);
e.test("null", "false", null);
e.test("same object", "true", e);
e.test("different type", "false", Integer.valueOf(99));
e.test("same values", "true", eq);
e.test("different values", "false", neq);
}
public static void main(String[] args) {
testAll(Equality::new);
}
}
/* output: made 'Equality' made 'Equality' made 'Equality' ----Testing null -- null instanceof Equality: false Excepted false, got false ----Testing same object -- same object instanceof Equality: true Excepted true, got true ----Testing different type -- different type instanceof Equality: false Excepted false, got false ----Testing same values -- same values instanceof Equality: true Excepted true, got true ----Testing different values -- different values instanceof Equality: true Excepted false, got false */
testAll() The comparison of all the different types of objects we expect is performed . It uses factories to create Equality object .
But the above equals() Functions are tedious , And we can simplify it into a canonical form , Please note that :
- instanceof Inspection reduces null The need for inspection .
- and this The comparison of is more than . A correctly written
equals()Function can compare itself correctly .
class Part {
String ss;
double dd;
Part(String ss, double dd) {
this.ss = ss;
this.dd = dd;
}
@Override
public boolean equals(Object o) {
/*if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Part part = (Part) o; return Double.compare(part.dd, dd) == 0 && Objects.equals(ss, part.ss);*/
return o instanceof Part &&
Objects.equals(ss, ((Part) o).ss) &&
Objects.equals(dd, ((Part) o).dd);
}
@Override
public int hashCode() {
return Objects.hash(ss, dd);
}
}
public class ComposedEquality extends SuccinctEquality{
Part part;
public ComposedEquality(int i, String s, double d) {
super(i, s, d);
part = new Part(s, d);
System.out.println("made 'ComposedEquality'");
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
ComposedEquality that = (ComposedEquality) o;
return Objects.equals(part, that.part);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), part);
}
public static void main(String[] args) {
Equality.testAll(ComposedEquality::new);
}
}
/* output: // There are three pairs here because they have been initialized three times made 'Equality' made 'SuccinctEquality' made 'ComposedEquality' made 'Equality' made 'SuccinctEquality' made 'ComposedEquality' made 'Equality' made 'SuccinctEquality' made 'ComposedEquality' ----Testing null -- null instanceof Equality: false Excepted false, got false ----Testing same object -- same object instanceof Equality: true Excepted true, got true ----Testing different type -- different type instanceof Equality: false Excepted false, got false ----Testing same values -- same values instanceof Equality: true Excepted true, got true ----Testing different values -- different values instanceof Equality: true Excepted false, got false */
1.1 Equality of different subclasses ( How to define equals)
Inheritance means that two objects of different subclasses can be equal when they are transformed upwards .
enum Size{
SMALL,MEDIUM,LARGE }
class Animal {
private static int counter = 0;
private final int id = counter++;
private final String name;
private final Size size;
Animal(String name, Size size) {
this.name = name;
this.size = size;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Animal animal = (Animal) o;
return //id == animal.id && //[1]
Objects.equals(name, animal.name) && size == animal.size;
}
@Override
public int hashCode() {
return Objects.hash(name, size);
// return Objects.hash(id, name, size);
}
@Override
public String toString() {
return String.format("%s[%d]: %s %s %x",
getClass().getSimpleName(), id, name, size, hashCode());
}
}
class Dog extends Animal {
Dog(String name, Size size) {
super(name, size);
}
}
class Pig extends Animal {
Pig(String name, Size size) {
super(name, size);
}
}
public class SubtypeEquality {
public static void main(String[] args) {
Set<Animal> pets = new HashSet<>();
pets.add(new Dog("Ralph", Size.MEDIUM));
pets.add(new Pig("Ralph", Size.MEDIUM));
pets.forEach(System.out::println);
}
}
/* output: Dog[0]: Ralph MEDIUM d1c09fb Pig[1]: Ralph MEDIUM d1c09fb */
We are in the base class Animal In the definition of equals() and hashCode(), And these two functions do not contain id Field . from equals() From the perspective of , It means We only care if it is Animal, Not its subclasses .
The two are the same . They have the same hashCode(), But because object No equals(), So both functions appear in HashSet in .
Two 、 Hashes and hashes
In the assembly , We use pre-defined classes as HashMap Key . The reason why we can do this , Because Predefined classes have the necessary wiring to enable their behavior to act correctly keys.
When you create your own class as HashMap When , One easy mistake to make is that you often forget the necessary connections . When this class is used as a key , There is no way to rewrite it hashcode(), So it inherits Object Method to calculate the hash code according to the address of the object , The object new The addresses are different .
public class Groundhog {
protected int number;
public Groundhog(int number) {
this.number = number;
}
@Override
public String toString() {
return "Groundhog #" + number;
}
}
public class SpringDetector {
public static <T extends Groundhog> void detectSpring(Class<T> type) {
try {
// Get the constructor of the incoming class
Constructor<T> ghog = type.getConstructor(int.class);
Map<Groundhog, Prediction> map =
IntStream.range(0, 10)//
.mapToObj(i -> {
try {
return ghog.newInstance(i);
} catch (Exception e) {
throw new RuntimeException(e);
}
})
toMap Both parameters of are based on the passed in gh To evaluate the , The two parameters have the same value
.collect(Collectors.toMap(Function.identity(), gh -> new Prediction()));
map.forEach((k, v) -> System.out.println(k + ": " + v));
Groundhog gh = ghog.newInstance(3);
System.out.println("Looking up prediction for " + gh);
if (map.containsKey(gh)) {
System.out.println(map.get(gh));
}else System.out.println("Key not found: " + gh);
} catch (NoSuchMethodException | IllegalAccessException |
InstantiationException | InvocationTargetException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
detectSpring(Groundhog.class);
}
}
/* output: Groundhog #2: Six more weeks of Winter! Groundhog #9: Early Spring Groundhog #4: Six more weeks of Winter! Groundhog #7: Early Spring Groundhog #1: Early Spring Groundhog #5: Six more weeks of Winter! Groundhog #3: Early Spring Groundhog #6: Early Spring Groundhog #0: Six more weeks of Winter! Groundhog #8: Six more weeks of Winter! Looking up prediction for Groundhog #3 Key not found: Groundhog #3 */
Every Groundhog Are given a constant , So we can match it with Predication Identify . and Predication By randomly generated boolean To choose the weather .detectSpring() Instantiate by reflection Groundhog class .
there HashMap By Groundhog Associated with it Of Predication fill . And the output shows HashMap The content in , Then we'll fill in the constants 3 Of Groundhog As key Used to find the corresponding Predication.( This key value pair must be in map in ).
But that didn't work ---- It cannot be found Numbers 3 This key of . The problem is Groundhog Class automatically inherits from the base class Object, So here is Object Of hashCode() Generate hash code , And it (Object) The default is to use the address of the object to calculate the hash code . therefore , from Groundhog(3) Hash code of the first instance generated (Map) And by Groundhog(3) The hash code of the second instance generated (Groundhog) Is different , And we are looking for the latter .
We need to rewrite it properly hashCode() Method . This means that at the same time we need to rewrite equals() Method , Because it's also Object Part of .HashMap Need to use equals() Determine whether the current key is the same as the key in the table .
Remember : default Object.equals() Just compare the address of the object , And our two Groundhog(3) The address of is different , therefore , If you want to use your own class as HashMap Key , Must overload at the same time hashCode() and equals().
public class Groundhog2 extends Groundhog {
public Groundhog2(int number) {
super(number);
}
@Override
// When you use your own class as a key , Be sure to rewrite it here hashCode Method
public int hashCode() {
return number;
}
@Override
public boolean equals(Object o) {
return o instanceof Groundhog2 &&
Objects.equals(number, ((Groundhog2) o).number);
}
}
public class SpringDetector2 {
public static void main(String[] args) {
SpringDetector.detectSpring(Groundhog2.class);
}
}
/* output: Groundhog #0: Six more weeks of Winter! Groundhog #1: Early Spring Groundhog #2: Six more weeks of Winter! Groundhog #3: Early Spring Groundhog #4: Early Spring Groundhog #5: Six more weeks of Winter! Groundhog #6: Early Spring Groundhog #7: Early Spring Groundhog #8: Six more weeks of Winter! Groundhog #9: Six more weeks of Winter! Looking up prediction for Groundhog #3 Early Spring */
Groundhog2.hashCode() return Groundhog As hash code .hashCode() It is not always possible to return a unique identifier ( To clarify later ), however equals() Methods must strictly determine whether two objects are the same .
2.1 understand hashCode
The previous example is only the first step to solve the problem correctly . It says : If you don't overwrite your keys hashCode() and equals(), So objects that use hash data structures (HashSet、HashMap、LinkedHashSet、LinkedHashMap) You can't handle your keys properly . So you must have a good understanding of the internal structure of these data structures .
public class MapEntry<K,V> implements Map.Entry<K,V>{
private K key;
private V value;
public MapEntry(K key, V value) {
this.key = key;
this.value = value;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
@Override
public V setValue(V value) {
V result = this.value;
this.value = value;
return result;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MapEntry<?, ?> mapEntry = (MapEntry<?, ?>) o;
return Objects.equals(key, mapEntry.key) && Objects.equals(value, mapEntry.value);
}
@Override
public int hashCode() {
return Objects.hash(key, value);
}
@Override
public String toString() {
return key + " = " + value;
}
}
When you define an object with more than one attribute hashCode() when , You can use this method . If your object has only one attribute , You can use it directly Objects.hashCode().
public class SlowMap<K,V> extends AbstractMap<K,V> {
private List<K> keys = new ArrayList<>();
private List<V> values = new ArrayList<>();
@Override
public V get(Object key) {
if (!keys.contains(key)) return null;
return values.get(keys.indexOf(key));
}
@Override
public V put(K key, V value) {
V oldValue = get(key);// The old value may be null
if (!keys.contains(key)) {
keys.add(key);
values.add(value);
} else values.set(keys.indexOf(key), value);
return oldValue;
}
@Override
// there Set Key value pairs can be made unique
public Set<Map.Entry<K, V>> entrySet() {
Set<Map.Entry<K, V>> set = new HashSet<>();
Iterator<K> ki = keys.iterator();
Iterator<V> vi = values.iterator();
while (ki.hasNext()) {
set.add(new MapEntry<>(ki.next(), vi.next()));
}
return set;
}
public static void main(String[] args) {
SlowMap<String, String> map = new SlowMap<>();
map.putAll(Countries.capitals(8));
map.forEach((k,v)-> System.out.println(k+" = "+v));
System.out.println(map.get("BENIN"));
map.entrySet().forEach(System.out::println);
}
}
/* output: CAMEROON = Yaounde ANGOLA = Luanda BURKINA FASO = Ouagadougou BURUNDI = Bujumbura ALGERIA = Algiers BENIN = Porto-Novo CAPE VERDE = Praia BOTSWANA = Gaberone Porto-Novo CAMEROON = Yaounde ANGOLA = Luanda BURKINA FASO = Ouagadougou BURUNDI = Bujumbura ALGERIA = Algiers BENIN = Porto-Novo CAPE VERDE = Praia BOTSWANA = Gaberone */
First , The purpose of using hashes is : Want to use one object to find another . But the use of TreeMap Or you can do it yourself Map This can also be achieved .
The above example uses a pair of ArrayList Implemented a Map, This class contains Map Complete implementation of the interface , So it provides entrySet() Method .
put The method simply places the key value pair in the corresponding ArrayList. In order to Map Interface consistency , It must return the old key , Or return without any old keys null.
Be careful : stay get() in key The type is Object, Not the parameterized type you expect K, This is injecting generics into Java Results in language .
Map.entrySet() Method must produce a Map.Entry Object set . But this object set is just an interface , If you want to define your own map, You must implement this interface at the same time .
Although the above solution is very simple , But this is not an appropriate implementation , Because it creates copies of keys and values .entrySet The proper implementation of should be in Map Provides views in , Not a copy , And this view allows you to modify the original mapping table ( The copy is not ).
2.2 Hash for speed ( The principle of hashing )
SlowMap.java Explains how to create a new Map It is feasible. , But just like its name , It's not efficient . The problem with it is the query for keys , Keys are not saved in any particular order , So you can only use simple linear queries , Linear query is the slowest way to query .
The value of hashing is speed , We know , The time complexity of hash query is very low , The efficiency is very high .
Because the bottleneck is at key Query speed of , So one of the solutions is to keep the sorting state of the keys , And then use Collections.binarySearch() The query .
Hashing is a step closer , It keeps the keys somewhere , So that we can find . Which will match the key Key information (key information) Stored in array ( The fastest data structure to access ) in , And this so-called key information is what we know Hash code ( Remember that arrays do not hold keys themselves ): By definition in Object Medium 、 And may be covered by your class hashCode() Method ( Hash function in data structure ) Generate . Generate a number from the key object , Use it as a subscript for an array .
So the first step in the process of querying a value is to calculate the hash code , Then use hash code to query the array . When using hash codes , Because our functions are different , The possibility of conflict is also different , Generally, conflicts are caused by external links ( Commonly known as zipper method ) solve : Arrays do not store values directly , It's about saving values list. Then on list The value in uses equals() Make linear queries . This part will be slower , But if the hash function is good , There won't be too many values at each position of the array . That's why HashMap The overall reason for this speed , For more details, see the hash of data structure .
public class SimpleHashMap<K,V> extends AbstractMap<K, V> {
// Select an initial number as the size of the hash table , To achieve uniform distribution of data
static final int SIZE = 997;
// You can't have a real generic array , But you can turn one up , This array is used to implement the zipper method
LinkedList<MapEntry<K, V>>[] buckets = new LinkedList[SIZE];
@Override
public V get(Object key) {
int index = Math.abs(key.hashCode()) % SIZE;
if (buckets[index] == null) return null;
for (MapEntry<K, V> entry : buckets[index])
if (entry.getKey().equals(key))
return entry.getValue();
return null;
}
@Override
public V put(K key, V value) {
V oldValue = null;
// take hashCode The absolute value of SIZE Remainder
int index = Math.abs(key.hashCode()) % SIZE;
// Give each array a subscript link list
if (buckets[index] == null) {
buckets[index] = new LinkedList<>();
}
// Get the corresponding... Under the hash code list
LinkedList<MapEntry<K, V>> bucket = buckets[index];
MapEntry<K, V> pair = new MapEntry<>(key, value);
boolean found = false;
ListIterator<MapEntry<K, V>> it = bucket.listIterator();
while (it.hasNext()) {
MapEntry<K, V> entry = it.next();
// According to the matching Map Of key Value substitution already exists
if (entry.getKey().equals(key)) {
oldValue = entry.getValue();
it.set(pair);
found = true;
break;
}
}
//
if (!found) {
buckets[index].add(pair);
}
return oldValue;
}
@Override
// Every map Can generate a entrySet
public Set<Entry<K, V>> entrySet() {
Set<Map.Entry<K, V>> set = new HashSet<>();
for (LinkedList<MapEntry<K, V>> bucket : buckets) {
if (bucket == null) continue;
for (MapEntry<K, V> entry : bucket) {
set.add(entry);
}
}
return set;
}
public static void main(String[] args) {
SimpleHashMap<String, String> m = new SimpleHashMap<>();
m.putAll(Countries.capitals(8));
m.forEach((k, v) -> System.out.println(k + "=" + v));
System.out.println(m.get("BENIN"));
m.entrySet().forEach(System.out::println);
}
}
Due to... In the hash table “ Slot position ”(slot) It is usually called bucket position (bucket), So we name the array that represents the actual hash table bucket, To make the hash distribution uniform , The number of barrels is usually prime ( Now basically use 2 To the power of an integer ).
Be careful : In order to automatically handle conflicts , Used a LinkedList Array of , Each new element is simply added directly to list In a specific bucket position at the end . Even if Java You are not allowed to create generic arrays , You can also create a reference to this array .
// You can't have a real generic array , But you can turn one up , This array is used to implement the zipper method LinkedList<MapEntry<K, V>>[] buckets = new LinkedList[SIZE];
here , It is very convenient to transform up to this kind of array , This prevents additional conversions in later code .
Be careful : This implementation does not mean that performance is tuned , It just wants to show the various operations performed by the hash table . and java.util.HashMap Source code , It is tuned . there entrySet() It is too simple .
2.3 rewrite hashCode()
After understanding how to hash , You can write your own hashCode() 了 .
First , You can't control bucket Generation of subscript values of arrays . This value depends on the concrete HashMap The capacity of the object , The change of capacity is related to the filling degree and load factor of the container .hashCode() Generated results , After processing, it is called the subscript of the bucket position , In the previous example , Just take the mold .
Design hashCode() The most important factor is : No matter when , Call... On the same object hashCode() Will generate the same value .
Let's say String Class, for example :String There is a characteristic : If there are more than one in the program String object , All contain the same string sequence , that these String Objects are mapped to the same memory area .
public class StringHashCode {
public static void main(String[] args) {
String[] hellos = "Hello Hello".split(" ");
System.out.println(hellos[0].hashCode());
System.out.println(hellos[1].hashCode());
}
}
/* output: 69609650 69609650 */
about String for ,hashCode() The comparison of is obviously based on String Content .
therefore , Want to hashCode() practical , It must be fast and meaningful . in other words : It must generate hash codes based on the contents of the object . Hash codes need not be unique , But by hashCode() and equals(), Must be able to fully identify the object . The calculation formula of hash code :result = 37 * result + c;
And you write hashCode() When , Remember the following rules : Use Objects.hash() Objects for hashing multiple fields , And then use Objects.hashCode() Objects for hashing single fields .
Here are the single field objects :
public class CountedString {
private static List<String> created = new ArrayList<>();
private String s;
private int id = 0;
public CountedString(String str) {
s = str;
created.add(s);
//id yes this Class String Total number of instances
for (String s2 : created) {
if (s2.equals(s))
id++;
}
}
@Override
public int hashCode() {
// Very simple implementation : return s.hashCode() * id;
int result = 17;
result = 37 * result + s.hashCode();
result = 37 * result + id;
return result;
}
@Override
public boolean equals(Object obj) {
return obj instanceof CountedString &&
Objects.equals(s,((CountedString) obj).s) &&
Objects.equals(id,((CountedString) obj).id);
}
@Override
public String toString() {
return "String: " + s +" id: "+id+" hashCode: "+hashCode();
}
public static void main(String[] args) {
Map<CountedString, Integer> map = new HashMap<>();
CountedString[] cs = new CountedString[5];
for (int i = 0; i < cs.length; i++) {
cs[i] = new CountedString("hi");
map.put(cs[i], i);
}
System.out.println(map);
System.out.println("==================");
for (CountedString cstring : cs) {
System.out.println("Looking up " + cstring);
System.out.println(map.get(cstring));
}
}
}
/* output: // Remember to print map When , be equal to ‘=’ All the front ones correspond to the back ones {String: hi id: 4 hashCode: 146450=3, String: hi id: 5 hashCode: 146451=4, String: hi id: 2 hashCode: 146448=1, String: hi id: 3 hashCode: 146449=2, String: hi id: 1 hashCode: 146447=0} ================== Looking up String: hi id: 1 hashCode: 146447 0 Looking up String: hi id: 2 hashCode: 146448 1 Looking up String: hi id: 3 hashCode: 146449 2 Looking up String: hi id: 4 hashCode: 146450 3 Looking up String: hi id: 5 hashCode: 146451 4 */
hashCode() and equals() Are based on CountString Of String and id These two fields are used to generate the results . stay main in , Use the same String Created multiple objects , But because id Different , Make their hash codes different .
Here is the second multi field example :
public class Individual implements Comparable<Individual> {
private static long counter = 0;
private final long id = counter++;
private String name;
public Individual(String name) {
this.name = name;
}
public Individual() {
}
@Override
public String toString() {
return getClass().getSimpleName() + (name == null ? "" : " " + name);
}
public long getId() {
return id;
}
@Override
public boolean equals(Object o) {
return o instanceof Individual &&
Objects.equals(id,((Individual) o).id);
}
@Override
public int hashCode() {
return Objects.hash(name,id);
}
@Override
//compareTo Will produce a sort sequence , The sorting rules first sort according to the actual type , Then if there's a name , according to name Sort , Finally, in the order of creation (id) Sort
public int compareTo(Individual o) {
// First, compare according to the class name
String first = getClass().getSimpleName();
String oFirst = o.getClass().getSimpleName();
int firstCompare = first.compareTo(oFirst);
if (firstCompare != 0) return firstCompare;
if (name != null && o.name != null) {
int secondCompare = name.compareTo(o.name);
if (secondCompare != 0) {
return secondCompare;
}
}
return o.id < id ? -1:(o.id == id ? 0:1);
}
}
3、 ... and 、 tuning HashMap
It is possible to manually tune hashMap To improve its performance in specific applications .
The following terms must be understood :
- Capacity (Capacity): Number of barrels stored in the table .
- Initial capacity (Initial Capacity): When the table is created , Initial number of barrels .HashMap and HashSet There are constructors that let you specify the initial capacity .
- Number (Size): The number of key value pairs currently stored in the table .
- Load factor (Load factor): It is usually expressed as Size/Capacity, When the load factor is 0 It means an empty table . When the size is 0.5 It means half full , Lightly loaded tables have little conflict , Therefore, it is the best choice for insertion and deletion , and 0.75 It is a balance strategy that takes into account both time and space ,HashMap and HashSet There are constructors that let you specify load factors , When the capacity reaches the load factor , The collection will automatically expand to twice its original capacity , And store the original data in a new bucket .
边栏推荐
- stm32mp1 Cortex M4开发篇9:扩展板空气温湿度传感器控制
- The spring recruitment is also terrible. Ali asked at the beginning of the interview: how to design a high concurrency system? I just split
- Jar package required for full stack development
- 【C】 [time operation] time operation in C language
- 并发编程高级部分:并行流,Tasks和Executors以及CompletableFuture类
- The next stop of Intelligent Manufacturing: cloud native + edge computing two wheel drive
- Application configuration management, basic principle analysis
- Eureka的TimedSupervisorTask类(自动调节间隔的周期性任务)
- Observation on the salary data of the post-90s: poor, counselled and serious
- JS resource disaster recovery
猜你喜欢

TC软件详细设计文档(手机群控)

Stm32mp1 cortex M4 development part 10: expansion board nixie tube control

Alibaba P6 employees came to a small company for an interview and asked for an annual salary increase of 500000 yuan. How dare you speak
![[practice] stm32mp157 development tutorial FreeRTOS system 3: FreeRTOS counting semaphore](/img/b1/e4b944877fecc079a772b81c55bfc8.jpg)
[practice] stm32mp157 development tutorial FreeRTOS system 3: FreeRTOS counting semaphore

The internal structure of MySQL and how an SQL statement is executed
![[practice] stm32mp157 development tutorial FreeRTOS system 6: FreeRTOS list and list items](/img/28/51be35224959b1bf70f4edc8a54ff4.png)
[practice] stm32mp157 development tutorial FreeRTOS system 6: FreeRTOS list and list items

一条命令开启监控之旅!

Stm32mp1 cortex M4 development part 13: external interrupt of expansion board key

Solve the problem of error when typescript object gets value

Lei niukesi --- basis of embedded AI
随机推荐
Ali has been working for 8 years. This learning note is left when he reaches P8. He has helped his friends get 10 offers
Stm32mp1 cortex M4 Development Chapter 11: expansion board buzzer control
Stm32mp1 cortex M4 development part 8: LED lamp control experiment of expansion board
Alibaba P6 employees came to a small company for an interview and asked for an annual salary increase of 500000 yuan. How dare you speak
[actual combat] STM32 FreeRTOS porting series Tutorial 4: FreeRTOS software timer
Job hopping is better than promotion
The spring recruitment is also terrible. Ali asked at the beginning of the interview: how to design a high concurrency system? I just split
108. detailed use of Redux (case)
获取配置文件properties中的数据
Stm32mp1 cortex M4 development part 10: expansion board nixie tube control
Judge the data type of JS
Embedded software project process and project startup instructions (example)
How to connect the Internet - FTTH
R language uses the < - operator to create a new variable, uses the existing data column (sum, mean) to create a new data column, uses the ifelse function or conditional judgment to create a discrete
finally block can not complete normally
The @transactional in JUnit disappears. Can @rollback test the flag of rollback?
121. Redux detailed summary + effect drawing + Case
并发-条件变量
Several ways to trigger link jump
Style penetration of vant UI components -- that is, some styles in vant UI components cannot be modified