热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Dagger依赖注入框架(入门)

目录Dagger简介Dagger1.x,Dagger2.x简单对比Dagger中的注解@Inject@Provide@Module@Component@Bindsmultibind

目录

    1. Dagger简介
    1. Dagger1.x,Dagger2.x简单对比
    1. Dagger中的注解
    • @Inject
    • @Provide
    • @Module
    • @Component
    • @Binds
    • multibinds
    • 其他关键注解
  • 4.使用
  • 5.Dagger & Android
    • Activity的注入
    • Fragment的注入

Dagger官方文档
Dagger&Android
Dagger分享(这一篇笔记也很好,内容全面易懂,排版也很清晰,可以参考学习)
DaggerDamo(文中的Dagger&Android的Demo都是参考这里写的)

1. Dagger简介:

依赖注入的框架。依赖注入的目的——解耦。

依赖注入的常规方式

如果class A中有class B的实例,就可以说A依赖于B,如MVP中,V(Activity)持有P(Presenter),V依赖于P,这个例子存在一个问题,如果有一天,B的构造方法变了,那我们就需要修改A中创建B的代码,而且所有创建B实例的地方,都需要修改。
———— 如果使用Dagger来进行依赖注入,只需要用@Inject和其他注解就能实现。

依赖注入的几种方式

  1. 构造函数注入

// Constructor
Client(Service service) {
// Save the reference to the passed-in
// service inside this client
this.service = service;
}

  1. setter方法注入

// Setter method
public void setService(Service service) {
// Save the reference to the passed-in
// service inside this client
this.service = service;
}

  1. 接口注入

// Service setter interface.
public interface ServiceSetter {
public void setService(Service service);
}
// Client class
public class Client implements ServiceSetter {
// Internal reference to the service used by this
// client.
private Service service;
// Set the service that this client is to use.
@Override
public void setService(Service service) {
this.service = service;
}
}

2. Dagger1.x -> Dagger2.x

更好的性能:相较于Dagger1,它使用的预编译期间生成代码来完成依赖注入,而不是用的反射。大家知道反射对手机应用开发影响是比较大的,因为反射是在程序运行时加载类来进行处理所以会比较耗时,而手机硬件资源有限,所以相对来说会对性能产生一定的影响。
容易跟踪调试:因为dagger2是使用生成代码来实现完整依赖注入,所以完全可以在相关代码处下断点进行运行调试.

3. Dagger中的注解

@Inject

@Inject,两个作用,其一:用来标记构造函数,Dagger可以调用它创建该类的实例,对外提供依赖。其二:标记依赖字段,表示该字段由Dagger提供实例。也就是标记依赖源和依赖宿主。

如果一个类有@Inject注解的字段,但是没有@Inject注解的构造器(注入jar包中的类),可以使用方法注入——@Provide注解。

@Inject不生效的地方:

  1. 注解接口不生效,因为接口不能被实例化。
  2. 注入第三方jar包里的类不生效,因为这样的类不能被@Inject注解。
  3. Configurable objects must be configured!
    对于这些不能使用@Inject进行注入的地方,可以使用@Provider注解的方法提供依赖。

@Provide

@Provide用来注解方法,该方法用于提供依赖。当需要Dagger提供依赖注入的时候,这个方法会被调用——创建依赖对象。

@Module

@Module用于注解类,该类用于提供依赖。

@Component

@Component一般用来标注接口,被标注了Component的接口,在编译时会由Dagger生成接口的实例,作为提供依赖方(Module)和依赖宿主的桥梁,或者说注入器,把相关依赖注入到宿主容器中。

@Component(modules = DripCoffeeModule.class)
public interface CoffeeShop {
CoffeeMaker maker();
}

Component中有两个方法:

  • Provision:Provision方法如上,不接收参数,返回值是被注入的依赖类型,这个class必须有@Inject注解的构造器,或者有module创建这个依赖的实例。
  • Member-injection: Member-injection方法有一个参数,用于传入依赖的宿主,如果依赖宿主不能由Dagger实例化(如Activity),就可以使用这种方式进行注入。

void injectMembers(T)

@Binds

如果我们需要一个一般化的类型,而Dagger的object graph中已经一个该类型的子类,那么用Provide是这样做的:

@Provides
static Pump providePump(Thermosiphon pump) {
return pump;
}

Binds 注解可以这么做:

@Binds
static abstract Pump providePump(Thermosiphon pump);

@Binds 用于注解方法声明,如果 module 是接口,那么 binds 方法就是一个方法声明,如果 module 是类,那么 binds 方法就是一个abstract方法。 binds 方法有且只有一个参数,这个参数可以赋值给返回值类型。也就是说参数是返回接口的子类。

注意 Module 不能同时用 Binds 方法和 Provide 非static方法,因为 Binds 方法只是一个方法声明没有实现,一旦 Module 有了 Provide 方法(非static),意味着这个 Module 必须实例化,所以方法声明就必须得有实现,这便起了冲突。当然,如果 Provide 方法是static的,那也是可以的。

multibinds

multibinds不是一个注解,而是用于multibinds的一组注解。用于将对象绑定到集合中。

  1. 注解 Provider 方法,表示提供的对象会被注入 Set/Map。

Set:

  • @IntoSet 将一个元素注入 Set
  • @ElementsIntoSet 将一个集合注入 Set

Map
@IntoMap 表示该方法的返回值作为 Map 的 value 注入 Map 中,另外 Key 由下面的注解提供:

  • @Stringkey 提供字符串作为 key
  • @IntKey 提供 int
  • @ClassKey 提供 class
  • 也可用 @MapKey 自定义 key 的类型
    key 都是常量
    在dagger-android中我们会看到 @ActivityKey 注解,就是通过@MapKey 定义的。
  1. 注解集合Set/Map,@Multibinds
    对于 @Inject 注解的集合,找不到元素提供方的话,dagger 会在编译期报错:

java.util.Map cannot be provided without an @Provides- or @Produces-annotated method.

如果有在 Module 中声明了 @Multibinds,便不会报错,而是得到一个空集合。具体例子可以看dagger-android中的Activity注入。

注意:Module注解的优先级高于构造器注解。

其他关键注解

@Scope: Dagger2可以通过自定义注解限定注解作用域,来管理每个对象实例的生命周期,在它的生命周期范围内,提供单例。

@Qualifier: 有时只靠类型,不足以识别一个依赖,可能无法找到依赖提供方。比如我们注入的是一个特殊的接口实现,与默认实现有区别(可能是构造参数不同),这时,我们需要@Qualifiers声明一个额外的注解,用于区分依赖。

例如:在Android中,我们会需要不同类型的context,所以我们就可以定义 Qualifier注解@perApp@perActivity,这样当注入一个context的时候,我们就可以告诉 Dagger我们想要哪种类型的context。

@Singleton:可以在@Providers方法或依赖类声明中使用该注解。可在全局范围内提供注入类的单例。

@Provides
@Singleton
static Heater provideHeater() {
return new ElectricHeater();
}
@Singleton
class CoffeeMaker {
...
}

Component需要和Scope关联起来,一个Component不能既有@Singleton的依赖,又有@RequestScoped的依赖,因为二者的作用域不同,生命周期也不同。

@Component(modules = DripCoffeeModule.class)
@Singleton
interface CoffeeShop {
CoffeeMaker maker();
}

4.使用

背景:假设我们有一个咖啡机(CoffeeMaker),可以加热咖啡(Heater),有泵可以抽出咖啡(Pump)。现在,我们需要在CoffeeMaker中注入这两个装置,完成功能组合。

1. 定义依赖接口

//加热装置
public interface Heater {
void on();
void off();
boolean isHot();
}
public interface Pump {
void pump();
}

2.定义依赖宿主CoffeeMaker,使用@Inject注入依赖。

注意:我们注入的依赖,除了有@Inject注解的字段之外,还需要通过构造函数参数传入。

public class CoffeeMaker {
@Inject Heater heater;
@Inject Pump pump;
CoffeeMaker(Heater heater, Pump pump) {
this.heater = heater;
this.pump = pump;
}
public void brew() {
heater.on();
pump.pump();
System.out.println(" [_]P coffee! [_]P ");
heater.off();
}
}

3.使用@Module,定义依赖提供方。

ElectricHeater是Heater的实现类。Thermosiphon是Pump的实现类。

@Module
public class DripCoffeeModule {
@Provides static Heater provideHeater() {
return new ElectricHeater();
}
@Provides static Pump providePump(Thermosiphon pump) {
return pump;
}
}

4.使用@Component定义注入器接口,完成Module和依赖宿主的关联。

@Component(modules = DripCoffeeModule.class)
public interface CoffeeShop {
CoffeeMaker maker();
}

返回的CoffeeMaker对象也由Dagger提供。修改CoffeeMaker的构造函数,添加@Inject

@Inject
CoffeeMaker(Heater heater, Pump pump) {
this.heater = heater;
this.pump = pump;
}

5.在Activity或Main方法中通过Component获取CoffeeMaker实例。

可以发现,我们并没有实现DaggerCoffeeShop,这里为什么可以调用呢?因为在编译期间,Dagger自动为我们的Component接口生成实现类,DaggerCoffeeShop就是CoffeeShop接口的实例.
dripCoffeeModule (),该方法根据Component绑定的Module自动生成,传入Module实例。

CoffeeShop coffeeShop = DaggerCoffeeShop.builder()
.dripCoffeeModule(new DripCoffeeModule())
.build();
coffeeShop.maker().brew();

以上,就完成了依赖注入。
但是存在一个问题:Module对应一个默认的构造器,Dagger在编译期间生成的DaggerCoffeeShop,可以访问Module,获取依赖实例。ModuleProvide方法是静态的,不需要我们手动构造Module实例就可以访问其Provide方法。—— 所以,我们可以使用create()代替build()

CoffeeShop coffeeShop = DaggerCoffeeShop.create();
coffeeShop.maker().brew();

我们使用Component关联依赖提供方法(Module)和依赖宿主。并返回CoffeeMaker对象,这个CoffeeMaker对象如何获取?

  1. 会通过Dagger。先找有没有Module提供CoffeeMaker对象,如果没有,再找有没有@Inject注解的构造器。
  2. 找到构造器之后,发现CoffeeMaker构造器有两个参数,那个这两个参数也是注入的。
  3. 再找有没有提供参数的Provider方法,因为之前声明了Component接口,把ModuleCoffeeMaker关联了,这里会去DripCoffeeModuleProvider方法。这里也就解释了为什么还需要在CoffeeMaker中传入依赖,最为构造函数的参数。
  4. 最终完成注入

5.Dagger & Android

Dagger相比于其他依赖注入框架,优点在于它完全静态生成依赖。
Dagger在Android中使用的特点在于,Android中很多组件是由Framework进行实例化的。常规的注入方式,是通过构造函数参数传递依赖。但是在Android中,显然不能由Dagger创建Activity/Fragment,无法访问到其构造函数。所以,我们必须在组件的生命周期方法内进行成员的注入。

常规的注入方式,通过构造函数传递依赖

public class CoffeeMaker {
//通过Module提供注入——DripCoffeeModule
@Inject Heater heater;
@Inject Pump pump;
@Inject
CoffeeMaker(Heater heater, Pump pump) {
this.heater = heater;
this.pump = pump;
}
}

Android中的注入

/**
* 给Application用的component,可以作为一个全局的component,如果其他的Activity需要使用,从Appliction中获取,以此达到复用的目的。
* 将依赖提供方AppModule和依赖宿主FeatureActivity关联起来
*/
@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
//需要我们自己声明builder,通常builder都是Dagger自动生成的,但是在Android中注入Activity,需要我们自己声明,提供component
@Component.Builder
interface Builder {
@BindsInstance
Builder application(App application);
AppComponent build();//提供获取组件的方法,这个组件bind到了Application上
}
/**
* Member-injection方法,用于传入依赖的宿主,对于Activity这样的组件,不能由Dagger进行实例化,无法在其构造器上添加注入的参数,需要中这种方式进行注入
*/
void inject(FeatureActivity featureActivity);
}
@Module
public class AppModule {
@Provides
Context provideContext(App application) {
return application.getApplicationContext();
}
@Singleton
@Provides
SomeClientApi provideSomeClientApi() {
return new SomeClientApiImpl();
}
}
public class App extends Application {
//将Component绑定到Application,Activity需要使用时,通过Application获取该组件,达到复用的目的
private AppComponent appComponent;
@Override
public void onCreate() {
super.onCreate();
appCompOnent= DaggerAppComponent
.builder()
.application(this)
.build();
}
public AppComponent getAppComponent() {
return appComponent;
}
}

在Android中,framework为我们初始化了Activity,除了使用@Inject注解注入的变量之外,我们需要调用component的members injection方法完成注入。

public class FeatureActivity extends AppCompatActivity {
//在Activity中注入api,通过Application获取
@Inject
SomeClientApi mSomeClientApi;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//在Activity的生命周期方法内完成注入
((App) getApplication())
.getAppComponent()
.inject(this);
//调用注入的依赖
mSomeClientApi.report();
}
}

以上是在Activity注入一个依赖的全部步骤,但是这样的注入方式带来的问题:

  • 在其他需要注入这个依赖的Activity中,会在onCreate()写这样的重复代码。
  • 被注入方——Activity,它知道我们是如何注入一个依赖的,用什么注入器(Component),注入器怎么调用。这样违反了依赖注入的原则:一个类不需要知道它如何被注入依赖的。

使用dagger-android module进行依赖注入

1. 添加dagger-android依赖

// Dagger core dependencies
implementation 'com.google.dagger:dagger:2.16'
annotationProcessor 'com.google.dagger:dagger-compiler:2.16'
// Dagger Android dependencies
implementation 'com.google.dagger:dagger-android:2.16'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.16'
implementation 'com.google.dagger:dagger-android-support:2.16' // if you use the support libraries

2. 定义一个@Subcomponent接口

该component实现 AndroidInjector接口,并声明其内部抽象类Builder,继承AndroidInjector.Builder

@Subcomponent
public interface FeatureSubComponent extends AndroidInjector {
@Subcomponent.Builder
public abstract class Builder extends AndroidInjector.Builder {}
}

3. 定义一个Module,用来bind subComponent的Builder

@Module(subcompOnents= FeatureSubComponent.class)
public abstract class BuildersModule {
@Binds
@IntoMap
@ActivityKey(FeatureActivity.class)
abstract AndroidInjector.Factory bindFeatureActivityInjectorFactory(FeatureSubComponent.Builder builder);
// Add more bindings here for other sub components
}

把这个Module绑定到Application的注入容器(Component)中.此外还需要注入AndroidSupportInjectionModule

@Singleton
@Component(modules = {AndroidSupportInjectionModule.class, AppModule.class, BuildersModule.class})
public interface AppComponent {
// void inject(FeatureActivity featureActivity);
//这里不再注入Activity,而是注入到Application中
void inject(App app);
}

4.改造Application

实现HasActivityInjector接口,实现接口的activityInjector()方法返回注入的 DispatchingAndroidInjector
最后在onCreate()中进行注入。

public class App extends Application implements HasActivityInjector{
@Inject
DispatchingAndroidInjector dispatchingActivityInjector;
@Override
public void onCreate() {
super.onCreate();
DaggerAppComponent.create()
.inject(this);
}
@Override
public AndroidInjector activityInjector() {
return dispatchingActivityInjector;
}
}

5. 在Activity中注入依赖

移除原来的注入代码,改为在super.onCreate()前调用AndroidInjection.inject(this)

public class FeatureActivity extends AppCompatActivity {
@Inject
SomeClientApi mSomeClientApi;
@Override
protected void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//调用依赖的方法
mSomeClientApi.report();
}
}

注入的依赖,需要宿主提供参数

有时我们注入的依赖,它的实例化参数需要宿主方提供,在没有dagger-android 的时候,需要我们在Activity中调用inject()进行注入之前,就将Activity作为参数传给module。如:在MVP模式中,实例化P的时候,需要传入V的实例,需要我们在把Activity作为Module的构造函数的参数进行传递,再添加provider函数提供Activity。

@Module
class FeatureModule {
private FeatureView view;
//构造module的时候传入view
public FeatureModule(FeatureView view) {
this.view = view;
}
//再把这个view提供出去
@Provides FeatureView provideView() {
return view;
}
}
//presenter中使用构造函数注入
class FeaturePresenter {
private final FeatureView view; @Inject
Presenter(FeatureView view) {
this.view = view;
}
public void doSomething() {
}
}
public class FeatureActivity extends AppCompatActivity implements FeatureView {
@Inject FeaturePresenter presenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//在Activity中实例化FeatureModule,并把Activity作为参数
DaggerFeatureComponent.builder()
.featureModule(FeatureModule(this)).build()
.inject(this)

// presenter ready to be used
presenter.doNothing();
}
}

但是在dagger-android中,Activity只调用AndroidInjection.inject(this)进行注入,不在Activity中初始化Module。应该怎么进行注入呢?使用dagger-android的时候,activity实例已经存在于dagger的对象图中了,使用@Binds,我们可以从对象图中,取出activity作为参数,具体代码如下:

@Module
public abstract class FeatureModule {
//使用Binds表示从Dagger的对象图中获取FeatureActivity实例,然后将其赋值给FeatureConstrant.View,参数是返回值的子类
@Binds
abstract FeatureConstrant.View provideFutureView(FeatureActivity featureActivity);
}

在presenter中添加inject构造器,view作为参数。

public class FeaturePresenter implements FeatureConstrant.Presenter {
private FeatureConstrant.View mView;
@Inject
public FeaturePresenter(FeatureConstrant.View view) {
this.mView = view;
}
//业务代码
}

在component中加入这个module,最后在view中添加@Inject注解的presenter字段就OK。

@Singleton
@Component(modules = {AndroidSupportInjectionModule.class, AppModule.class, BuildersModule.class, FeatureModule.class})
public interface AppComponent {
//...
}

注入Fragment

Fragment的注入和Activity的注入流程差不多。步骤如下:

  1. Activity实现HasFragmentInjector接口。提供DispatchingAndroidInjector对象。和Activity的注入有区别,Fragment是在Activity中加载的,所以在Activity中实现接口。

public class FeatureActivity extends AppCompatActivity HasFragmentInjector{
@Inject
DispatchingAndroidInjector fragmentInjector;
@Override
protected void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
}
@Override
public AndroidInjector fragmentInjector() {
return fragmentInjector;
}
//other function
}

  1. 在Fragment中添加依赖,并调用AndroidInjection.inject(this)进行依赖注入。注意inject()需要在onAttach()super.onAttach(activity)前调用。

public class TextFragment extends Fragment {
//注入的依赖
@Inject
SomeClientApi mSomeClientApi;
@Override
public void onAttach(Activity activity) {
AndroidInjection.inject(this);
super.onAttach(activity);
}
//...
}

  1. 定义subComponent,实现AndroidInjector接口,定义内部类Builder。

@Subcomponent
public interface FragmentSubComponent extends AndroidInjector {
@Subcomponent.Builder
public abstract class Builder extends AndroidInjector.Builder {}
}

4.把SubComponent的Builder绑定到Module。使用FragmentKey注解内部方法。

@Module(subcompOnents= {OtherSubComponent.class, FragmentSubComponent.class})
public abstract class BuildersModule {
//add fragment sub component
@Binds
@IntoMap
@FragmentKey(TextFragment.class)
abstract AndroidInjector.Factory bindFragmentInjectorFratory(FragmentSubComponent.Builder builder);
// Add more bindings here for other sub components
}

5.把Module绑定到Component。

@Singleton
@Component(modules = {AndroidSupportInjectionModule.class, BuildersModule.class, OtherModule.class})
public interface AppComponent {
//这里不再注入Activity,而是注入到Application中
void inject(App app);
}

6.总结

这一部分先到这里,后续想到再补充。


推荐阅读
  • Imtryingtofigureoutawaytogeneratetorrentfilesfromabucket,usingtheAWSSDKforGo.我正 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • 电话号码的字母组合解题思路和代码示例
    本文介绍了力扣题目《电话号码的字母组合》的解题思路和代码示例。通过使用哈希表和递归求解的方法,可以将给定的电话号码转换为对应的字母组合。详细的解题思路和代码示例可以帮助读者更好地理解和实现该题目。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • IhaveconfiguredanactionforaremotenotificationwhenitarrivestomyiOsapp.Iwanttwodiff ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 本文介绍了如何清除Eclipse中SVN用户的设置。首先需要查看使用的SVN接口,然后根据接口类型找到相应的目录并删除相关文件。最后使用SVN更新或提交来应用更改。 ... [详细]
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • ALTERTABLE通过更改、添加、除去列和约束,或者通过启用或禁用约束和触发器来更改表的定义。语法ALTERTABLEtable{[ALTERCOLUMNcolu ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • 本文讨论了一个数列求和问题,该数列按照一定规律生成。通过观察数列的规律,我们可以得出求解该问题的算法。具体算法为计算前n项i*f[i]的和,其中f[i]表示数列中有i个数字。根据参考的思路,我们可以将算法的时间复杂度控制在O(n),即计算到5e5即可满足1e9的要求。 ... [详细]
author-avatar
一个不起眼的小人物
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有