当前位置:网站首页>"NetEase Internship" Weekly Diary (3)
"NetEase Internship" Weekly Diary (3)
2022-08-02 01:56:00 【Liknananana】
『网易实习』周记(三)
本周知识清单:
- kotlin基础
- jar包和arr包的区别
- 分析Google的MVVMThe case of architecture
- LiveData与MutableLive的区别
- kotlin lamda语法
- kotlin的匿名内部类
- kotlin函数式编程
- RxJava
kotlin基础
A learning, of course, is to look at the official documentation,推荐阅读:kotlin中文官方文档菜鸟教程 ,Me some time to read the document after the company took over the real project,And found a lot of advanced usage haven't read it,So again to catch up with the,关于这些I see the article recommended:
从Java过渡到kotlin
"Kotlin"系列: 一、Kotlin入门
"Kotlin"系列: 二、Kotlin泛型
Lambda语法在Java中已经被广泛的运用,我们在开发Android中几乎上每一个项目也会在项目中接入Lambda插件,因为LambdaReally can reduce a lot of amount of code.无独有偶,在Kotlin中也是Lambda语法的,在这篇文章中就详细的为大家讲解Lambda语法的编写与使用
Late themselves out somekotlin的读书笔记,欢迎关注
jar包和arr包的区别
Recently learned componentization,Found it useless (no use) our company componentization projectARouter路由组件,我很好奇,Ask the project architecture predecessors,He asked me the company code is nothing two parts
java包和sdk包,javaIs the real business logic,Are not exposed to the outside world,sdkPackage is this module provides the interface,我们通过把sdk打包成jar包,Other packages to use introduced directly by the,It also can realize the page jump,为什么不用ARouter呢?前辈说:ARouterTo configure the routing path,Routing path is a string,If the configuration is wrong is hard to find,But if you call interface,The compiler can't through,One to reduce the error probability
**jar包与aarThe difference between package files **:
两者区别:
*.jar:只包含了class文件与清单文件,不包含资源文件,如图片等所有res中的文件.
*.aar:包含所有资源,class以及res资源文件全部包含
If you are just a simple class library with the generated.jar文件即可;如果你的是一个UI库,Contains some control layout file and write their own font resources such as file so can only use.aar文件.
推荐阅读:android studio library生成jar包和aar的方法总结
分析Google的MVVMThe case of architecture
案例地址:https://github.com/android/architecture-components-samples/tree/main/BasicSample
Due to the company before the project isMVVM的架构,To learn on their own to thenJetpack里面的ViewModel和DataBinding LiveDataHas never written and project,So on this is now a little knowledge,And then this week to have demand recently,The company is a big project,Then see dizziness later teacher let me to readgoogle的案例,Google's case is a list of goods,Point in is the product details page,这次分析Mainly from a data request to start,Analysis of a complete data request,特此记录一下
先看项目结构:
public class BasicApp extends Application {
private AppExecutors mAppExecutors;
@Override
public void onCreate() {
super.onCreate();
// Create a thread to handle class
mAppExecutors = new AppExecutors();
}
//Returns a database operation to do class
public AppDatabase getDatabase() {
return AppDatabase.getInstance(this, mAppExecutors);
}
// Return to a class of data storage warehouse
public DataRepository getRepository() {
return DataRepository.getInstance(getDatabase());
}
}
//This is a page to load thelist的商品类,From the analysis data of loading request
public class ProductListFragment extends Fragment {
public static final String TAG = "ProductListFragment";
private ProductAdapter mProductAdapter;
private ListFragmentBinding mBinding;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
// 这就是获取DataBinding
mBinding = DataBindingUtil.inflate(inflater, R.layout.list_fragment, container, false);
//.This is for home pagerecycleview设置适配器
mProductAdapter = new ProductAdapter(mProductClickCallback);
mBinding.productsList.setAdapter(mProductAdapter);
return mBinding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// 获取页面的viewModel
final ProductListViewModel viewModel =
new ViewModelProvider(this).get(ProductListViewModel.class);
// This is the query in the search box
mBinding.productsSearchBtn.setOnClickListener(v -> {
Editable query = mBinding.productsSearchBox.getText();
viewModel.setQuery(query);
});
//1.调用subscribeUiShow the commodity data in,Focus on here so he went to get onProducts的
subscribeUi(viewModel.getProducts());
}
private void subscribeUi(LiveData<List<ProductEntity>> liveData) {
// Update the list when the data changes
liveData.observe(getViewLifecycleOwner(), new Observer<List<ProductEntity>>() {
@Override
// 这里就是LiveDataThere are any changes of data is to look at the data
public void onChanged(List<ProductEntity> myProducts) {
if (myProducts != null) {
mBinding.setIsLoading(false);
mProductAdapter.setProductList(myProducts);
} else {
mBinding.setIsLoading(true);
}
// espresso does not know how to wait for data binding's loop so we execute changes
// sync.
mBinding.executePendingBindings();
}
});
}
@Override
public void onDestroyView() {
mBinding = null;
mProductAdapter = null;
super.onDestroyView();
}
//This back off function is used to do when I click the specific pageitem数据的时候.Details to display the page
private final ProductClickCallback mProductClickCallback = product -> {
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
((MainActivity) requireActivity()).show(product);
}
};
}
回到关注点1:我们进入了ProductListViewModel
public class ProductListViewModel extends AndroidViewModel {
private static final String QUERY_KEY = "QUERY";
private final SavedStateHandle mSavedStateHandler;
private final DataRepository mRepository;
private final LiveData<List<ProductEntity>> mProducts;
public ProductListViewModel(@NonNull Application application,
@NonNull SavedStateHandle savedStateHandle) {
super(application);
mSavedStateHandler = savedStateHandle;
mRepository = ((BasicApp) application).getRepository();
// Use the savedStateHandle.getLiveData() as the input to switchMap,
// allowing us to recalculate what LiveData to get from the DataRepository
// based on what query the user has entered
// 3.Here you can see here ismProducts数据初始化的时候,We can see that the data is fromRepository类
mProducts = Transformations.switchMap(
savedStateHandle.getLiveData("QUERY", null),
(Function<CharSequence, LiveData<List<ProductEntity>>>) query -> {
if (TextUtils.isEmpty(query)) {
return mRepository.getProducts();
}
return mRepository.searchProducts("*" + query + "*");
});
}
public void setQuery(CharSequence query) {
// Save the user's query into the SavedStateHandle.
// This ensures that we retain the value across process death
// and is used as the input into the Transformations.switchMap above
mSavedStateHandler.set(QUERY_KEY, query);
}
/** * Expose the LiveData Products query so the UI can observe it. */
// 2.This is the first concerns the method called,Then we can see the class constructor
public LiveData<List<ProductEntity>> getProducts() {
return mProducts;
}
}
回到DataReppsitory类,
public class DataRepository {
private static DataRepository sInstance;
private final AppDatabase mDatabase;
private MediatorLiveData<List<ProductEntity>> mObservableProducts;
private DataRepository(final AppDatabase database) {
mDatabase = database;
mObservableProducts = new MediatorLiveData<>();
// 5.这里可以知道,Data from againDatabase里面的productDao去调
mObservableProducts.addSource(mDatabase.productDao().loadAllProducts(),
productEntities -> {
if (mDatabase.getDatabaseCreated().getValue() != null) {
mObservableProducts.postValue(productEntities);
}
});
}
public static DataRepository getInstance(final AppDatabase database) {
if (sInstance == null) {
synchronized (DataRepository.class) {
if (sInstance == null) {
sInstance = new DataRepository(database);
}
}
}
return sInstance;
}
/** * Get the list of products from the database and get notified when the data changes. */
// 4.Can know the focus of the data is from here
public LiveData<List<ProductEntity>> getProducts() {
return mObservableProducts;
}
public LiveData<ProductEntity> loadProduct(final int productId) {
return mDatabase.productDao().loadProduct(productId);
}
public LiveData<List<CommentEntity>> loadComments(final int productId) {
return mDatabase.commentDao().loadComments(productId);
}
public LiveData<List<ProductEntity>> searchProducts(String query) {
return mDatabase.productDao().searchAllProducts(query);
}
}
回到AppDatabase,这个类就是Room数据库的操作类,这个类是个抽象类,我们调用productDao(),会去
ProductDaoInterface to execute insidesql语句
@Database(entities = {
ProductEntity.class, ProductFtsEntity.class, CommentEntity.class}, version = 2)
@TypeConverters(DateConverter.class)
public abstract class AppDatabase extends RoomDatabase {
private static AppDatabase sInstance;
@VisibleForTesting
public static final String DATABASE_NAME = "basic-sample-db";
public abstract ProductDao productDao();
public abstract CommentDao commentDao();
private final MutableLiveData<Boolean> mIsDatabaseCreated = new MutableLiveData<>();
public static AppDatabase getInstance(final Context context, final AppExecutors executors) {
if (sInstance == null) {
synchronized (AppDatabase.class) {
if (sInstance == null) {
sInstance = buildDatabase(context.getApplicationContext(), executors);
sInstance.updateDatabaseCreated(context.getApplicationContext());
}
}
}
return sInstance;
}
/** * Build the database. {@link Builder#build()} only sets up the database configuration and * creates a new instance of the database. * The SQLite database is only created when it's accessed for the first time. */
private static AppDatabase buildDatabase(final Context appContext,
final AppExecutors executors) {
return Room.databaseBuilder(appContext, AppDatabase.class, DATABASE_NAME)
.addCallback(new Callback() {
@Override
public void onCreate(@NonNull SupportSQLiteDatabase db) {
super.onCreate(db);
executors.diskIO().execute(new Runnable() {
@Override
public void run() {
// Add a delay to simulate a long-running operation
addDelay();
// Generate the data for pre-population
AppDatabase database = AppDatabase.getInstance(appContext, executors);
List<ProductEntity> products = DataGenerator.generateProducts();
List<CommentEntity> comments =
DataGenerator.generateCommentsForProducts(products);
insertData(database, products, comments);
// notify that the database was created and it's ready to be used
database.setDatabaseCreated();
}
});
}
})
.addMigrations(MIGRATION_1_2)
.build();
}
/** * Check whether the database already exists and expose it via {@link #getDatabaseCreated()} */
private void updateDatabaseCreated(final Context context) {
if (context.getDatabasePath(DATABASE_NAME).exists()) {
setDatabaseCreated();
}
}
private void setDatabaseCreated(){
mIsDatabaseCreated.postValue(true);
}
private static void insertData(final AppDatabase database, final List<ProductEntity> products,
final List<CommentEntity> comments) {
database.runInTransaction(() -> {
database.productDao().insertAll(products);
database.commentDao().insertAll(comments);
});
}
private static void addDelay() {
try {
Thread.sleep(4000);
} catch (InterruptedException ignored) {
}
}
public LiveData<Boolean> getDatabaseCreated() {
return mIsDatabaseCreated;
}
private static final Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("CREATE VIRTUAL TABLE IF NOT EXISTS `productsFts` USING FTS4("
+ "`name` TEXT, `description` TEXT, content=`products`)");
database.execSQL("INSERT INTO productsFts (`rowid`, `name`, `description`) "
+ "SELECT `id`, `name`, `description` FROM products");
}
};
}
Obviously it is all explained,The data of experience:
Other details want to know about yourself to see oh,就不一一写出来了
LiveData与MutableLive的区别
- LiveDataIs one of the observer pattern data entity class,He can, and whether his registered observer callback data has been updated
- LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期.这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者.
MutableLive与LiveData的区别
1.MutableLiveData的父类是LiveData
2.LiveData在实体类里可以通知指定某个字段的数据更新.
3.MutableLiveData则是完全是整个实体类或者数据类型变化后才通知.不会细节到某个字段
推荐阅读 :MutableLiveData详解
Kotlin:Lambda表达式
Recently took over the demand arekotlin开发,Alternative predecessors wrotekollin很多都是lambda表达式,I'm a beginner looks really bad,The recently took advantage of the weekendLambdaLearn about,特此记录一下.
Lambda的特征
- Lambda表达式总是被大括号括着
- 其参数(如果存在)在符号’->'之前声明(参数类型可以省略)
- 函数体(如果存在)在符号’->'后面.
- 简单来说,Lambda Is a can be passed as a parameter to the code,It can be used as the parameters of the function,返回值,At the same time can also be assigned to a variable
- Lambda Complete expression of the grammatical structure:{ 参数名1:参数类型,参数名2:参数类型 -> 函数体 }
- 很多时候,We will use simplified forms of syntax structure,Direct is a function of body:{函数体},这种情况是当 Lambda Expression of only one parameter in the parameter list,We can put the parameters to omit,默认会有个 it 参数
- Kotlin 中规定,当 Lambda Expression as a function of the last parameter,我们可以把 Lambda 表达式移到函数括号的外面
- Kotlin 中规定,当 Lambda The expression is the only parameter function,函数的括号可以省略
无参数:
val 函数名 = {函数体}
有参数:
val 函数名 : (参数1:类型, 参数2:类型, …) -> 返回值类型 = { 参数1, 参数2, … -> 函数体 }
val 函数名 = { 参数1:类型1, 参数2:类型2, … -> 函数体 }
只有一个参数的时候,返回值中的参数形参可以省略,引用时通过it进行引用
匿名函数:
val 函数名 = fun(参数1:类型1, 参数2:类型2, …): 返回值类型 { 函数体 }
高阶函数
KotlinEverything inside the function,Introduced the function type,Can also function as a type of,当作参数传递,那么使用LambdaExpressions will have a feeling that is used as the composite function of high school mathematics,f(g(x))There are many replacement
// 匿名函数
val sum = fun(a: Int, b: Int): Int {
return a + b
}
// 具名函数
fun namedSum(a: Int, b: Int): Int {
return a + b
}
// 高阶函数
fun highSum(a: Int, b: Int, f: (Int, Int) -> Int): Int {
return f(a, b)
}
fun main(args: Array<String>) {
// 通过()来执行匿名函数sum
val add = sum(1, 2)
println(add)
// 通过lambda表达式来完成函数highSum
val add2 = highSum(3, 4) {
a, b -> a + b }
println(add2)
// 通过函数引用来完成函数highSum
val add3 = highSum(5, 6, ::namedSum)
println(add3)
// forEach参数接收一个函数
args.forEach({
it: String -> println(it) })
// 去掉返回值,自动推断
args.forEach({
it -> println(it) })
// 只有一个参数的时候可以省略it
args.forEach({
println(it) })
// lambda表达式在最后一个参数可以外移
args.forEach() {
println(it) }
// 函数若无参数可以去掉()
args.forEach {
println(it) }
// 引用函数
args.forEach(::println)
}
函数类型与实例化
- :: 双冒号操作符表示对函数的引用
- lambda表达式
- 匿名函数
fun main(args: Array<String>) {
// 引用函数
println(test(1, 2, ::add))
// 匿名函数
val add = fun(a: Int, b: Int): Int {
return a + b
}
println(test(3, 4, add))
// lambda表达式
println(test(5, 6, {
a, b -> a + b }))// lambda作为最后一个参数可以提到括号外
println(test(5, 6) {
a, b -> a + b })
}
fun test(a: Int, b: Int, f: (Int, Int) -> Int): Int {
return f(a, b)
}
fun add(a: Int, b: Int): Int {
return a + b
}
推荐阅读:Kotlin修炼指南(二):lambda表达式的精髓Kotlin:Lambda表达式
Kotlin 的高阶函数、匿名函数和 Lambda 表达式
kotlin匿名内部类
Kotlin Write an anonymous inner class and Java 有一点区别,Kotlin Because in the abandoned new 关键字,改用 object 关键字就可以了
//java 中的匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
}
}).start();
//Kotlin 中可以这么写
Thread(object : Runnable{
override fun run() {
}
}).start()
/** * We went on to simplify Kotlin 中的写法 * 因为 Runnable 类中只有一个待实现方法,Even if there is no display rewritten run() 方法, * Kotlin Can understand the back of the Lambda 表达式就是要在 run() 方法中实现的内容 */
Thread(Runnable{
}).start()
//Because a single abstract method interface,We can be omitted the interface name
Thread({
}).start()
//当 Lambda Expression as a function of the last parameter,我们可以把 Lambda 表达式移到函数括号的外面
Thread(){
}.start()
//当 Lambda The expression is the only parameter function,函数的括号可以省略
Thread{
}.start()
推荐阅读:如何在 kotlin 优雅的封装匿名内部类(DSL、高阶函数)
kotlin的函数式编程
函数式编程(FP)Is based on the premise of a simple and meaningful:Only pure functions to build the program.The deeper meaning of this sentence is,We should function to build applications with no side effect.什么是副作用呢?Function with side effects in the process of call is not only a simple input and output behavior,It also do some other things.And these effects will influence the diffusion to the outside of the function,比如:
- Modify an external variable(函数外定义)
- 设置成员变量
- 修改数据结构(List、Map、Set)
- Throw an exception, or an error to stop
- 读取、写入文件
RxJava学习
Recent project there are many are with theRxJava,You also need to update their knowledge base,由于时间原因,我只学了RxJava的如何使用,RxJavaMany of the source and the use of other,Have the time to come back to catch up with.
RxJava的简介
- RxJava:Reactive Extensions for the JVM ,基于JVM的Rx
- Reactive Extensions Is a Microsoft put forward a comprehensive induction and based on the event-driven programming library
- RxJava的核心就是异步数据流和响应式编程:
1·Put all the events(数据)As a river,He can be observed,Filtering or operation,Can also and another rivers converge into a new river
2·Once the event or change,Can trigger to observe the role of these events(观察者/订阅者)做出响应
RxJavaThe advantage and the suitable scene
- 具备响应式编程该有的特性
- 为异步而生,无需手动创建线程,Have the ability to thread
- 支持链式调用,保证代码的简洁性
- 各种操作符,功能非常强大,满足各种业务需求
- 简化了异常的处理
RxJava的适用场景:网络请求,数据库读写,文件读写,Timing task and other lengthy operations are oh ah done through asynchronous operations can be usedRxJava
RxJava There are three basic elements of the:
- 被观察者(Observable)
- 观察者(Observer)
- 订阅(subscribe)
// 导入依赖
implementation "io.reactivex.rxjava2:rxjava:2.2.21"
implementation "io.reactivex.rxjava2:rxandroid:2.0.2"
Events like a river
Observable.create(new ObservableOnSubscribe<Integer>() {
//上游 Observable
@Override
public void subscribe(ObservableEmitter<Integer> e) throws Exception {
//To add a waternext事件,Passed to the downstream has corresponding receivingnext事件的方法
e.onNext(1);
//In the water is acomplete事件,Passed to the downstream has corresponding receivingnext事件的方法
e.onComplete(2);
}
})
.subscribe(
//subscible Is the link,Connects upstream and downstream
new Observer<Integer>() {
//observer 表示 下游
Disposable d;
//DisposableSaid the link switch
@Override
public void onSubscribe(Disposable d) {
this.d=d;
}
@Override
public void onNext(Integer integer) {
//To accept the upstreamnext事件
Log.d(TAG, "onNext: "+integer);
}
@Override
public void onError(Throwable e) {
Log.d(TAG, "onError: "+e.getMessage());
}
@Override
public void onComplete() {
//To accept the upstreamcomplete事件`在这里插入代码片`
Log.d(TAG, "onComplete ");
}
});
ObservableEmitter
ObservableEmitter: Emitter是发射器的意思,He is used to emit events,He can send out three types of events,通过调用emitter的onNext(T value)、onComplete()和onError(Throwable error)就可以分别发出next事件、complete事件和error事件.
Emit events need to follow certain rules
- onNext():The upstream can make multipleonNextEvents can also received a number of downstreamonNext事件.
- onComplete():Send a upstreamonComplete事件后,Can continue to send other events,But the downstream accept toonComplete事件以后,Just stop receive events.
- onError():和onComplete相同,The upstreamonErrorAfter continue to send other events,And downstream to receiveonErrorAfter the event no longer receive events.
- The upstream can not sendonComplete和onError事件
- onComplete和onErrorMust be mutually exclusive and only,Don't can also make multipleonCompleteEvents also can not make multiple at the same timeonError事件,Also can not startonCompleteEvents againonError事件,反之亦然.
Disposable
He is equivalent to we mentioned the link,When the link was closed the calldisposable.dispose()方法,We won't be able to receive any events from the upstream downstream.But this does not affect the upstream continue to emit events.
public class test {
public static void main(String[] args) {
String TAG="SHOW";
Observable.create(new ObservableOnSubscribe<Integer>() {
//被观察者
@Override
public void subscribe(ObservableEmitter<Integer> e) throws Exception {
e.onNext(1);
System.out.println("subscribe: next1");
e.onNext(2);
System.out.println("subscribe: next2");
//e.onComplete();
//System.out.println("subscribe: complete");
e.onNext(3);
System.out.println("subscribe: next3");
e.onNext(4);
System.out.println("subscribe: next4");
}
}).subscribe(new Observer<Integer>() {
//观察者
Disposable d;
@Override
public void onSubscribe(Disposable d) {
this.d=d;
}
@Override
public void onNext(Integer integer) {
//DisposableTo be able to cut the pipe
if(integer>=2){
//当Integer大于2The close link between,Don't accept any downstream events,The upstream can continue to send
d.dispose();
}
System.out.println("onNext: "+integer);
}
@Override
public void onError(Throwable e) {
System.out.println("onError: "+e.getMessage());
}
@Override
public void onComplete() {
System.out.println("onComplete ");
}
});
}
}
Schedulers
- Schedulers.newThread():A regular child thread
- AndroidSchedulers.mainThread():Activity所在的主线程
- Schedulers.io():适合IO密集型的操作
- Schedulers.computation():Suitable for computationally intensive operations
subscribeOn
- Used to specify the observed in the thread,一般来说,We in the interior of the observed perform network request orIO操作,So on the child thread to perform.
- 注意一点,subscribeOnMethods if the called many times,Will be subject to when the first call to the specified thread,看如下代码
observable.subscribeOn(Schedulers.computation())
.subscribeOn(Schedulers.io())
.subscribeOn(Schedulers.newThread())
This last callsubscribeOn方法,最后ObservableWill be in accordance with the first call the specified thread running.
observerOn
It is used to specify the observer's own thread,As a result of observers accepted observed operation,May need to be updatedUI,所以大部分情况下,We will specify it in the main thread.
Also need to note,observerOnMethod is called many times,Will be subject to the specified thread one last time,即调用一次observerOn方法,线程便会切换一次.
推荐阅读:RxJava2 只看这一篇文章就够了
写在最后:Notes just record of learning,Probably will not be fully,But also for their knowledge of a output,Record of his own little by little,I am carrying a bag,Drifting in the outside of young, Bag with my dream
边栏推荐
- 60种特征工程操作:使用自定义聚合函数【收藏】
- 6-25漏洞利用-irc后门利用
- ¶Backtop 回到顶部 不生效
- Huawei's 5-year female test engineer resigns: what a painful realization...
- typescript37-class的构造函数实例方法继承(extends)
- Reflex WMS中阶系列7:已经完成拣货尚未Load的HD如果要取消拣货,该如何处理?
- typescript32-ts中的typeof
- typescript30 - any type
- 【ORB_SLAM2】SetPose、UpdatePoseMatrices
- YGG 公会发展计划第 1 季总结
猜你喜欢
Yunhe Enmo: Let the value of the commercial database era continue to prosper in the openGauss ecosystem
Oracle data to mysql FlinkSQL CDC to achieve synchronization
typescript30 - any type
Day115. Shangyitong: Background user management: user lock and unlock, details, authentication list approval
MySQL优化策略
电子制造仓储条码管理系统解决方案
手写博客平台~第二天
Basic use of typescript34-class
一本适合职场新人的好书
LeetCode Brushing Diary: 74. Searching 2D Matrix
随机推荐
R语言使用table1包绘制(生成)三线表、使用单变量分列构建三线表、编写自定义三线表结构(将因子变量细粒度化重新构建三线图)、自定义修改描述性统计参数输出自定义统计量
搜罗汇总的效应
canal realizes mysql data synchronization
哈希冲突和一致性哈希
《自然语言处理实战入门》 基于知识图谱的问答机器人
哈希表
云和恩墨:让商业数据库时代的价值在openGauss生态上持续繁荣
"Introduction to Natural Language Processing Practice" Question Answering Robot Based on Knowledge Graph
数据链路层的数据传输
HSDC和独立生成树相关
【ORB_SLAM2】void Frame::ComputeImageBounds(const cv::Mat &imLeft)
typeof in typescript32-ts
hash table
Navicat数据显示不完全的解决方法
LeetCode刷题日记:34、 在排序数组中查找元素的第一个和最后一个位置
求大神解答,这种 sql 应该怎么写?
The characteristics and principle of typescript29 - enumeration type
Redis 持久化 - RDB 与 AOF
Rust P2P网络应用实战-1 P2P网络核心概念及Ping程序
YGG Guild Development Plan Season 1 Summary