一.Paging组件的意义

  分页加载是在应用程序开发过程中十分常见的需求,我们经常需要以列表的方式加载大量的数据,这些数据通常来自网络或本地数据库。然而,如果一次性将所有数据全部加载出来,必然会消耗大量的时间和数据流量,而且用户可能只是需要一部分数据就行。因此,Google便推出了paging组件,来实现分页加载;分页加载就是对数据进行按需加载,在不影响用户体验的同时,还能节省数据流量,提升应用的性能。

二.Paging支持的架构类型

  Paging支持3种架构类型,分别是:网络,数据库,网络+数据库

  网络:也就是通过网络请求的方式去获得服务器返回的数据,然后分页加载出来

  数据库:掌握了从网络上获取数据并加载出来,从数据库加载就变得很简单,只需替换数据源即可

  网络+数据库:出于用户的体验,通常我们会对网络数据进行缓存,以便用户下次打开应用程序时,应用程序可以先展示缓存数据,我们通常会利用数据库对网络数据进行缓存,但这也意味着我们需要同时处理好网络和数据库这两个数据源。但是,多个数据源会让业务逻辑变得更为复杂,所以我们通常采用单一数据源作为解决方案,即从网络获取的数据,直接缓存进数据库,列表仅从数据库这个唯一的数据源获取数据。

三.三种分页机制的适用场景

  PositionalDataSource:

  适用于从任意位置加载任意数量的数据,且目标数据源中数据固定的情况。

  PageKeyedDataSource:

  适用于数据源以页的方式进行请求的情况,比如请求第二页的5条数据。

  ItemKeyedDataSource:

  适用于当目标数据的下一页需要依赖于上一页数据中最后一个对象中的某个字段作为key的情况,例如我请求key=9001后的5条数据作为下一页的数据。

  下面会以PositionalDataSource为例进行讲解,其他方式极其相似。

四.分页机制的实现

  我们以从豆瓣网上获取热度最高的250部电影为例进行讲解,这里我们使用Retrofit+OkHttp进行网络数据的获取,如果对这两个网络请求工具不熟悉的话,可以看这篇博客:https://www.cnblogs.com/luqman/p/okhttp_retrofit.html

  这里我们请求的api接口是https://movie.douban.com/j/chart/top_list?type=11&interval_id=100%3A90&action=&start=0&limit=250,接口返回的前五条数据如下所示:

[{"rating":["9.7","50"],"rank":1,"cover_url":"https://img2.doubanio.com\/view\/photo\/s_ratio_poster\/public\/p480747492.webp","is_playable":true,"id":"1292052","types":["犯罪","剧情"],"regions":["美国"],"title":"肖申克的救赎","url":"https:\/\/movie.douban.com\/subject\/1292052\/","release_date":"1994-09-10","actor_count":25,"vote_count":2912430,"score":"9.7","actors":["蒂姆·罗宾斯","摩根·弗里曼","鲍勃·冈顿","威廉姆·赛德勒","克兰西·布朗","吉尔·贝罗斯","马克·罗斯顿","詹姆斯·惠特摩","杰弗里·德曼","拉里·布兰登伯格","尼尔·吉恩托利","布赖恩·利比","大卫·普罗瓦尔","约瑟夫·劳格诺","祖德·塞克利拉","保罗·麦克兰尼","芮妮·布莱恩","阿方索·弗里曼","V·J·福斯特","弗兰克·梅德拉诺","马克·迈尔斯","尼尔·萨默斯","耐德·巴拉米","布赖恩·戴拉特","唐·麦克马纳斯"],"is_watched":false},{"rating":["9.6","50"],"rank":2,"cover_url":"https://img1.doubanio.com\/view\/photo\/s_ratio_poster\/public\/p2561716440.webp","is_playable":true,"id":"1291546","types":["剧情","爱情","同性"],"regions":["中国大陆","中国香港"],"title":"霸王别姬","url":"https:\/\/movie.douban.com\/subject\/1291546\/","release_date":"1993-07-26","actor_count":28,"vote_count":2150114,"score":"9.6","actors":["张国荣","张丰毅","巩俐","葛优","英达","蒋雯丽","吴大维","吕齐","雷汉","尹治","马明威","费振翔","智一桐","李春","赵海龙","李丹","童弟","沈慧芬","黄斐","徐杰","黄磊","冯远征","杨立新","方征","周璞","隋永清","宋小川","杜广沛"],"is_watched":false},{"rating":["9.6","50"],"rank":3,"cover_url":"https://img2.doubanio.com\/view\/photo\/s_ratio_poster\/public\/p2578474613.webp","is_playable":true,"id":"1292063","types":["剧情","喜剧","爱情","战争"],"regions":["意大利"],"title":"美丽人生","url":"https:\/\/movie.douban.com\/subject\/1292063\/","release_date":"2020-01-03","actor_count":29,"vote_count":1332240,"score":"9.6","actors":["罗伯托·贝尼尼","尼可莱塔·布拉斯基","乔治·坎塔里尼","朱斯蒂诺·杜拉诺","赛尔乔·比尼·布斯特里克","玛丽萨·帕雷德斯","霍斯特·布赫霍尔茨","利迪娅·阿方西","朱利亚娜·洛约迪切","亚美利哥·丰塔尼","彼得·德·席尔瓦","弗朗西斯·古佐","拉法埃拉·莱博罗尼","克劳迪奥·阿方西","吉尔·巴罗尼","马西莫·比安奇","恩尼奥·孔萨尔维","吉安卡尔洛·科森蒂诺","阿伦·克雷格","汉尼斯·赫尔曼","弗兰科·梅斯科利尼","安东尼奥·普雷斯特","吉娜·诺维勒","理查德·塞梅尔","安德烈提多娜","迪尔克·范登贝格","奥梅罗·安东努蒂","沈晓谦","张欣"],"is_watched":false},{"rating":["9.6","50"],"rank":4,"cover_url":"https://img2.doubanio.com\/view\/photo\/s_ratio_poster\/public\/p492406163.webp","is_playable":true,"id":"1295124","types":["剧情","历史","战争"],"regions":["美国"],"title":"辛德勒的名单","url":"https:\/\/movie.douban.com\/subject\/1295124\/","release_date":"1993-11-30","actor_count":49,"vote_count":1110744,"score":"9.6","actors":["连姆·尼森","本·金斯利","拉尔夫·费因斯","卡罗琳·古多尔","乔纳森·萨加尔","艾伯丝·戴维兹","马尔戈萨·格贝尔","马克·伊瓦涅","碧翠斯·马科拉","安德烈·瑟韦林","弗里德里希·冯·图恩","克齐斯茨托夫·拉夫特","诺伯特·魏塞尔","维斯瓦夫·科马萨","布拉德·雅各布维茨","Maciej Orlos","皮奥特·赛尔沃斯","Tadeusz Huk","马丁·塞梅洛格","托马斯·德德克","奥拉夫·卢巴申科","马瑞安·格林卡","约亨·尼克尔","阿格涅兹卡·克鲁科沃娜","阿格尼兹卡·旺格","托马斯·莫里斯","佐久间玲","吴俊全","约阿希姆·保罗·阿斯波克","彭河","戈兹·奥托","玛雅·奥丝塔泽斯卡","Maciej Kozlowski","艾尔文·莱德","Eugeniusz Priwieziencew","Marta Bizon","埃兹拉·达甘","吉恩·莱赫纳","Razia Israeli","拉米·希尔伯格","布兰科·拉斯蒂格","路德格·皮斯特","埃琳娜·勒文松","胡契克·卡勒塔","塔德乌什·布拉德茨基","亨里克·比斯塔","帕维·德朗柯","耶日·诺瓦克","安娜·穆查"],"is_watched":false},{"rating":["9.6","50"],"rank":5,"cover_url":"https://img1.doubanio.com\/view\/photo\/s_ratio_poster\/public\/p1505392928.webp","is_playable":true,"id":"1296141","types":["剧情","犯罪","悬疑"],"regions":["美国"],"title":"控方证人","url":"https:\/\/movie.douban.com\/subject\/1296141\/","release_date":"1957-12-17","actor_count":51,"vote_count":564999,"score":"9.6","actors":["泰隆·鲍华","玛琳·黛德丽","查尔斯·劳顿","埃尔莎·兰彻斯特","约翰·威廉姆斯","亨利·丹尼尔","伊安·沃尔夫","托林·撒切尔","诺玛·威登","尤娜·奥康纳","茹塔·李","贝丝·弗劳尔斯","比尔·厄尔文","J·帕特·奥马利","本·怀特","Paul Kruger","Jack Raine","Paul Power","乔治·佩林","威廉·H·奥布莱恩","奥托拉内史密斯","Frank McClure","Colin Kenny","Jeanne Lafayette","Wilbur Mack","Fred Rapport","利奥达·理查德斯","Glen Walters","Arthur Tovey","伯特史蒂文斯","Cap Somers","Lucille Sewall","斯考特西顿","Norbert Schiller","杰弗里·塞尔","John Roy","Al Roberts","Art Howard","Stuart Hall","Francis Compton","Philip Tonge","帕特里克·艾亨","富兰克林·法纳姆","玛乔丽·伊顿","史蒂夫·卡鲁瑟斯","George Calliga","乔治布鲁格曼","丹尼·鲍沙其","Brandon Beach","埃迪·贝克","沃尔特·培根"],"is_watched":false}]

  有了这些json数据后,我们就可以编写对应的javaBean了,下面我们开始编写代码。

  a.添加相关依赖:

implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'androidx.paging:paging-runtime:2.1.2'

  b.添加网络权限

  c.构建网络请求框架:

    public interface Api {
/**
* 获取电影院当前上映的电影
* https://movie.douban.com/j/chart/top_list?type=11&interval_id=100%3A90&action=&start=40&limit=20
*/
@GET("j/chart/top_list")
Call<List<Movie>> getMovies(@Query("start") int start, @Query("limit") int limit);
}

  

public class RetrofitClient {
private static final String BASE_URL="https://movie.douban.com";
private static RetrofitClient retrofitClient;
private final Retrofit retrofit; private RetrofitClient(){
retrofit=new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create()) //添加转换器
.client(getClient()) //添加OkHttp的拦截器
.build();
}
public static synchronized RetrofitClient getInstance(){
if(retrofitClient==null){
retrofitClient=new RetrofitClient();
}
return retrofitClient;
}
public Api getApi(){
return retrofit.create(Api.class);
}
private OkHttpClient getClient(){
OkHttpClient client=new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException { //拦截器用于添加请求参数,会在源url上进行修改
Request original = chain.request();
HttpUrl url = original.url();
HttpUrl currentUrl = url.newBuilder()
.addQueryParameter("interval_id", "100:90")
.addQueryParameter("type", "11")
.addQueryParameter("action", "")
.build();
Request newRequest = original.newBuilder()
.url(currentUrl)
.build();
return chain.proceed(newRequest);
}
})
.build();
return client;
}
}

  d.创建Model类

public class Movie{

    private List<String> rating;
private Integer rank;
private String cover_url;
private String is_playable;
private String id;
private List<String> types;
private List<String> regions;
private String title;
private String url;
private String release_date;
private Integer actor_count;
private Integer vote_count;
private String score;
private List<String> actors;
private String is_watched; public List<String> getRating() {
return this.rating;
} public void setRating(List<String> rating) {
this.rating = rating;
} public Integer getRank() {
return this.rank;
} public void setRank(Integer rank) {
this.rank = rank;
} public String getCover_url() {
return this.cover_url;
} public void setCover_url(String cover_url) {
this.cover_url = cover_url;
} public String getIs_playable() {
return this.is_playable;
} public void setIs_playable(String is_playable) {
this.is_playable = is_playable;
} public String getId() {
return this.id;
} public void setId(String id) {
this.id = id;
} public List<String> getTypes() {
return this.types;
} public void setTypes(List<String> types) {
this.types = types;
} public List<String> getRegions() {
return this.regions;
} public void setRegions(List<String> regions) {
this.regions = regions;
} public String getTitle() {
return this.title;
} public void setTitle(String title) {
this.title = title;
} public String getUrl() {
return this.url;
} public void setUrl(String url) {
this.url = url;
} public String getRelease_date() {
return this.release_date;
} public void setRelease_date(String release_date) {
this.release_date = release_date;
} public Integer getActor_count() {
return this.actor_count;
} public void setActor_count(Integer actor_count) {
this.actor_count = actor_count;
} public Integer getVote_count() {
return this.vote_count;
} public void setVote_count(Integer vote_count) {
this.vote_count = vote_count;
} public String getScore() {
return this.score;
} public void setScore(String score) {
this.score = score;
} public List<String> getActors() {
return this.actors;
} public void setActors(List<String> actors) {
this.actors = actors;
} public String getIs_watched() {
return this.is_watched;
} public void setIs_watched(String is_watched) {
this.is_watched = is_watched;
} @Override
public String toString() {
return "Movie{" +
"rating=" + rating +
", rank=" + rank +
", cover_url='" + cover_url + '\'' +
", is_playable='" + is_playable + '\'' +
", id='" + id + '\'' +
", types=" + types +
", regions=" + regions +
", title='" + title + '\'' +
", url='" + url + '\'' +
", release_date='" + release_date + '\'' +
", actor_count=" + actor_count +
", vote_count=" + vote_count +
", score='" + score + '\'' +
", actors=" + actors +
", is_watched='" + is_watched + '\'' +
'}';
}
}

  e.编写一个类继承PositionalDataSource,并在此类中进行网络请求,获取服务器返回的数据。

public class MovieDataSource extends PositionalDataSource<Movie> {
public static final int pageSize=5;
@Override
public void loadInitial(@NonNull LoadInitialParams loadInitialParams, @NonNull LoadInitialCallback<Movie> loadInitialCallback) {//负责第一页数据的加载
int startPosition=0;
RetrofitClient.getInstance()
.getApi()
.getMovies(startPosition,pageSize)
.enqueue(new Callback<List<Movie>>() {
@Override
public void onResponse(Call<List<Movie>> call, Response<List<Movie>> response) {
if(response.body()!=null){
loadInitialCallback.onResult(response.body(),0,250);//将数据返回给PagedList,250是指数据源中的数据总数,当调用了setEnablePlaceHolder(true)方法时,必须传入此参数,以便预留位置
}
} @Override
public void onFailure(Call<List<Movie>> call, Throwable t) { }
});
} @Override
public void loadRange(@NonNull LoadRangeParams loadRangeParams, @NonNull LoadRangeCallback<Movie> loadRangeCallback) {//负责第一页之后数据的加载
RetrofitClient.getInstance()
.getApi()
.getMovies(loadRangeParams.startPosition,pageSize) //在你滑动手机屏幕到底部请求下一页的数据时,loadRangeParams.startPosition会自动维护,不需要你手动修改
.enqueue(new Callback<List<Movie>>() {
@Override
public void onResponse(Call<List<Movie>> call, Response<List<Movie>> response) {
if(response.body()!=null){
loadRangeCallback.onResult(response.body());
}
} @Override
public void onFailure(Call<List<Movie>> call, Throwable t) { }
});
}
}

  f.创建MovieDataSourceFactory类,负责MovieDataSource的创建。

public class MovieDataSourceFactory extends DataSource.Factory<Integer, Movie>{
@NonNull
@Override
public DataSource<Integer, Movie> create() {
return new MovieDataSource();
}
}

  g.有了Factory类之后,接下来需要创建ViewModel类,在这个类中通过LivePagedListBuilder类创建和配置PagedList,并使用LiveData包装PagedList,然后在MainActivity中监测PagedList中数据的变化,并更新页面。

public class MovieViewModel extends ViewModel {
public LiveData<PagedList<Movie>> moviePagedList;
public MovieViewModel(){
PagedList.Config config=new PagedList.Config.Builder()
.setEnablePlaceholders(true) //设置是否为那些数量已知,但尚未加载出来的数据预留位置
.setPageSize(5)
.setPrefetchDistance(3) //设置当距离底部还有多少数据时加载下一页数据
.setInitialLoadSizeHint(20) //设置首次加载数据的数量
.setMaxSize(65536*5) 
.build();
moviePagedList=new LivePagedListBuilder<>(new MovieDataSourceFactory(),config).build(); //MovieDataSource中的onResult方法会把服务器返回的数据传递到PagedList当中
}
}

  h.编写RecyclerView的适配器类,此类需要继承自PagedListAdapter。

public class MoviePagedListAdapter extends PagedListAdapter<Movie, MoviePagedListAdapter.ViewHolder> {
private final Context context;
public MoviePagedListAdapter(Context context){
super(diffCallback);
this.context=context;
}
private static final DiffUtil.ItemCallback<Movie> diffCallback=new DiffUtil.ItemCallback<Movie>() {
@Override
public boolean areItemsTheSame(@NonNull Movie oldItem, @NonNull Movie newItem) {//根据id来判断两条数据是否是同一条数据
return oldItem.getId().equals(newItem.getId());
} @SuppressLint("DiffUtilEquals")
@Override
public boolean areContentsTheSame(@NonNull Movie oldItem, @NonNull Movie newItem) {//比较两条数据的内容是否一样
return oldItem.equals(newItem);
}
};
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater=LayoutInflater.from(context);
MovieItemBinding movieItemBinding= DataBindingUtil.inflate(inflater,R.layout.movie_item,parent,false);
return new ViewHolder(movieItemBinding);
} @Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Movie movie = getItem(position);//从PagedList中获取数据,如果没有的话,PagedList会通知DataSource获取下一页的数据
if(movie!=null){
holder.movieItemBinding.setMovie(movie);
}
} public static class ViewHolder extends RecyclerView.ViewHolder{
public MovieItemBinding movieItemBinding;
public ViewHolder(MovieItemBinding movieItemBinding) {
super(movieItemBinding.getRoot());
this.movieItemBinding=movieItemBinding;
}
}
}

  i.MainActivity实现:

public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding activityMainBinding= DataBindingUtil.setContentView(this,R.layout.activity_main);
activityMainBinding.recyclerView.setLayoutManager(new LinearLayoutManager(this));
MoviePagedListAdapter moviePagedListAdapter=new MoviePagedListAdapter(this);
activityMainBinding.recyclerView.setAdapter(moviePagedListAdapter);
MovieViewModel movieViewModel = new ViewModelProvider(this).get(MovieViewModel.class);
movieViewModel.moviePagedList.observe(this, new Observer<PagedList<Movie>>() {
@Override
public void onChanged(PagedList<Movie> movies) {
moviePagedListAdapter.submitList(movies);//当PagedList数据发生变化时,通知适配器更新数据,然后用getItem()方法获取数据
}
});
}
}

  j.布局文件:

  activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/recyclerView"/>
</LinearLayout>
</layout>

  movie_item.xml:

<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="movie"
type="com.example.paging.model.Movie" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/movieName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{movie.title}"/>
</LinearLayout>
</layout>

  到这里,就可以运行程序,查看效果了!

  不过,我在写代码的时候,遇到了一个bug调了很久,就是当我在MainActivity中将activityMainBinding.recyclerView.setHasFixedSize(true);这句代码加上时,加载不出来任何的数据;但是如果我将RecyclerView组件的布局高度改成match_parent后,即使设置了setHasFixedSize(true)也能加载出来数据,也不知道咋回事。

  

paging组件的用法和意义的更多相关文章

  1. Jetpack系列:Paging组件帮你解决分页加载实现的痛苦

    相信很多小伙伴们在项目实战中,经常会用到界面的分页显示.加载更多等功能.需要针对具体功能做针对性开发和调试,耗时耗力. Paging组件的使用将这部分的工作简化,从而让开发者更专注于业务的具体实现.下 ...

  2. Ogre 1.8 terrain 和 paging 组件

    以下转自:http://hi.baidu.com/xocoder/item/e8d87cf53d87612b753c4cfd OGRE地形生成 OGRE可以通过两个接口来生成地形,分别是void Te ...

  3. java Future用法和意义一句话击破

    在并发编程时,一般使用runnable,然后扔给线程池完事,这种情况下不需要线程的结果. 所以run的返回值是void类型. 如果是一个多线程协作程序,比如菲波拉切数列,1,1,2,3,5,8...使 ...

  4. TCPClient组件和TCPServer组件的主要方法和属性

    IdTCPClient属性1 : IOHandler 如果有相应的输入/输出操作,那么IOHandler相对应的组件或接口将提供一个虚拟/抽象的输入/输出接口给相应的网络连接2 : Intercept ...

  5. Svn中的tag标签的用法和意义

    使用场景: 假如你的项目的某个版本已经完成测试开发.测试并已经上线,接下来街道新的需求,新项目开发需要修改多个文件的代码,当需求已经开发一段时间的时候,突然接到用户和测试人员的反馈,项目中某个重大的b ...

  6. vue组件最佳实践

    看了老外的一篇关于组件开发的建议(强烈建议阅读英文原版),感觉不错翻译一下加深理解. 这篇文章制定一个统一的规则来开发你的vue程序,以至于达到一下目的. 1.让开发者和开发团队更容易发现一些事情. ...

  7. react - 解刨组件的多种写法

    一,原始的createClass写法 对于写react组件,很多人第一印象往往是createClass,这是因为createClass是react组件最原始的写法,基本每个学react的人都是接触这种 ...

  8. ionic3+angular4开发混合app 之自定义组件

    这里主要是记录ionic3+angular4开发混合app时自定义组件,我想自定义组件的方法和angular4应该类似,具体在纯angular4中自定义组件,暂时没有实践,个人觉得差别不大,之后实践了 ...

  9. Android之ASD组件(一)

    Google在android5.0之后推出新设计标准Material Design,为了能在低版本上使用Material Design,google发布了Android Support Design支 ...

  10. Vue.js 组件编码规范

    本规范提供了一种统一的编码规范来编写 Vue.js 代码.这使得代码具有如下的特性: 其它开发者或是团队成员更容易阅读和理解. IDEs 更容易理解代码,从而提供高亮.格式化等辅助功能 更容易使用现有 ...

随机推荐

  1. getattr函数

    函数说明 getattr(object, name, default=None) object:类实例 name:str 属性名 default:str 如果没有查找到,用该值替代 用法实现反射,主要 ...

  2. < Python全景系列-5 > 解锁Python并发编程:多线程和多进程的神秘面纱揭晓

    欢迎来到我们的系列博客<Python全景系列>!在这个系列中,我们将带领你从Python的基础知识开始,一步步深入到高级话题,帮助你掌握这门强大而灵活的编程语法.无论你是编程新手,还是有一 ...

  3. JS 数组常用操作全集

    文章目录 1.push()方法 2.unshift()方法 3.pop() 方法 4.shift() 方法 5.filter() 方法 6.join()方法 7. indexOf() 方法 8.rev ...

  4. 执行pod setup 报错error: RPC failed; curl 18 transfer closed with outstanding read data remainin

    执行pod setup 报错 error: RPC failed; curl 18 transfer closed with outstanding read data remaining fatal ...

  5. INFINI Labs 产品更新 | Console 新增数据比对、新增数据看板表格组件及支持下钻功能等

    INFINI Labs 产品更新啦~,本次产品版本更新包括 Gateway v1.14.0.Console v1.2.0.Easysearch v1.1.1 等,其中 Console 在上一版基础上做 ...

  6. 曲线艺术编程 coding curves 第五章 谐波图形(谐振图形) HARMONOGRAPHS

    原作:Keith Peters https://www.bit-101.com/blog/2022/11/coding-curves/ 译者:池中物王二狗(sheldon) blog: http:// ...

  7. JuiceFS 社区版 v1.1- Beta 发布,新增五个实用功能

    我们很高兴地宣布 JuiceFS v1.1-Beta 版本正式发布啦!这是一个功能丰富的版本,带来了许多实用的新功能和改进.在这个版本中我们新增了以下功能: 目录配额:为目录设置配额限制,控制其大小和 ...

  8. 【后端面经】MySQL主键、唯一索引、联合索引的区别和作用

    目录 0. 简介 1. 主键 2. 唯一索引 3. 联合索引 4. 索引对数据库操作的影响 5. 其他索引 5.1 普通索引 5.2 全文索引 5.3 前缀索引 6. 总结 7. 参考资料 0. 简介 ...

  9. 自然语言处理 Paddle NLP - 开放域对话系统-理论

    常见的对话系统 个人助手 •Apple Siri, Amazon Alexa, Baidu Xiaodu 客户服务•餐厅预定.商品咨询.债务催收 休闲娱乐 •Microsoft XiaoIce,Rep ...

  10. 力扣 (LeetCode)算法入门——Day1

    704. 二分查找 题目:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1. ...