Flutter中的Key

在Flutter 中,Key 是 几乎所有 widget 都具有的属性。为什么 widget 具有 Key 呢?Key的作用是什么?

什么是 Key

Key是Widget、Element 和 SemanticNodes 的标识符。 Key 是Widget、Element 和 SemanticNodes的唯一标识。例如对于 Widget 在 Widget 树中改变了位置,Key 可以帮助它们保留状态。相对于无状态的Widget,Key对于有状态的 Widget 作用更大。

 Key的使用场景

在添加、删除或重排同一类型的 widget 集合时,Key 可以让这些 widget 保持状态,并且在 widget 树中处于相同的级别。

例子:两个颜色块单击按钮时交换两者位置。

两种实现方式:

第一种实现:widget 是无状态的,色值保存在 widget 本身中。当点击 FloatingActionButton,色块会交换位置。

import 'dart:math';import 'package:flutter/material.dart';//色块widget是无状态的
class StatelessColorTitles extends StatelessWidget {//色值保存在本身控件中var r = Random().nextInt(256);var g = Random().nextInt(256);var b = Random().nextInt(256);@overrideWidget build(BuildContext context) {return Container(height: 100,width: 100,color: Color.fromRGBO(r, g, b, 1),);}
}class PositionTiles extends StatefulWidget {const PositionTiles({Key? key}) : super(key: key);@overrideState<StatefulWidget> createState() {return _PositionTilesState();}
}class _PositionTilesState extends State<PositionTiles> {List<Widget> titles = [];@overridevoid initState() {super.initState();titles = [StatelessColorTitles(), StatelessColorTitles()];}@overrideWidget build(BuildContext context) {return Scaffold(body: Center(child: Row(mainAxisAlignment: MainAxisAlignment.center,children: titles,),),floatingActionButton: FloatingActionButton(child: const Icon(Icons.sentiment_very_satisfied),onPressed: swapTitles,),);}void swapTitles() {setState(() {titles.insert(1, titles.removeAt(0));});}
}class MyCustomApp extends StatelessWidget {const MyCustomApp({super.key});@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue),home: const PositionTiles(),);}
}

下面是第二种实现:色块 widget 有状态,色值保存在状态中。为了正确交换平铺位置,我们需要向有状态的 widget 添加 key 参数。

import 'dart:math';import 'package:flutter/material.dart';class _StatefulColorTilesState2 extends State<StatefulColorTiles2> {//色值保存在本身控件中var r = Random().nextInt(256);var g = Random().nextInt(256);var b = Random().nextInt(256);@overridevoid initState() {super.initState();}@overrideWidget build(BuildContext context) {return Container(height: 100,width: 100,color: Color.fromRGBO(r, g, b, 1),);}
}class StatefulColorTiles2 extends StatefulWidget {const StatefulColorTiles2({Key? key}) : super(key: key);@overrideState<StatefulWidget> createState() {return _StatefulColorTilesState2();}
}class PositionTiles2 extends StatefulWidget {const PositionTiles2({Key? key}) : super(key: key);@overrideState<StatefulWidget> createState() {return _PositionTilesState2();}
}class _PositionTilesState2 extends State<PositionTiles2> {List<Widget> titles = [];@overridevoid initState() {super.initState();titles = [//添加了key参数,若不添加则点击按钮色块不会交互StatefulColorTiles2(key: UniqueKey()),StatefulColorTiles2(key: UniqueKey())];}@overrideWidget build(BuildContext context) {return Scaffold(body: Center(child: Row(mainAxisAlignment: MainAxisAlignment.center,children: titles,)),floatingActionButton: FloatingActionButton(child: Icon(Icons.sentiment_very_satisfied),onPressed: swapTitles,),);}void swapTitles() {setState(() {titles.insert(1, titles.removeAt(0));});}
}class MyCustomApp2 extends StatelessWidget {const MyCustomApp2({super.key});@overrideWidget build(BuildContext context) {return MaterialApp(title: "FLutter Demo about key",theme: ThemeData(primarySwatch: Colors.blue),home: const PositionTiles2());}
}

widget 有状态时,才需要设置 key。如果是无状态的 widget 则可以不需要设置 key。

原理

渲染 widget 时,Flutter 在构建 widget 树的同时,还会构建其对应的Element树。Element树持有 widget 树中 widget 的信息及其子 widget 的引用。在修改和重新渲染的过程中,Flutter 查找Element树查看其是否改变,以便在元素未改变时可以复用旧元素。

注意:

1.widget 树封装配置信息,Element树相当于实例对象。widget 类似于 json串,Element树类似于 json 解析后的 bean。

2. widget 类型 和 key 值 ,在没用 key 的情况下,类型相同表示新旧 widget 可复用。

#Widget

  /// Whether the `newWidget` can be used to update an [Element] that currently/// has the `oldWidget` as its configuration.////// An element that uses a given widget as its configuration can be updated to/// use another widget as its configuration if, and only if, the two widgets/// have [runtimeType] and [key] properties that are [operator==].////// If the widgets have no key (their key is null), then they are considered a/// match if they have the same type, even if their children are completely/// different.static bool canUpdate(Widget oldWidget, Widget newWidget) {return oldWidget.runtimeType == newWidget.runtimeType&& oldWidget.key == newWidget.key;}

在上面实现方式一的例子中:原色块是 一 和 二, 交换后 oldWidget  与 newWidget 比较,因为 一 和 二 是同类型 StatelessColorTiles ,则表示原来在Element树中的 Element(一)元素在交换后是可以继续供 Element(二)元素 复用。

键类型

Key 是抽象类,有两个重要的子类 LocalKey和 GlobalKey。

Key

/// A [Key] is an identifier for [Widget]s, [Element]s and [SemanticsNode]s.
///
/// A new widget will only be used to update an existing element if its key is
/// the same as the key of the current widget associated with the element.
///
/// {@youtube 560 315 https://www.youtube.com/watch?v=kn0EOS-ZiIc}
///
/// Keys must be unique amongst the [Element]s with the same parent.
///
/// Subclasses of [Key] should either subclass [LocalKey] or [GlobalKey].
///
/// See also:
///
///  * [Widget.key], which discusses how widgets use keys.
@immutable
abstract class Key {/// Construct a [ValueKey<String>] with the given [String].////// This is the simplest way to create keys.const factory Key(String value) = ValueKey<String>;/// Default constructor, used by subclasses.////// Useful so that subclasses can call us, because the [Key.new] factory/// constructor shadows the implicit constructor.@protectedconst Key.empty();
}

Localkey 

LocalKey 是 Key 的子类,用于标识局部范围内的 Widgets。它有两个具体实现:ValueKey 和 ObjectKey。

/// A key that is not a [GlobalKey].
///
/// Keys must be unique amongst the [Element]s with the same parent. By
/// contrast, [GlobalKey]s must be unique across the entire app.
///
/// See also:
///
///  * [Widget.key], which discusses how widgets use keys.
abstract class LocalKey extends Key {/// Abstract const constructor. This constructor enables subclasses to provide/// const constructors so that they can be used in const expressions.const LocalKey() : super.empty();
}

ValueKey

ValueKey 是简单的值(如字符串、数字)来标识 Widget。它通常用于具有唯一标识的对象。

/// A key that uses a value of a particular type to identify itself.
///
/// A [ValueKey<T>] is equal to another [ValueKey<T>] if, and only if, their
/// values are [operator==].
///
/// This class can be subclassed to create value keys that will not be equal to
/// other value keys that happen to use the same value. If the subclass is
/// private, this results in a value key type that cannot collide with keys from
/// other sources, which could be useful, for example, if the keys are being
/// used as fallbacks in the same scope as keys supplied from another widget.
///
/// See also:
///
///  * [Widget.key], which discusses how widgets use keys.
class ValueKey<T> extends LocalKey {/// Creates a key that delegates its [operator==] to the given value.const ValueKey(this.value);/// The value to which this key delegates its [operator==]final T value;@overridebool operator ==(Object other) {if (other.runtimeType != runtimeType) {return false;}return other is ValueKey<T>&& other.value == value;}@overrideint get hashCode => Object.hash(runtimeType, value);@overrideString toString() {final String valueString = T == String ? "<'$value'>" : '<$value>';// The crazy on the next line is a workaround for// https://github.com/dart-lang/sdk/issues/33297if (runtimeType == _TypeLiteral<ValueKey<T>>().type) {return '[$valueString]';}return '[$T $valueString]';}
}
Container(alignment: Alignment.centerLeft,width: 100,height: 70,key: const ValueKey("TipItem")
)

ObjectKey

ObjectKey 使用对象作为标识符。这对于需要使用对象来唯一标识 Widget 的情况非常有用。

ObjectKey跟ValueKey类似,只是value的类型从T变成了Object,operator方法也有变化。
identical对比是否是相等或者说相同的时候,它其实属于对比引用或者说指针是否相等,类似于Java中对比内存地址。

/// A key that takes its identity from the object used as its value.
///
/// Used to tie the identity of a widget to the identity of an object used to
/// generate that widget.
///
/// See also:
///
///  * [Key], the base class for all keys.
///  * The discussion at [Widget.key] for more information about how widgets use
///    keys.
class ObjectKey extends LocalKey {/// Creates a key that uses [identical] on [value] for its [operator==].const ObjectKey(this.value);/// The object whose identity is used by this key's [operator==].final Object? value;@overridebool operator ==(Object other) {if (other.runtimeType != runtimeType) {return false;}return other is ObjectKey&& identical(other.value, value);}@overrideint get hashCode => Object.hash(runtimeType, identityHashCode(value));@overrideString toString() {if (runtimeType == ObjectKey) {return '[${describeIdentity(value)}]';}return '[${objectRuntimeType(this, 'ObjectKey')} ${describeIdentity(value)}]';}
}

class Student {final String name;final int age;Student(this.name, this.age);@overridebool operator ==(Object other) {return identical(this, other) ||other is Student &&runtimeType == other.runtimeType &&name == other.name &&age == other.age;}@overrideint get hashCode => name.hashCode ^ age.hashCode;
}
Container(
alignment: Alignment.centerLeft,
width: 100,
height: 70,
key:  ObjectKey(Student("Tom", 22)),
color: Colors.amber)

UniqueKey

UniqueKey:通过该对象可以生成一个具有唯一性的 hash 码。又因为每次 Widget 构建时都会重新生成一个新的 UniqueKey,失去了使用意义。(所以UniqueKey使用价值不高)。UniqueKey是一个独一无二的Key,比如UniqueKey()和UniqueKey()是不相等的。

/// A key that is only equal to itself.
///
/// This cannot be created with a const constructor because that implies that
/// all instantiated keys would be the same instance and therefore not be unique.
class UniqueKey extends LocalKey {/// Creates a key that is equal only to itself.////// The key cannot be created with a const constructor because that implies/// that all instantiated keys would be the same instance and therefore not/// be unique.// ignore: prefer_const_constructors_in_immutables , never use const for this classUniqueKey();@overrideString toString() => '[#${shortHash(this)}]';
}

PageStorageKey

PageStorageKey可以用于滑动列表,在列表页面通过某一个 Item的点击 跳转到了一个新的页面,当返回之前的列表页面时,列表的滑动的距离回到了顶部。此时给 Sliver 设置一个 PageStorageKey,就能够保持 Sliver 的滚动状态。

/// A [Key] that can be used to persist the widget state in storage after the
/// destruction and will be restored when recreated.
///
/// Each key with its value plus the ancestor chain of other [PageStorageKey]s
/// need to be unique within the widget's closest ancestor [PageStorage]. To
/// make it possible for a saved value to be found when a widget is recreated,
/// the key's value must not be objects whose identity will change each time the
/// widget is created.
///
/// See also:
///
///  * [PageStorage], which manages the data storage for widgets using
///    [PageStorageKey]s.
class PageStorageKey<T> extends ValueKey<T> {/// Creates a [ValueKey] that defines where [PageStorage] values will be saved.const PageStorageKey(super.value);
}

PageView + BottomNavigationBar 或者 TabBarView + TabBar 的时候发现当切换到另一页面的时候, 前一个页面就会被销毁, 再返回前一页时, 页面会被重建, 随之数据会重新加载, 控件会重新渲染 带来了极不好的用户体验。可以使用使用PageStorage在页面切换时保存状态。

import 'package:flutter/material.dart';class Page1 extends StatefulWidget {@overrideState<StatefulWidget> createState() {return _Page1State();}
}class Page1Params {int counter = 0;
}class _Page1State extends State<Page1> {Page1Params _params = Page1Params();@overrideWidget build(BuildContext context) {return Container(padding: EdgeInsets.all(20.0),child: Column(mainAxisAlignment: MainAxisAlignment.start,children: <Widget>[Text('${_params.counter}',style: TextStyle(fontSize: 50,)),Row(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[IconButton(onPressed: () {setState(() {_params.counter--;});},icon: Icon(Icons.remove, size: 32.0)),IconButton(onPressed: () {setState(() {_params.counter++;});},icon: Icon(Icons.add, size: 32.0)),],)],),);}
}class Page2 extends StatelessWidget {@overrideWidget build(BuildContext context) {return Container();}
}class TabInfo {String label;Widget widget;TabInfo(this.label, this.widget);
}class MyCustomApp extends StatelessWidget {final List<TabInfo> _tabs = [TabInfo("FIRST", Page1()),TabInfo("SECOND", Page2())];@overrideWidget build(BuildContext context) {return DefaultTabController(length: _tabs.length,child: Scaffold(appBar: AppBar(title: Text('Tab Controller'),bottom: PreferredSize(child: TabBar(isScrollable: true,tabs: _tabs.map((TabInfo tab) {return Tab(text: tab.label);}).toList(),),preferredSize: Size.fromHeight(50.0),),),body: TabBarView(children: _tabs.map((tab) => tab.widget).toList(),),),);}
}void main() => runApp(MaterialApp(debugShowCheckedModeBanner: false,home: MyCustomApp(),),);

在上面的例子中,通过点击FIRST  和   SECOND 按钮切换页面可以发现来回切换以后,页面状态被清理了。

在上面的例子中,当出现页面切换的情况,每次标签栏切换页面时,之前的页面就被清理了。
出现上面问题的原因是,之前页面的状态(State)没有被保留下来,状态的reset导致页面发生了初始化。

通过使用PageStorage管理改造:

import 'package:flutter/material.dart';class Page1 extends StatefulWidget {//为Page1 添加一个带参数的构造函数,//通过其将Key直接传给super,通过key可恢复状态Page1({Key? key}) : super(key: key);@overrideState<StatefulWidget> createState() {return _Page1State();}
}class Page1Params {int counter = 0;
}class _Page1State extends State<Page1> {Page1Params _params = Page1Params();@overrideWidget build(BuildContext context) {return Container(padding: EdgeInsets.all(20.0),child: Column(mainAxisAlignment: MainAxisAlignment.start,children: <Widget>[Text('${_params.counter}',style: TextStyle(fontSize: 50,)),Row(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[IconButton(onPressed: () {//setState的同时通过writeState将状态保存进PageStorage。setState(() {_params.counter--;});PageStorage.of(context).writeState(context, _params);},icon: Icon(Icons.remove, size: 32.0)),IconButton(onPressed: () {//setState的同时通过writeState将状态保存进PageStorage。setState(() {_params.counter++;});PageStorage.of(context).writeState(context, _params);},icon: Icon(Icons.add, size: 32.0)),],)],),);}//didChangeDependencies会紧跟在initState之后被调用,便于进行state初始化@overridevoid didChangeDependencies() {//重写State类的didChangeDependencies方法,//在里面通过readState从PageStorage读取并恢复被保存的状态。var p = PageStorage.of(context).readState(context);if (p != null) {_params = p;} else {_params = Page1Params();}super.didChangeDependencies();}
}class Page2 extends StatelessWidget {@overrideWidget build(BuildContext context) {return Container();}
}class TabInfo {String label;Widget widget;TabInfo(this.label, this.widget);
}class MyCustomApp extends StatelessWidget {final List<TabInfo> _tabs = [//创建widget时指定keyTabInfo("FIRST", Page1(key: PageStorageKey<String>("key_Page1"))),TabInfo("SECOND", Page2())];@overrideWidget build(BuildContext context) {return DefaultTabController(length: _tabs.length,child: Scaffold(appBar: AppBar(title: Text('Tab Controller'),bottom: PreferredSize(child: TabBar(isScrollable: true,tabs: _tabs.map((TabInfo tab) {return Tab(text: tab.label);}).toList(),),preferredSize: Size.fromHeight(50.0),),),body: TabBarView(children: _tabs.map((tab) => tab.widget).toList(),),),);}
}void main() => runApp(MaterialApp(debugShowCheckedModeBanner: false,home: MyCustomApp(),),);

 上述代码示例中页面切换时通过PageStorage保存并恢复状态。

GlobalKey

GlobalKey 适用于在应用程序的全局范围内唯一标识的 Widgets。GlobalKey不应该在每次build的时候重新重建, 它是State拥有的长期存在的对象。GlobalKey 能够跨 Widget 访问状态。

默认实现是LabeledGlobalKey,每次创建都是新的GlobalKey。GlobalKey都保存在BuildOwner类中的一个map里,map的key为GlobalKey,map的value则为GlobalKey关联的element。
 

/// A key that is unique across the entire app.
///
/// Global keys uniquely identify elements. Global keys provide access to other
/// objects that are associated with those elements, such as [BuildContext].
/// For [StatefulWidget]s, global keys also provide access to [State].
///
/// Widgets that have global keys reparent their subtrees when they are moved
/// from one location in the tree to another location in the tree. In order to
/// reparent its subtree, a widget must arrive at its new location in the tree
/// in the same animation frame in which it was removed from its old location in
/// the tree.
///
/// Reparenting an [Element] using a global key is relatively expensive, as
/// this operation will trigger a call to [State.deactivate] on the associated
/// [State] and all of its descendants; then force all widgets that depends
/// on an [InheritedWidget] to rebuild.
///
/// If you don't need any of the features listed above, consider using a [Key],
/// [ValueKey], [ObjectKey], or [UniqueKey] instead.
///
/// You cannot simultaneously include two widgets in the tree with the same
/// global key. Attempting to do so will assert at runtime.
///
/// ## Pitfalls
///
/// GlobalKeys should not be re-created on every build. They should usually be
/// long-lived objects owned by a [State] object, for example.
///
/// Creating a new GlobalKey on every build will throw away the state of the
/// subtree associated with the old key and create a new fresh subtree for the
/// new key. Besides harming performance, this can also cause unexpected
/// behavior in widgets in the subtree. For example, a [GestureDetector] in the
/// subtree will be unable to track ongoing gestures since it will be recreated
/// on each build.
///
/// Instead, a good practice is to let a State object own the GlobalKey, and
/// instantiate it outside the build method, such as in [State.initState].
///
/// See also:
///
///  * The discussion at [Widget.key] for more information about how widgets use
///    keys.
@optionalTypeArgs
abstract class GlobalKey<T extends State<StatefulWidget>> extends Key {/// Creates a [LabeledGlobalKey], which is a [GlobalKey] with a label used for/// debugging.////// The label is purely for debugging and not used for comparing the identity/// of the key.factory GlobalKey({ String? debugLabel }) => LabeledGlobalKey<T>(debugLabel);/// Creates a global key without a label.////// Used by subclasses because the factory constructor shadows the implicit/// constructor.const GlobalKey.constructor() : super.empty();Element? get _currentElement => WidgetsBinding.instance.buildOwner!._globalKeyRegistry[this];/// The build context in which the widget with this key builds.////// The current context is null if there is no widget in the tree that matches/// this global key.BuildContext? get currentContext => _currentElement;/// The widget in the tree that currently has this global key.////// The current widget is null if there is no widget in the tree that matches/// this global key.Widget? get currentWidget => _currentElement?.widget;/// The [State] for the widget in the tree that currently has this global key.////// The current state is null if (1) there is no widget in the tree that/// matches this global key, (2) that widget is not a [StatefulWidget], or the/// associated [State] object is not a subtype of `T`.T? get currentState {final Element? element = _currentElement;if (element is StatefulElement) {final StatefulElement statefulElement = element;final State state = statefulElement.state;if (state is T) {return state;}}return null;}
}

一个泛型类型,T必须要继承自State<StatefulWidget>,可以说这个GlobalKey专门用于StatefulWidget组件。GlobalKey包含一个Map,key和value分别为自身和Element。GlobalKey会在组件Mount阶段把自身放到一个Map里面缓存起来。

GlobalKey的注意点:

  • 当拥有GlobalKey的widget从树的一个位置上移动到另一个位置时,需要reparent它的子树。为了reparent它的子树,必须在一个动画帧里完成从旧位置移动到新位置的操作。
  • 上面的reparent操作代价昂贵,因为要调用所有相关联的State和所有子节点的deactive方法,并且所有依赖InheritedWidget的widget去重建。
  • 不要在build方法中创建GlobalKey,性能不好,容易出现异常。比如子树里的GestureDetector可能会由于每次build时重新创建GlobalKey而无法继续追踪手势事件。
  • GlobalKey提供了访问其关联的Element和State的方法。

GlobalKey使用场景:

获取或者修改Widget的状态(State)

实现局部刷新

查找Widget

维持Widget的持久性

参考:

Keys in Flutter. Working with Flutter, many times we… | by Karmacharyasamriddhi | codingmountain | Medium

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/51889.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构之 “单链表“

&#xff08;1&#xff09;在顺表表中&#xff0c;如果是头插/删的时间复杂度是O(1)&#xff1b;尾插/删的时间复杂度是O(N) &#xff08;2&#xff09;增容一般是呈2倍的增长&#xff0c;势必会有一定的空间浪费。比如&#xff1a;申请了50个空间&#xff0c;只用了两个&#…

Type-C接口诱骗取电快充方案

Type-C XSP08Q 快充协议芯片是一种新型电源管理芯片&#xff0c;主要负责控制充电电流和电压等相关参数&#xff0c;从而实现快速充电功能。Type-C XSP08Q快充协议是在Type-C接口基础上&#xff0c;加入了XSP08Q协议芯片的支持&#xff0c;很大程度上提升了充电速度。 正常情况…

Linux——性能调优工具一览

一、CPU 1.调优工具 根据指标找工具 性能指标工具说明 平均负载 uptime、top uptime最简单、top提供了更全的指标 系统整体CPU使用率 vmstat、mpstat、top、sar、/proc/stat top、vmstat、mpstat只可以动态查看&#xff0c;而sar还可以记录历史数据 /proc/stat是其他性…

UE引擎内置插件信息 储存的位置

.uproject。图标文件可以让UE 引擎内置插件&#xff0c;配置更改,比如我希望我的DataSmithImporter插件是启用的。

STM32 ADC采样详解

Content 0x00 前言0x01 ADC配置0x02 滤波处理 0x00 前言 在单片机开发过程中&#xff0c;常常涉及到ADC的使用&#xff0c;市面上大部分便宜的传感器都是采用的ADC来获取其数据&#xff0c;如MQ-2 烟雾传感器、光敏传感器等等。 此类传感器工作原理为根据所采集到的数据变化…

大模型入门 ch01:大模型概述

本文是github上的大模型教程LLMs-from-scratch的学习笔记&#xff0c;教程地址&#xff1a;教程链接 STAGE 1&#xff1a; BUILDING 1. 数据准备与采样 LLM的预测过程&#xff0c;是一个不断预测下一个词&#xff08;准确的说是token&#xff09;的过程&#xff0c;每次根据输…

【C++八股题整理】内存布局、堆和栈、内存泄露、函数调用栈

C八股题整理 内存布局C中的内存分配情况堆和栈的内存有什么区别&#xff1f; 堆堆内存分配慢如何优化&#xff1f;内存池内存溢出和内存泄漏是什么&#xff1f;如何避免&#xff1f;内存碎片是什么&#xff1f;怎么解决&#xff1f; 栈为什么栈的访问效率比堆高&#xff1f;函数…

UI自动化测试 —— web端元素获取元素等待实践!

前言 Web UI自动化测试是一种软件测试方法&#xff0c;通过模拟用户行为&#xff0c;自动执行Web界面的各种操作&#xff0c;并验证操作结果是否符合预期&#xff0c;从而提高测试效率和准确性。 目的&#xff1a; 确保Web应用程序的界面在不同环境(如不同浏览器、操作系统)下…

【前缀和算法】--- 进阶题目赏析

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; 算法Journey 本篇我们来赏析前缀和算法的进阶题目。 &#x1f3e0; 和可被K整除的子数组 &#x1f4cc; 题目解析 和可被k整除的子数组 &#x1f4cc; …

记一次ssh伪终端修改为shell

问题 用户ssh进行连接后&#xff0c;默认为伪终端。 解决办法&#xff0c;可以先拿到终端shell&#xff0c;查看用户是否为/bin/bash&#xff1a; 不是/bin/bash&#xff0c;使用如下命令进行修改&#xff1a; chsh -s /bin/bash rootservice sshd restart

量化投资策略与技术学习PART1.1:量化选股之再谈多因子模型(二)

在上一个多因子模型中&#xff0c;我手动对各个因子进行了回测&#xff0c;但是数据结果并不是十分理想&#xff0c;难道基本面指标真的和股票走势关系不大么&#xff1f; 这里我还是准备再测试一下&#xff0c;策略如下&#xff1a; &#xff08;1&#xff09;首先我获取了一下…

codeforces Round 970 (Div. 3)(A-F)

文章目录 [Codeforces Round 970 (Div. 3)](https://codeforces.com/contest/2008)A-[Sakurakos Exam](https://codeforces.com/contest/2008/problem/A)B-[Square or Not](https://codeforces.com/contest/2008/problem/B)C-[Longest Good Array](https://codeforces.com/cont…

Ubuntu上安装配置(jdk/tomcat/ufw防火墙/mysql)+mysql卸载

jdk安装 1.上传jdk压缩包 详情&#xff1a; 下载rz服务&#xff08;lrzsz&#xff09;&#xff1a;sudo apt install lrzsz(在主用户root就不用sudo)下载压缩包&#xff1a;rz 2.解压jdk压缩包 &#xff1a; 详情&#xff1a; 在压缩包所在位置&#xff08;解压压缩使用看Li…

测试人如何高效地设计自动化测试框架?

关于测试框架的好处&#xff0c;比如快速回归提高测试效率&#xff0c;提高测试覆盖率等这里就不讨论了。这里主要讨论自动化框架包含哪些内容&#xff0c;以及如何去设计一个测试框架。 什么是自动化测试框架&#xff1f; 它是由一个或多个自动化测试基础模块、自动化测试管…

Qt22双缓冲机制

Qt22双缓冲机制 知识点drawwidgetdrawwidget.hdrawwidget.cpp mainwindowmainwindow.hmainwindow.cpp main.cpp运行图 知识点 双缓冲就是在内存区申请一块缓存&#xff1b;然后显卡直接从这块内存读取数据.。 这样就不用鼠标边画&#xff0c;经过IO来读取这个环节&#xff1b;…

EasyExcel实现复杂Excel的导入

最近项目中遇到一个复杂的Excel的导入&#xff0c;并且数据量较大。因为数据不规则&#xff0c;所以只能使用POI进行自定义读取&#xff0c;但是发现数据量大之后&#xff0c;读取数据非常耗时。后面换成EasyExcel&#xff0c;性能起飞。 1. Excel样板 如上图&#xff0c;需要…

【C++】汇编分析,函数是如何调用,传参,返回

传参 有的是用寄存器传参&#xff0c;有的用push传参 我在MSVC编译测出来的是PUSH传参&#xff08;debug模式&#xff09;&#xff0c;具体过程如下 long func(long a, long b, long c, long d,long e, long f, long g, long h) {long sum;sum (a b c d e f g h);ret…

VMware安装windows虚拟机详细过程

目录 准备工作配置虚拟机为虚拟机设置操作系统镜像安装windows10 准备工作 安装好VMware软件并激活&#xff0c;激活码自行查找 准备好系统镜像文件&#xff0c;可以在MSDN中下载&#xff0c;地址&#xff1a;https://next.itellyou.cn/ 配置虚拟机 选择自定义 默认 选择稍后…

骨灵冷火!Solon Cloud Gateway 照面发布

骨灵冷火&#xff0c;是练药的好火哟。极冷&#xff0c;又极热。在冰冻中被烧死&#xff1a;&#xff09; 1、认识 Solon Cloud Gateway Solon Cloud Gateway 是基于 Solon Cloud、Vert.X 和 Solon Rx(reactive-streams) 接口实现。小特点&#xff1a; 纯响应式的接口体验流…

[Linux]:基本指令(上)

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;Linux学习 贝蒂的主页&#xff1a;Betty’s blog 与Windows环境不同&#xff0c;我们在linux环境下需要通过指令进行各操作&…