You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by sh...@apache.org on 2021/07/09 15:19:28 UTC

[echarts] branch dataset-perf updated: refact(data): separate List to DataStorage and SeriesData

This is an automated email from the ASF dual-hosted git repository.

shenyi pushed a commit to branch dataset-perf
in repository https://gitbox.apache.org/repos/asf/echarts.git


The following commit(s) were added to refs/heads/dataset-perf by this push:
     new 85ee886  refact(data): separate List to DataStorage and SeriesData
85ee886 is described below

commit 85ee886e86e599bc5b5f8d374d3b14cd2dbe9c51
Author: pissang <bm...@gmail.com>
AuthorDate: Fri Jul 9 23:18:18 2021 +0800

    refact(data): separate List to DataStorage and SeriesData
---
 src/animation/universalTransition.ts               |   18 +-
 src/chart/bar/BarSeries.ts                         |    8 +-
 src/chart/bar/BarView.ts                           |   24 +-
 src/chart/bar/BaseBarSeries.ts                     |    6 +-
 src/chart/bar/PictorialBarView.ts                  |   16 +-
 src/chart/boxplot/BoxplotView.ts                   |    8 +-
 src/chart/candlestick/CandlestickSeries.ts         |    4 +-
 src/chart/candlestick/CandlestickView.ts           |    8 +-
 src/chart/candlestick/candlestickLayout.ts         |   10 +-
 src/chart/custom/CustomSeries.ts                   |    6 +-
 src/chart/custom/CustomView.ts                     |   10 +-
 src/chart/effectScatter/EffectScatterSeries.ts     |    8 +-
 src/chart/funnel/FunnelSeries.ts                   |    8 +-
 src/chart/funnel/FunnelView.ts                     |   10 +-
 src/chart/funnel/funnelLayout.ts                   |    6 +-
 src/chart/gauge/GaugeSeries.ts                     |    8 +-
 src/chart/gauge/GaugeView.ts                       |    4 +-
 src/chart/graph/GraphSeries.ts                     |   14 +-
 src/chart/graph/GraphView.ts                       |    4 +-
 src/chart/graph/circularLayoutHelper.ts            |    4 +-
 src/chart/heatmap/HeatmapSeries.ts                 |    6 +-
 src/chart/helper/EffectLine.ts                     |   14 +-
 src/chart/helper/EffectPolyline.ts                 |    4 +-
 src/chart/helper/EffectSymbol.ts                   |    6 +-
 src/chart/helper/LargeLineDraw.ts                  |    4 +-
 src/chart/helper/LargeSymbolDraw.ts                |   12 +-
 src/chart/helper/Line.ts                           |   12 +-
 src/chart/helper/LineDraw.ts                       |   10 +-
 src/chart/helper/Polyline.ts                       |   12 +-
 src/chart/helper/Symbol.ts                         |   12 +-
 src/chart/helper/SymbolDraw.ts                     |   12 +-
 src/chart/helper/createGraphFromNodeEdge.ts        |   14 +-
 ...stFromArray.ts => createSeriesDataFromArray.ts} |    9 +-
 ...eateListSimply.ts => createSeriesDataSimply.ts} |    8 +-
 src/chart/helper/labelHelper.ts                    |    6 +-
 src/chart/helper/whiskerBoxCommon.ts               |    8 +-
 src/chart/line/LineSeries.ts                       |    6 +-
 src/chart/line/LineView.ts                         |   20 +-
 src/chart/line/helper.ts                           |    6 +-
 src/chart/line/lineAnimationDiff.ts                |    6 +-
 src/chart/lines/LinesSeries.ts                     |    4 +-
 src/chart/lines/LinesView.ts                       |    6 +-
 src/chart/lines/linesVisual.ts                     |    6 +-
 src/chart/map/MapSeries.ts                         |   15 +-
 src/chart/map/mapDataStatistic.ts                  |    4 +-
 src/chart/parallel/ParallelSeries.ts               |    6 +-
 src/chart/parallel/ParallelView.ts                 |   10 +-
 src/chart/pie/PieSeries.ts                         |    8 +-
 src/chart/pie/PieView.ts                           |   10 +-
 src/chart/radar/RadarSeries.ts                     |    8 +-
 src/chart/radar/RadarView.ts                       |    8 +-
 src/chart/sankey/SankeySeries.ts                   |    6 +-
 src/chart/sankey/SankeyView.ts                     |    4 +-
 src/chart/scatter/ScatterSeries.ts                 |    8 +-
 src/chart/scatter/ScatterView.ts                   |    4 +-
 src/chart/sunburst/SunburstSeries.ts               |    4 +-
 src/chart/themeRiver/ThemeRiverSeries.ts           |    6 +-
 src/chart/themeRiver/themeRiverLayout.ts           |    4 +-
 src/chart/tree/TreeSeries.ts                       |    6 +-
 src/chart/tree/TreeView.ts                         |   10 +-
 src/chart/treemap/TreemapSeries.ts                 |    4 +-
 src/component/helper/MapDraw.ts                    |    4 +-
 src/component/marker/MarkAreaView.ts               |   12 +-
 src/component/marker/MarkLineView.ts               |   18 +-
 src/component/marker/MarkPointView.ts              |    6 +-
 src/component/marker/MarkerModel.ts                |   10 +-
 src/component/marker/markerHelper.ts               |    8 +-
 src/component/timeline/SliderTimelineModel.ts      |    4 +-
 src/component/timeline/TimelineModel.ts            |    6 +-
 src/component/visualMap/VisualMapModel.ts          |    4 +-
 src/coord/axisHelper.ts                            |    6 +-
 src/coord/cartesian/Grid.ts                        |    4 +-
 src/coord/parallel/Parallel.ts                     |    4 +-
 src/core/Scheduler.ts                              |    4 +-
 src/core/task.ts                                   |    6 +-
 src/data/DataDimensionInfo.ts                      |    5 +-
 src/data/DataStorage.ts                            | 1141 ++++++++++
 src/data/Graph.ts                                  |    6 +-
 src/data/List.ts                                   | 2278 --------------------
 src/data/SeriesData.ts                             | 1299 +++++++++++
 src/data/Tree.ts                                   |   12 +-
 src/data/helper/completeDimensions.ts              |    4 +-
 src/data/helper/createDimensions.ts                |    4 +-
 src/data/helper/dataProvider.ts                    |   10 +-
 src/data/helper/dataStackHelper.ts                 |    8 +-
 src/data/helper/dimensionHelper.ts                 |    7 +-
 src/data/helper/{linkList.ts => linkSeriesData.ts} |   50 +-
 src/export/api.ts                                  |    2 +-
 src/export/api/helper.ts                           |    4 +-
 src/label/labelStyle.ts                            |    4 +-
 src/layout/barGrid.ts                              |    2 +-
 src/layout/points.ts                               |    7 +-
 src/model/Series.ts                                |   35 +-
 src/processor/dataStack.ts                         |    4 +-
 src/scale/Log.ts                                   |    4 +-
 src/scale/Ordinal.ts                               |    4 +-
 src/scale/Scale.ts                                 |    4 +-
 src/util/model.ts                                  |    6 +-
 src/util/states.ts                                 |    4 +-
 src/util/types.ts                                  |   17 +-
 src/view/Chart.ts                                  |    4 +-
 src/visual/LegendVisualProvider.ts                 |   10 +-
 src/visual/commonVisualTypes.ts                    |    2 +-
 src/visual/helper.ts                               |    8 +-
 src/visual/symbol.ts                               |    6 +-
 src/visual/visualSolution.ts                       |    4 +-
 .../spec/data/{List.test.ts => SeriesData.test.ts} |   52 +-
 107 files changed, 2891 insertions(+), 2724 deletions(-)

diff --git a/src/animation/universalTransition.ts b/src/animation/universalTransition.ts
index 140afca..9dc27e2 100644
--- a/src/animation/universalTransition.ts
+++ b/src/animation/universalTransition.ts
@@ -27,7 +27,7 @@ import Path from 'zrender/src/graphic/Path';
 import { EChartsExtensionInstallRegisters } from '../extension';
 import { initProps } from '../util/graphic';
 import DataDiffer from '../data/DataDiffer';
-import List from '../data/List';
+import SeriesData from '../data/SeriesData';
 import { Dictionary, DimensionLoose, OptionDataItemObject, UniversalTransitionOption } from '../util/types';
 import {
     UpdateLifecycleParams,
@@ -43,22 +43,22 @@ import Displayable from 'zrender/src/graphic/Displayable';
 
 const DATA_COUNT_THRESHOLD = 1e4;
 
-interface GlobalStore { oldSeries: SeriesModel[], oldData: List[] };
+interface GlobalStore { oldSeries: SeriesModel[], oldData: SeriesData[] };
 const getUniversalTransitionGlobalStore = makeInner<GlobalStore, ExtensionAPI>();
 
 interface DiffItem {
-    data: List
+    data: SeriesData
     dim: DimensionLoose
     divide: UniversalTransitionOption['divideShape']
     dataIndex: number
 }
 interface TransitionSeries {
-    data: List
+    data: SeriesData
     divide: UniversalTransitionOption['divideShape']
     dim?: DimensionLoose
 }
 
-function getGroupIdDimension(data: List) {
+function getGroupIdDimension(data: SeriesData) {
     const dimensions = data.dimensions;
     for (let i = 0; i < dimensions.length; i++) {
         const dimInfo = data.getDimensionInfo(dimensions[i]);
@@ -452,7 +452,7 @@ interface SeriesTransitionBatch {
     newSeries: TransitionSeries[]
 }
 
-function getDivideShapeFromData(data: List) {
+function getDivideShapeFromData(data: SeriesData) {
     if (data.hostModel) {
         return ((data.hostModel as SeriesModel)
             .getModel('universalTransition') as Model<UniversalTransitionOption>)
@@ -466,12 +466,12 @@ function findTransitionSeriesBatches(
 ) {
     const updateBatches = createHashMap<SeriesTransitionBatch>();
 
-    const oldDataMap = createHashMap<List>();
+    const oldDataMap = createHashMap<SeriesData>();
     // Map that only store key in array seriesKey.
     // Which is used to query the old data when transition from one to multiple series.
     const oldDataMapForSplit = createHashMap<{
         key: string,
-        data: List
+        data: SeriesData
     }>();
 
     each(globalStore.oldSeries, (series, idx) => {
@@ -667,7 +667,7 @@ export function installUniversalTransition(registers: EChartsExtensionInstallReg
         // Save all series of current update. Not only the updated one.
         const allSeries = ecModel.getSeries();
         const savedSeries: SeriesModel[] = globalStore.oldSeries = [];
-        const savedData: List[] = globalStore.oldData = [];
+        const savedData: SeriesData[] = globalStore.oldData = [];
         for (let i = 0; i < allSeries.length; i++) {
             const data = allSeries[i].getData();
             // Only save the data that can have transition.
diff --git a/src/chart/bar/BarSeries.ts b/src/chart/bar/BarSeries.ts
index 2008e91..06ede94 100644
--- a/src/chart/bar/BarSeries.ts
+++ b/src/chart/bar/BarSeries.ts
@@ -29,10 +29,10 @@ import {
     SeriesEncodeOptionMixin
 } from '../../util/types';
 import type Cartesian2D from '../../coord/cartesian/Cartesian2D';
-import createListFromArray from '../helper/createListFromArray';
+import createListFromArray from '../helper/createSeriesDataFromArray';
 import type Polar from '../../coord/polar/Polar';
 import { inheritDefaultOption } from '../../util/component';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import { BrushCommonSelectorsForSeries } from '../../component/brush/selector';
 
 export type PolarBarLabelPosition = SeriesLabelOption['position']
@@ -89,7 +89,7 @@ class BarSeriesModel extends BaseBarSeriesModel<BarSeriesOption> {
 
     coordinateSystem: Cartesian2D | Polar;
 
-    getInitialData(): List {
+    getInitialData(): SeriesData {
         return createListFromArray(this.getSource(), this, {
             useEncodeDefaulter: true,
             createInvertedIndices: !!this.get('realtimeSort', true) || null
@@ -119,7 +119,7 @@ class BarSeriesModel extends BaseBarSeriesModel<BarSeriesOption> {
         return progressiveThreshold;
     }
 
-    brushSelector(dataIndex: number, data: List, selectors: BrushCommonSelectorsForSeries): boolean {
+    brushSelector(dataIndex: number, data: SeriesData, selectors: BrushCommonSelectorsForSeries): boolean {
         return selectors.rect(data.getItemLayout(dataIndex));
     }
 
diff --git a/src/chart/bar/BarView.ts b/src/chart/bar/BarView.ts
index 521da0e..e6f71db 100644
--- a/src/chart/bar/BarView.ts
+++ b/src/chart/bar/BarView.ts
@@ -35,7 +35,7 @@ import {throttle} from '../../util/throttle';
 import {createClipPath} from '../helper/createClipPathFromCoordSys';
 import Sausage from '../../util/shape/sausage';
 import ChartView from '../../view/Chart';
-import List, {DefaultDataVisual} from '../../data/List';
+import SeriesData, {DefaultDataVisual} from '../../data/SeriesData';
 import GlobalModel from '../../model/Global';
 import ExtensionAPI from '../../core/ExtensionAPI';
 import {
@@ -88,7 +88,7 @@ type RealtimeSortConfig = {
 // Return a number, based on which the ordinal sorted.
 type OrderMapping = (dataIndex: number) => number;
 
-function getClipArea(coord: CoordSysOfBar, data: List) {
+function getClipArea(coord: CoordSysOfBar, data: SeriesData) {
     const coordSysClipArea = coord.getArea && coord.getArea();
     if (isCoordinateSystemType<Cartesian2D>(coord, 'cartesian2d')) {
         const baseAxis = coord.getBaseAxis();
@@ -115,7 +115,7 @@ class BarView extends ChartView {
     static type = 'bar' as const;
     type = BarView.type;
 
-    private _data: List;
+    private _data: SeriesData;
 
     private _isLargeDraw: boolean;
 
@@ -467,7 +467,7 @@ class BarView extends ChartView {
     }
 
     private _dataSort(
-        data: List<BarSeriesModel, DefaultDataVisual>,
+        data: SeriesData<BarSeriesModel, DefaultDataVisual>,
         baseAxis: Axis2D,
         orderMapping: OrderMapping
     ): OrdinalSortInfo {
@@ -498,7 +498,7 @@ class BarView extends ChartView {
     }
 
     private _isOrderChangedWithinSameData(
-        data: List<BarSeriesModel, DefaultDataVisual>,
+        data: SeriesData<BarSeriesModel, DefaultDataVisual>,
         orderMapping: OrderMapping,
         baseAxis: Axis2D
     ): boolean {
@@ -543,7 +543,7 @@ class BarView extends ChartView {
     }
 
     private _updateSortWithinSameData(
-        data: List<BarSeriesModel, DefaultDataVisual>,
+        data: SeriesData<BarSeriesModel, DefaultDataVisual>,
         orderMapping: OrderMapping,
         baseAxis: Axis2D,
         api: ExtensionAPI
@@ -566,7 +566,7 @@ class BarView extends ChartView {
     }
 
     private _dispatchInitSort(
-        data: List<BarSeriesModel, DefaultDataVisual>,
+        data: SeriesData<BarSeriesModel, DefaultDataVisual>,
         realtimeSortCfg: RealtimeSortConfig,
         api: ExtensionAPI
     ) {
@@ -709,7 +709,7 @@ const clip: {
 
 interface ElementCreator {
     (
-        seriesModel: BarSeriesModel, data: List, newIndex: number,
+        seriesModel: BarSeriesModel, data: SeriesData, newIndex: number,
         layout: RectLayout | SectorLayout, isHorizontalOrRadial: boolean,
         animationModel: BarSeriesModel,
         axisModel: CartesianAxisModel | AngleAxisModel | RadiusAxisModel,
@@ -875,7 +875,7 @@ const isValidLayout: Record<'cartesian2d' | 'polar', (layout: RectLayout | Secto
 } as const;
 
 interface GetLayout {
-    (data: List, dataIndex: number, itemModel?: Model<BarDataItemOption>): RectLayout | SectorLayout
+    (data: SeriesData, dataIndex: number, itemModel?: Model<BarDataItemOption>): RectLayout | SectorLayout
 }
 const getLayout: {
     [key in 'cartesian2d' | 'polar']: GetLayout
@@ -936,7 +936,7 @@ function createPolarPositionMapping(isRadial: boolean)
 
 function updateStyle(
     el: BarPossiblePath,
-    data: List, dataIndex: number,
+    data: SeriesData, dataIndex: number,
     itemModel: Model<BarDataItemOption>,
     layout: RectLayout | SectorLayout,
     seriesModel: BarSeriesModel,
@@ -1169,7 +1169,7 @@ function largePathFindDataIndex(largePath: LargePath, x: number, y: number) {
 function setLargeStyle(
     el: LargePath,
     seriesModel: BarSeriesModel,
-    data: List
+    data: SeriesData
 ) {
     const globalStyle = data.getVisual('style');
 
@@ -1183,7 +1183,7 @@ function setLargeStyle(
 function setLargeBackgroundStyle(
     el: LargePath,
     backgroundModel: Model<BarSeriesOption['backgroundStyle']>,
-    data: List
+    data: SeriesData
 ) {
     const borderColor = backgroundModel.get('borderColor') || backgroundModel.get('color');
     const itemStyle = backgroundModel.getItemStyle();
diff --git a/src/chart/bar/BaseBarSeries.ts b/src/chart/bar/BaseBarSeries.ts
index 36ea14a..46eeeb4 100644
--- a/src/chart/bar/BaseBarSeries.ts
+++ b/src/chart/bar/BaseBarSeries.ts
@@ -18,7 +18,7 @@
 */
 
 import SeriesModel from '../../model/Series';
-import createListFromArray from '../helper/createListFromArray';
+import createListFromArray from '../helper/createSeriesDataFromArray';
 import {
     SeriesOption,
     SeriesOnCartesianOptionMixin,
@@ -28,7 +28,7 @@ import {
 } from '../../util/types';
 import GlobalModel from '../../model/Global';
 import Cartesian2D from '../../coord/cartesian/Cartesian2D';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 
 
 export interface BaseBarSeriesOption<StateOption, ExtraStateOption = DefaultExtraStateOpts>
@@ -78,7 +78,7 @@ class BaseBarSeriesModel<Opts extends BaseBarSeriesOption<unknown> = BaseBarSeri
     static type = 'series.__base_bar__';
     type = BaseBarSeriesModel.type;
 
-    getInitialData(option: Opts, ecModel: GlobalModel): List {
+    getInitialData(option: Opts, ecModel: GlobalModel): SeriesData {
         return createListFromArray(this.getSource(), this, {useEncodeDefaulter: true});
     }
 
diff --git a/src/chart/bar/PictorialBarView.ts b/src/chart/bar/PictorialBarView.ts
index 565b44e..1a81fed 100644
--- a/src/chart/bar/PictorialBarView.ts
+++ b/src/chart/bar/PictorialBarView.ts
@@ -27,7 +27,7 @@ import {parsePercent, isNumeric} from '../../util/number';
 import ChartView from '../../view/Chart';
 import PictorialBarSeriesModel, {PictorialBarDataItemOption} from './PictorialBarSeries';
 import ExtensionAPI from '../../core/ExtensionAPI';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import GlobalModel from '../../model/Global';
 import Model from '../../model/Model';
 import { ColorString, AnimationOptionMixin, ECElement } from '../../util/types';
@@ -131,7 +131,7 @@ class PictorialBarView extends ChartView {
     static type = 'pictorialBar';
     readonly type = PictorialBarView.type;
 
-    private _data: List;
+    private _data: SeriesData;
 
     render(
         seriesModel: PictorialBarSeriesModel,
@@ -239,7 +239,7 @@ class PictorialBarView extends ChartView {
 
 // Set or calculate default value about symbol, and calculate layout info.
 function getSymbolMeta(
-    data: List,
+    data: SeriesData,
     dataIndex: number,
     itemModel: ItemModel,
     opt: CreateOpts
@@ -338,7 +338,7 @@ function convertToCoordOnAxis(axis: Axis2D, value: number) {
 
 // Support ['100%', '100%']
 function prepareSymbolSize(
-    data: List,
+    data: SeriesData,
     dataIndex: number,
     layout: RectLayout,
     symbolRepeat: PictorialBarDataItemOption['symbolRepeat'],
@@ -726,7 +726,7 @@ function createOrUpdateClip(
     }
 }
 
-function getItemModel(data: List, dataIndex: number) {
+function getItemModel(data: SeriesData, dataIndex: number) {
     const itemModel = data.getItemModel(dataIndex) as ItemModel;
     itemModel.getAnimationDelayParams = getAnimationDelayParams;
     itemModel.isAnimationEnabled = isAnimationEnabled;
@@ -746,7 +746,7 @@ function isAnimationEnabled(this: ItemModel) {
     return this.parentModel.isAnimationEnabled() && !!this.getShallow('animation');
 }
 
-function createBar(data: List, opt: CreateOpts, symbolMeta: SymbolMeta, isUpdate?: boolean) {
+function createBar(data: SeriesData, opt: CreateOpts, symbolMeta: SymbolMeta, isUpdate?: boolean) {
     // bar is the main element for each data.
     const bar = new graphic.Group() as PictorialBarElement;
     // bundle is used for location and clip.
@@ -798,7 +798,7 @@ function updateBar(bar: PictorialBarElement, opt: CreateOpts, symbolMeta: Symbol
 }
 
 function removeBar(
-    data: List, dataIndex: number, animationModel: Model<AnimationOptionMixin>, bar: PictorialBarElement
+    data: SeriesData, dataIndex: number, animationModel: Model<AnimationOptionMixin>, bar: PictorialBarElement
 ) {
     // Not show text when animating
     const labelRect = bar.__pictorialBarRect;
@@ -825,7 +825,7 @@ function removeBar(
     data.setItemGraphicEl(dataIndex, null);
 }
 
-function getShapeStr(data: List, symbolMeta: SymbolMeta) {
+function getShapeStr(data: SeriesData, symbolMeta: SymbolMeta) {
     return [
         data.getItemVisual(symbolMeta.dataIndex, 'symbol') || 'none',
         !!symbolMeta.symbolRepeat,
diff --git a/src/chart/boxplot/BoxplotView.ts b/src/chart/boxplot/BoxplotView.ts
index 68ac23a..748ff6e 100644
--- a/src/chart/boxplot/BoxplotView.ts
+++ b/src/chart/boxplot/BoxplotView.ts
@@ -25,7 +25,7 @@ import Path, { PathProps } from 'zrender/src/graphic/Path';
 import BoxplotSeriesModel, { BoxplotDataItemOption } from './BoxplotSeries';
 import GlobalModel from '../../model/Global';
 import ExtensionAPI from '../../core/ExtensionAPI';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import { BoxplotItemLayout } from './boxplotLayout';
 import { saveOldStyle } from '../../animation/basicTrasition';
 
@@ -33,7 +33,7 @@ class BoxplotView extends ChartView {
     static type = 'boxplot';
     type = BoxplotView.type;
 
-    private _data: List;
+    private _data: SeriesData;
 
     render(seriesModel: BoxplotSeriesModel, ecModel: GlobalModel, api: ExtensionAPI) {
         const data = seriesModel.getData();
@@ -141,7 +141,7 @@ class BoxPath extends Path<BoxPathProps> {
 
 function createNormalBox(
     itemLayout: BoxplotItemLayout,
-    data: List,
+    data: SeriesData,
     dataIndex: number,
     constDim: number,
     isInit?: boolean
@@ -164,7 +164,7 @@ function createNormalBox(
 function updateNormalBoxData(
     itemLayout: BoxplotItemLayout,
     el: BoxPath,
-    data: List,
+    data: SeriesData,
     dataIndex: number,
     isInit?: boolean
 ) {
diff --git a/src/chart/candlestick/CandlestickSeries.ts b/src/chart/candlestick/CandlestickSeries.ts
index 68e8b8a..d71cecd 100644
--- a/src/chart/candlestick/CandlestickSeries.ts
+++ b/src/chart/candlestick/CandlestickSeries.ts
@@ -33,7 +33,7 @@ import {
     SeriesEncodeOptionMixin,
     DefaultEmphasisFocus
 } from '../../util/types';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import Cartesian2D from '../../coord/cartesian/Cartesian2D';
 import { BrushCommonSelectorsForSeries } from '../../component/brush/selector';
 import { mixin } from 'zrender/src/core/util';
@@ -151,7 +151,7 @@ class CandlestickSeriesModel extends SeriesModel<CandlestickSeriesOption> {
         return 'open';
     }
 
-    brushSelector(dataIndex: number, data: List, selectors: BrushCommonSelectorsForSeries): boolean {
+    brushSelector(dataIndex: number, data: SeriesData, selectors: BrushCommonSelectorsForSeries): boolean {
         const itemLayout = data.getItemLayout(dataIndex);
         return itemLayout && selectors.rect(itemLayout.brushRect);
     }
diff --git a/src/chart/candlestick/CandlestickView.ts b/src/chart/candlestick/CandlestickView.ts
index 3a8e971..7e62360 100644
--- a/src/chart/candlestick/CandlestickView.ts
+++ b/src/chart/candlestick/CandlestickView.ts
@@ -27,7 +27,7 @@ import CandlestickSeriesModel, { CandlestickDataItemOption } from './Candlestick
 import GlobalModel from '../../model/Global';
 import ExtensionAPI from '../../core/ExtensionAPI';
 import { StageHandlerProgressParams } from '../../util/types';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import {CandlestickItemLayout} from './candlestickLayout';
 import { CoordinateSystemClipArea } from '../../coord/CoordinateSystem';
 import Model from '../../model/Model';
@@ -42,7 +42,7 @@ class CandlestickView extends ChartView {
 
     private _isLargeDraw: boolean;
 
-    private _data: List;
+    private _data: SeriesData;
 
     render(seriesModel: CandlestickSeriesModel, ecModel: GlobalModel, api: ExtensionAPI) {
         // If there is clipPath created in large mode. Remove it.
@@ -273,7 +273,7 @@ function isNormalBoxClipped(clipArea: CoordinateSystemClipArea, itemLayout: Cand
     return clipped;
 }
 
-function setBoxCommon(el: NormalBoxPath, data: List, dataIndex: number, isSimpleBox?: boolean) {
+function setBoxCommon(el: NormalBoxPath, data: SeriesData, dataIndex: number, isSimpleBox?: boolean) {
     const itemModel = data.getItemModel(dataIndex) as Model<CandlestickDataItemOption>;
 
     el.useStyle(data.getItemVisual(dataIndex, 'style'));
@@ -359,7 +359,7 @@ function createLarge(seriesModel: CandlestickSeriesModel, group: graphic.Group,
     }
 }
 
-function setLargeStyle(sign: number, el: LargeBoxPath, seriesModel: CandlestickSeriesModel, data: List) {
+function setLargeStyle(sign: number, el: LargeBoxPath, seriesModel: CandlestickSeriesModel, data: SeriesData) {
     // TODO put in visual?
     const borderColor = seriesModel.get(['itemStyle', sign > 0 ? 'borderColor' : 'borderColor0'])
         || seriesModel.get(['itemStyle', sign > 0 ? 'color' : 'color0']);
diff --git a/src/chart/candlestick/candlestickLayout.ts b/src/chart/candlestick/candlestickLayout.ts
index 615c684..ae9db88 100644
--- a/src/chart/candlestick/candlestickLayout.ts
+++ b/src/chart/candlestick/candlestickLayout.ts
@@ -25,7 +25,7 @@ import {parsePercent} from '../../util/number';
 import {retrieve2} from 'zrender/src/core/util';
 import { StageHandler, StageHandlerProgressParams } from '../../util/types';
 import CandlestickSeriesModel from './CandlestickSeries';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import { RectLike } from 'zrender/src/core/BoundingRect';
 
 const LargeArr = typeof Float32Array !== 'undefined' ? Float32Array : Array;
@@ -78,7 +78,7 @@ const candlestickLayout: StageHandler = {
                 ? largeProgress : normalProgress
         };
 
-        function normalProgress(params: StageHandlerProgressParams, data: List) {
+        function normalProgress(params: StageHandlerProgressParams, data: SeriesData) {
             let dataIndex;
             while ((dataIndex = params.next()) != null) {
 
@@ -162,7 +162,7 @@ const candlestickLayout: StageHandler = {
             }
         }
 
-        function largeProgress(params: StageHandlerProgressParams, data: List) {
+        function largeProgress(params: StageHandlerProgressParams, data: SeriesData) {
             // Structure: [sign, x, yhigh, ylow, sign, x, yhigh, ylow, ...]
             const points = new LargeArr(params.count * 4);
             let offset = 0;
@@ -202,7 +202,7 @@ const candlestickLayout: StageHandler = {
     }
 };
 
-function getSign(data: List, dataIndex: number, openVal: number, closeVal: number, closeDim: string) {
+function getSign(data: SeriesData, dataIndex: number, openVal: number, closeVal: number, closeDim: string) {
     let sign;
     if (openVal > closeVal) {
         sign = -1;
@@ -221,7 +221,7 @@ function getSign(data: List, dataIndex: number, openVal: number, closeVal: numbe
     return sign;
 }
 
-function calculateCandleWidth(seriesModel: CandlestickSeriesModel, data: List) {
+function calculateCandleWidth(seriesModel: CandlestickSeriesModel, data: SeriesData) {
     const baseAxis = seriesModel.getBaseAxis();
     let extent;
 
diff --git a/src/chart/custom/CustomSeries.ts b/src/chart/custom/CustomSeries.ts
index b027f41..912ca33 100644
--- a/src/chart/custom/CustomSeries.ts
+++ b/src/chart/custom/CustomSeries.ts
@@ -44,9 +44,9 @@ import {
     ZRStyleProps
 } from '../../util/types';
 import Element, { ElementProps } from 'zrender/src/Element';
-import List, { DefaultDataVisual } from '../../data/List';
+import SeriesData, { DefaultDataVisual } from '../../data/SeriesData';
 import GlobalModel from '../../model/Global';
-import createListFromArray from '../helper/createListFromArray';
+import createListFromArray from '../helper/createSeriesDataFromArray';
 import { makeInner } from '../../util/model';
 import { CoordinateSystem } from '../../coord/CoordinateSystem';
 import SeriesModel from '../../model/Series';
@@ -413,7 +413,7 @@ export default class CustomSeriesModel extends SeriesModel<CustomSeriesOption> {
         this.currentZ = this.get('z', true);
     }
 
-    getInitialData(option: CustomSeriesOption, ecModel: GlobalModel): List {
+    getInitialData(option: CustomSeriesOption, ecModel: GlobalModel): SeriesData {
         return createListFromArray(this.getSource(), this);
     }
 
diff --git a/src/chart/custom/CustomView.ts b/src/chart/custom/CustomView.ts
index 44bc7ad..0d14ee7 100644
--- a/src/chart/custom/CustomView.ts
+++ b/src/chart/custom/CustomView.ts
@@ -51,7 +51,7 @@ import prepareGeo from '../../coord/geo/prepareCustom';
 import prepareSingleAxis from '../../coord/single/prepareCustom';
 import preparePolar from '../../coord/polar/prepareCustom';
 import prepareCalendar from '../../coord/calendar/prepareCustom';
-import List, { DefaultDataVisual } from '../../data/List';
+import SeriesData, { DefaultDataVisual } from '../../data/SeriesData';
 import GlobalModel from '../../model/Global';
 import ExtensionAPI from '../../core/ExtensionAPI';
 import Displayable, { DisplayableProps } from 'zrender/src/graphic/Displayable';
@@ -199,7 +199,7 @@ export default class CustomChartView extends ChartView {
     static type = 'custom';
     readonly type = CustomChartView.type;
 
-    private _data: List;
+    private _data: SeriesData;
 
     render(
         customSeries: CustomSeriesModel,
@@ -822,7 +822,7 @@ function updateZForEachState(
 
 function makeRenderItem(
     customSeries: CustomSeriesModel,
-    data: List<CustomSeriesModel>,
+    data: SeriesData<CustomSeriesModel>,
     ecModel: GlobalModel,
     api: ExtensionAPI
 ) {
@@ -1129,7 +1129,7 @@ function makeRenderItem(
     }
 }
 
-function wrapEncodeDef(data: List<CustomSeriesModel>): WrapEncodeDefRet {
+function wrapEncodeDef(data: SeriesData<CustomSeriesModel>): WrapEncodeDefRet {
     const encodeDef = {} as WrapEncodeDefRet;
     each(data.dimensions, function (dimName, dataDimIndex) {
         const dimInfo = data.getDimensionInfo(dimName);
@@ -1149,7 +1149,7 @@ function createOrUpdateItem(
     elOption: CustomElementOption,
     seriesModel: CustomSeriesModel,
     group: ViewRootGroup,
-    data: List<CustomSeriesModel>
+    data: SeriesData<CustomSeriesModel>
 ): Element {
     // [Rule]
     // If `renderItem` returns `null`/`undefined`/`false`, remove the previous el if existing.
diff --git a/src/chart/effectScatter/EffectScatterSeries.ts b/src/chart/effectScatter/EffectScatterSeries.ts
index 5d16446..dbdfaf4 100644
--- a/src/chart/effectScatter/EffectScatterSeries.ts
+++ b/src/chart/effectScatter/EffectScatterSeries.ts
@@ -17,7 +17,7 @@
 * under the License.
 */
 
-import createListFromArray from '../helper/createListFromArray';
+import createListFromArray from '../helper/createSeriesDataFromArray';
 import SeriesModel from '../../model/Series';
 import {
     SeriesOption,
@@ -35,7 +35,7 @@ import {
     CallbackDataParams
 } from '../../util/types';
 import GlobalModel from '../../model/Global';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import type { SymbolDrawItemModelOption } from '../helper/SymbolDraw';
 import { BrushCommonSelectorsForSeries } from '../../component/brush/selector';
 
@@ -88,11 +88,11 @@ class EffectScatterSeriesModel extends SeriesModel<EffectScatterSeriesOption> {
 
     hasSymbolVisual = true;
 
-    getInitialData(option: EffectScatterSeriesOption, ecModel: GlobalModel): List {
+    getInitialData(option: EffectScatterSeriesOption, ecModel: GlobalModel): SeriesData {
         return createListFromArray(this.getSource(), this, {useEncodeDefaulter: true});
     }
 
-    brushSelector(dataIndex: number, data: List, selectors: BrushCommonSelectorsForSeries): boolean {
+    brushSelector(dataIndex: number, data: SeriesData, selectors: BrushCommonSelectorsForSeries): boolean {
         return selectors.point(data.getItemLayout(dataIndex));
     }
 
diff --git a/src/chart/funnel/FunnelSeries.ts b/src/chart/funnel/FunnelSeries.ts
index 96a7739..9c1cd83 100644
--- a/src/chart/funnel/FunnelSeries.ts
+++ b/src/chart/funnel/FunnelSeries.ts
@@ -18,7 +18,7 @@
 */
 
 import * as zrUtil from 'zrender/src/core/util';
-import createListSimply from '../helper/createListSimply';
+import createSeriesDataSimply from '../helper/createSeriesDataSimply';
 import {defaultEmphasis} from '../../util/model';
 import {makeSeriesEncodeForNameBased} from '../../data/helper/sourceHelper';
 import LegendVisualProvider from '../../visual/LegendVisualProvider';
@@ -39,7 +39,7 @@ import {
     SeriesEncodeOptionMixin
 } from '../../util/types';
 import GlobalModel from '../../model/Global';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 
 type FunnelLabelOption = Omit<SeriesLabelOption, 'position'> & {
     position?: LabelOption['position']
@@ -104,8 +104,8 @@ class FunnelSeriesModel extends SeriesModel<FunnelSeriesOption> {
         this._defaultLabelLine(option);
     }
 
-    getInitialData(this: FunnelSeriesModel, option: FunnelSeriesOption, ecModel: GlobalModel): List {
-        return createListSimply(this, {
+    getInitialData(this: FunnelSeriesModel, option: FunnelSeriesOption, ecModel: GlobalModel): SeriesData {
+        return createSeriesDataSimply(this, {
             coordDimensions: ['value'],
             encodeDefaulter: zrUtil.curry(makeSeriesEncodeForNameBased, this)
         });
diff --git a/src/chart/funnel/FunnelView.ts b/src/chart/funnel/FunnelView.ts
index 4a58858..e379e64 100644
--- a/src/chart/funnel/FunnelView.ts
+++ b/src/chart/funnel/FunnelView.ts
@@ -23,7 +23,7 @@ import ChartView from '../../view/Chart';
 import FunnelSeriesModel, {FunnelDataItemOption} from './FunnelSeries';
 import GlobalModel from '../../model/Global';
 import ExtensionAPI from '../../core/ExtensionAPI';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import { ColorString } from '../../util/types';
 import { setLabelLineStyle, getLabelLineStatesModels } from '../../label/labelGuideHelper';
 import { setLabelStyle, getLabelStatesModels } from '../../label/labelStyle';
@@ -36,7 +36,7 @@ const opacityAccessPath = ['itemStyle', 'opacity'] as const;
  */
 class FunnelPiece extends graphic.Polygon {
 
-    constructor(data: List, idx: number) {
+    constructor(data: SeriesData, idx: number) {
         super();
 
         const polygon = this;
@@ -48,7 +48,7 @@ class FunnelPiece extends graphic.Polygon {
         this.updateData(data, idx, true);
     }
 
-    updateData(data: List, idx: number, firstCreate?: boolean) {
+    updateData(data: SeriesData, idx: number, firstCreate?: boolean) {
 
         const polygon = this;
 
@@ -95,7 +95,7 @@ class FunnelPiece extends graphic.Polygon {
         enableHoverEmphasis(this, emphasisModel.get('focus'), emphasisModel.get('blurScope'));
     }
 
-    _updateLabel(data: List, idx: number) {
+    _updateLabel(data: SeriesData, idx: number) {
         const polygon = this;
         const labelLine = this.getTextGuideLine();
         const labelText = polygon.getTextContent();
@@ -168,7 +168,7 @@ class FunnelView extends ChartView {
     static type = 'funnel' as const;
     type = FunnelView.type;
 
-    private _data: List;
+    private _data: SeriesData;
 
     ignoreLabelLineUpdate = true;
 
diff --git a/src/chart/funnel/funnelLayout.ts b/src/chart/funnel/funnelLayout.ts
index 71111c3..cb11676 100644
--- a/src/chart/funnel/funnelLayout.ts
+++ b/src/chart/funnel/funnelLayout.ts
@@ -21,7 +21,7 @@ import * as layout from '../../util/layout';
 import {parsePercent, linearMap} from '../../util/number';
 import FunnelSeriesModel, { FunnelSeriesOption, FunnelDataItemOption } from './FunnelSeries';
 import ExtensionAPI from '../../core/ExtensionAPI';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import GlobalModel from '../../model/Global';
 
 function getViewRect(seriesModel: FunnelSeriesModel, api: ExtensionAPI) {
@@ -33,7 +33,7 @@ function getViewRect(seriesModel: FunnelSeriesModel, api: ExtensionAPI) {
     );
 }
 
-function getSortedIndices(data: List, sort: FunnelSeriesOption['sort']) {
+function getSortedIndices(data: SeriesData, sort: FunnelSeriesOption['sort']) {
     const valueDim = data.mapDimension('value');
     const valueArr = data.mapArray(valueDim, function (val: number) {
         return val;
@@ -58,7 +58,7 @@ function getSortedIndices(data: List, sort: FunnelSeriesOption['sort']) {
     return indices;
 }
 
-function labelLayout(data: List) {
+function labelLayout(data: SeriesData) {
     const seriesModel = data.hostModel;
     const orient = seriesModel.get('orient');
     data.each(function (idx) {
diff --git a/src/chart/gauge/GaugeSeries.ts b/src/chart/gauge/GaugeSeries.ts
index b80652f..e54f659 100644
--- a/src/chart/gauge/GaugeSeries.ts
+++ b/src/chart/gauge/GaugeSeries.ts
@@ -17,7 +17,7 @@
 * under the License.
 */
 
-import createListSimply from '../helper/createListSimply';
+import createSeriesDataSimply from '../helper/createSeriesDataSimply';
 import SeriesModel from '../../model/Series';
 import {
     SeriesOption,
@@ -31,7 +31,7 @@ import {
     SeriesEncodeOptionMixin
 } from '../../util/types';
 import GlobalModel from '../../model/Global';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 
 // [percent, color]
 type GaugeColorStop = [number, ColorString];
@@ -180,8 +180,8 @@ class GaugeSeriesModel extends SeriesModel<GaugeSeriesOption> {
     visualStyleAccessPath = 'itemStyle';
     useColorPaletteOnData = true;
 
-    getInitialData(option: GaugeSeriesOption, ecModel: GlobalModel): List {
-        return createListSimply(this, ['value']);
+    getInitialData(option: GaugeSeriesOption, ecModel: GlobalModel): SeriesData {
+        return createSeriesDataSimply(this, ['value']);
     }
 
     static defaultOption: GaugeSeriesOption = {
diff --git a/src/chart/gauge/GaugeView.ts b/src/chart/gauge/GaugeView.ts
index a4e9005..4f10512 100644
--- a/src/chart/gauge/GaugeView.ts
+++ b/src/chart/gauge/GaugeView.ts
@@ -27,7 +27,7 @@ import GaugeSeriesModel, { GaugeDataItemOption } from './GaugeSeries';
 import GlobalModel from '../../model/Global';
 import ExtensionAPI from '../../core/ExtensionAPI';
 import { ColorString, ECElement } from '../../util/types';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import Sausage from '../../util/shape/sausage';
 import {createSymbol} from '../../util/symbol';
 import ZRImage from 'zrender/src/graphic/Image';
@@ -77,7 +77,7 @@ class GaugeView extends ChartView {
     static type = 'gauge' as const;
     type = GaugeView.type;
 
-    private _data: List;
+    private _data: SeriesData;
     private _progressEls: graphic.Path[];
 
     private _titleEls: graphic.Text[];
diff --git a/src/chart/graph/GraphSeries.ts b/src/chart/graph/GraphSeries.ts
index 54f9f36..42b833d 100644
--- a/src/chart/graph/GraphSeries.ts
+++ b/src/chart/graph/GraphSeries.ts
@@ -17,7 +17,7 @@
 * under the License.
 */
 
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import * as zrUtil from 'zrender/src/core/util';
 import {defaultEmphasis} from '../../util/model';
 import Model from '../../model/Model';
@@ -230,7 +230,7 @@ class GraphSeriesModel extends SeriesModel<GraphSeriesOption> {
 
     static readonly dependencies = ['grid', 'polar', 'geo', 'singleAxis', 'calendar'];
 
-    private _categoriesData: List;
+    private _categoriesData: SeriesData;
     private _categoriesModels: Model<GraphCategoryItemOption>[];
 
     /**
@@ -272,7 +272,7 @@ class GraphSeriesModel extends SeriesModel<GraphSeriesOption> {
         defaultEmphasis(option, 'edgeLabel', ['show']);
     }
 
-    getInitialData(option: GraphSeriesOption, ecModel: GlobalModel): List {
+    getInitialData(option: GraphSeriesOption, ecModel: GlobalModel): SeriesData {
         const edges = option.edges || option.links || [];
         const nodes = option.data || option.nodes || [];
         const self = this;
@@ -287,7 +287,7 @@ class GraphSeriesModel extends SeriesModel<GraphSeriesOption> {
             return graph.data;
         }
 
-        function beforeLink(nodeData: List, edgeData: List) {
+        function beforeLink(nodeData: SeriesData, edgeData: SeriesData) {
             // Overwrite nodeData.getItemModel to
             nodeData.wrapMethod('getItemModel', function (model) {
                 const categoriesModels = self._categoriesModels;
@@ -335,10 +335,10 @@ class GraphSeriesModel extends SeriesModel<GraphSeriesOption> {
     }
 
     getEdgeData() {
-        return this.getGraph().edgeData as List<GraphSeriesModel, LineDataVisual>;
+        return this.getGraph().edgeData as SeriesData<GraphSeriesModel, LineDataVisual>;
     }
 
-    getCategoriesData(): List {
+    getCategoriesData(): SeriesData {
         return this._categoriesData;
     }
 
@@ -380,7 +380,7 @@ class GraphSeriesModel extends SeriesModel<GraphSeriesOption> {
                 value: 0
             }, category);
         });
-        const categoriesData = new List(['value'], this);
+        const categoriesData = new SeriesData(['value'], this);
         categoriesData.initData(categories);
 
         this._categoriesData = categoriesData;
diff --git a/src/chart/graph/GraphView.ts b/src/chart/graph/GraphView.ts
index 1c8d10f..08cc6ae 100644
--- a/src/chart/graph/GraphView.ts
+++ b/src/chart/graph/GraphView.ts
@@ -33,7 +33,7 @@ import GraphSeriesModel, { GraphNodeItemOption, GraphEdgeItemOption } from './Gr
 import { CoordinateSystem } from '../../coord/CoordinateSystem';
 import View from '../../coord/View';
 import Symbol from '../helper/Symbol';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import Line from '../helper/Line';
 import { getECData } from '../../util/innerStore';
 
@@ -109,7 +109,7 @@ class GraphView extends ChartView {
 
         const edgeData = seriesModel.getEdgeData();
         // TODO: TYPE
-        lineDraw.updateData(edgeData as List);
+        lineDraw.updateData(edgeData as SeriesData);
 
         this._updateNodeAndLinkScale();
 
diff --git a/src/chart/graph/circularLayoutHelper.ts b/src/chart/graph/circularLayoutHelper.ts
index a81538c..91265be 100644
--- a/src/chart/graph/circularLayoutHelper.ts
+++ b/src/chart/graph/circularLayoutHelper.ts
@@ -22,7 +22,7 @@ import * as vec2 from 'zrender/src/core/vector';
 import {getSymbolSize, getNodeGlobalScale} from './graphHelper';
 import GraphSeriesModel, { GraphEdgeItemOption } from './GraphSeries';
 import Graph from '../../data/Graph';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import * as zrUtil from 'zrender/src/core/util';
 import {getCurvenessForEdge} from '../helper/multipleGraphEdgeHelper';
 
@@ -105,7 +105,7 @@ interface LayoutNode {
     (
         seriesModel: GraphSeriesModel,
         graph: Graph,
-        nodeData: List,
+        nodeData: SeriesData,
         r: number,
         cx: number,
         cy: number,
diff --git a/src/chart/heatmap/HeatmapSeries.ts b/src/chart/heatmap/HeatmapSeries.ts
index af831ef..9da21ae 100644
--- a/src/chart/heatmap/HeatmapSeries.ts
+++ b/src/chart/heatmap/HeatmapSeries.ts
@@ -18,7 +18,7 @@
 */
 
 import SeriesModel from '../../model/Series';
-import createListFromArray from '../helper/createListFromArray';
+import createListFromArray from '../helper/createSeriesDataFromArray';
 import CoordinateSystem from '../../core/CoordinateSystem';
 import {
     SeriesOption,
@@ -32,7 +32,7 @@ import {
     SeriesOnCalendarOptionMixin
 } from '../../util/types';
 import GlobalModel from '../../model/Global';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import type Geo from '../../coord/geo/Geo';
 import type Cartesian2D from '../../coord/cartesian/Cartesian2D';
 import type Calendar from '../../coord/calendar/Calendar';
@@ -72,7 +72,7 @@ class HeatmapSeriesModel extends SeriesModel<HeatmapSeriesOption> {
     // @ts-ignore
     coordinateSystem: Cartesian2D | Geo | Calendar;
 
-    getInitialData(option: HeatmapSeriesOption, ecModel: GlobalModel): List {
+    getInitialData(option: HeatmapSeriesOption, ecModel: GlobalModel): SeriesData {
         return createListFromArray(this.getSource(), this, {
             generateCoord: 'value'
         });
diff --git a/src/chart/helper/EffectLine.ts b/src/chart/helper/EffectLine.ts
index d269faa..bc92620 100644
--- a/src/chart/helper/EffectLine.ts
+++ b/src/chart/helper/EffectLine.ts
@@ -27,7 +27,7 @@ import * as zrUtil from 'zrender/src/core/util';
 import {createSymbol} from '../../util/symbol';
 import * as vec2 from 'zrender/src/core/vector';
 import * as curveUtil from 'zrender/src/core/curve';
-import type List from '../../data/List';
+import type SeriesData from '../../data/SeriesData';
 import { LineDrawSeriesScope, LineDrawModelOption } from './LineDraw';
 import Model from '../../model/Model';
 import { ColorString } from '../../util/types';
@@ -49,18 +49,18 @@ class EffectLine extends graphic.Group {
 
     private _symbolScale: number[];
 
-    constructor(lineData: List, idx: number, seriesScope: LineDrawSeriesScope) {
+    constructor(lineData: SeriesData, idx: number, seriesScope: LineDrawSeriesScope) {
         super();
         this.add(this.createLine(lineData, idx, seriesScope));
 
         this._updateEffectSymbol(lineData, idx);
     }
 
-    createLine(lineData: List, idx: number, seriesScope: LineDrawSeriesScope): graphic.Group {
+    createLine(lineData: SeriesData, idx: number, seriesScope: LineDrawSeriesScope): graphic.Group {
         return new Line(lineData, idx, seriesScope);
     }
 
-    private _updateEffectSymbol(lineData: List, idx: number) {
+    private _updateEffectSymbol(lineData: SeriesData, idx: number) {
         const itemModel = lineData.getItemModel<LineDrawModelOption>(idx);
         const effectModel = itemModel.getModel('effect');
         let size = effectModel.get('symbolSize');
@@ -107,7 +107,7 @@ class EffectLine extends graphic.Group {
     }
 
     private _updateEffectAnimation(
-        lineData: List,
+        lineData: SeriesData,
         effectModel: Model<LineDrawModelOption['effect']>,
         idx: number
     ) {
@@ -189,7 +189,7 @@ class EffectLine extends graphic.Group {
         ];
     }
 
-    updateData(lineData: List, idx: number, seriesScope: LineDrawSeriesScope) {
+    updateData(lineData: SeriesData, idx: number, seriesScope: LineDrawSeriesScope) {
         (this.childAt(0) as Line).updateData(lineData, idx, seriesScope);
         this._updateEffectSymbol(lineData, idx);
     }
@@ -236,7 +236,7 @@ class EffectLine extends graphic.Group {
     }
 
 
-    updateLayout(lineData: List, idx: number) {
+    updateLayout(lineData: SeriesData, idx: number) {
         (this.childAt(0) as Line).updateLayout(lineData, idx);
 
         const effectModel = lineData.getItemModel<LineDrawModelOption>(idx).getModel('effect');
diff --git a/src/chart/helper/EffectPolyline.ts b/src/chart/helper/EffectPolyline.ts
index 8049975..a250cb5 100644
--- a/src/chart/helper/EffectPolyline.ts
+++ b/src/chart/helper/EffectPolyline.ts
@@ -21,7 +21,7 @@ import Polyline from './Polyline';
 import EffectLine, {ECSymbolOnEffectLine} from './EffectLine';
 import * as vec2 from 'zrender/src/core/vector';
 import { LineDrawSeriesScope } from './LineDraw';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 
 
 class EffectPolyline extends EffectLine {
@@ -33,7 +33,7 @@ class EffectPolyline extends EffectLine {
     private _offsets: number[];
 
     // Override
-    createLine(lineData: List, idx: number, seriesScope: LineDrawSeriesScope) {
+    createLine(lineData: SeriesData, idx: number, seriesScope: LineDrawSeriesScope) {
         return new Polyline(lineData, idx, seriesScope);
     };
 
diff --git a/src/chart/helper/EffectSymbol.ts b/src/chart/helper/EffectSymbol.ts
index c20a160..db8db9a 100644
--- a/src/chart/helper/EffectSymbol.ts
+++ b/src/chart/helper/EffectSymbol.ts
@@ -21,7 +21,7 @@ import {createSymbol, normalizeSymbolOffset, normalizeSymbolSize} from '../../ut
 import {Group, Path} from '../../util/graphic';
 import { enterEmphasis, leaveEmphasis, enableHoverEmphasis } from '../../util/states';
 import SymbolClz from './Symbol';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import type { ZRColor, ECElement } from '../../util/types';
 import type Displayable from 'zrender/src/graphic/Displayable';
 import { SymbolDrawItemModelOption } from './SymbolDraw';
@@ -59,7 +59,7 @@ class EffectSymbol extends Group {
 
     private _effectCfg: RippleEffectCfg;
 
-    constructor(data: List, idx: number) {
+    constructor(data: SeriesData, idx: number) {
         super();
 
         const symbol = new SymbolClz(data, idx);
@@ -162,7 +162,7 @@ class EffectSymbol extends Group {
     /**
      * Update symbol properties
      */
-    updateData(data: List, idx: number) {
+    updateData(data: SeriesData, idx: number) {
         const seriesModel = data.hostModel;
 
         (this.childAt(0) as SymbolClz).updateData(data, idx);
diff --git a/src/chart/helper/LargeLineDraw.ts b/src/chart/helper/LargeLineDraw.ts
index 50e4f8d..fca6979 100644
--- a/src/chart/helper/LargeLineDraw.ts
+++ b/src/chart/helper/LargeLineDraw.ts
@@ -24,7 +24,7 @@ import IncrementalDisplayable from 'zrender/src/graphic/IncrementalDisplayable';
 import * as lineContain from 'zrender/src/contain/line';
 import * as quadraticContain from 'zrender/src/contain/quadratic';
 import { PathProps } from 'zrender/src/graphic/Path';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import { StageHandlerProgressParams, LineStyleOption, ColorString } from '../../util/types';
 import Model from '../../model/Model';
 import { getECData } from '../../util/innerStore';
@@ -49,7 +49,7 @@ interface LargeLinesCommonOption {
 /**
  * Data which can support large lines.
  */
-type LargeLinesData = List<Model<LargeLinesCommonOption> & {
+type LargeLinesData = SeriesData<Model<LargeLinesCommonOption> & {
     seriesIndex?: number
 }>;
 
diff --git a/src/chart/helper/LargeSymbolDraw.ts b/src/chart/helper/LargeSymbolDraw.ts
index 1840c54..3f3bcbb 100644
--- a/src/chart/helper/LargeSymbolDraw.ts
+++ b/src/chart/helper/LargeSymbolDraw.ts
@@ -24,7 +24,7 @@
 import * as graphic from '../../util/graphic';
 import {createSymbol} from '../../util/symbol';
 import IncrementalDisplayable from 'zrender/src/graphic/IncrementalDisplayable';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import { PathProps } from 'zrender/src/graphic/Path';
 import PathProxy from 'zrender/src/core/PathProxy';
 import SeriesModel from '../../model/Series';
@@ -182,7 +182,7 @@ class LargeSymbolDraw {
     /**
      * Update symbols draw by new data
      */
-    updateData(data: List, opt?: UpdateOpt) {
+    updateData(data: SeriesData, opt?: UpdateOpt) {
         this.group.removeAll();
         const symbolEl = new LargeSymbolPath({
             rectHover: true,
@@ -198,7 +198,7 @@ class LargeSymbolDraw {
         this._incremental = null;
     }
 
-    updateLayout(data: List) {
+    updateLayout(data: SeriesData) {
         if (this._incremental) {
             return;
         }
@@ -214,7 +214,7 @@ class LargeSymbolDraw {
         });
     }
 
-    incrementalPrepareUpdate(data: List) {
+    incrementalPrepareUpdate(data: SeriesData) {
         this.group.removeAll();
 
         this._clearIncremental();
@@ -233,7 +233,7 @@ class LargeSymbolDraw {
         }
     }
 
-    incrementalUpdate(taskParams: StageHandlerProgressParams, data: List, opt: UpdateOpt) {
+    incrementalUpdate(taskParams: StageHandlerProgressParams, data: SeriesData, opt: UpdateOpt) {
         let symbolEl;
         if (this._incremental) {
             symbolEl = new LargeSymbolPath();
@@ -258,7 +258,7 @@ class LargeSymbolDraw {
 
     _setCommon(
         symbolEl: LargeSymbolPath,
-        data: List,
+        data: SeriesData,
         isIncremental: boolean,
         opt: UpdateOpt
     ) {
diff --git a/src/chart/helper/Line.ts b/src/chart/helper/Line.ts
index 140509d..b806d0f 100644
--- a/src/chart/helper/Line.ts
+++ b/src/chart/helper/Line.ts
@@ -25,7 +25,7 @@ import * as graphic from '../../util/graphic';
 import { enableHoverEmphasis, enterEmphasis, leaveEmphasis, SPECIAL_STATES } from '../../util/states';
 import {getLabelStatesModels, setLabelStyle} from '../../label/labelStyle';
 import {round} from '../../util/number';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import { ZRTextAlign, ZRTextVerticalAlign, LineLabelOption, ColorString } from '../../util/types';
 import SeriesModel from '../../model/Series';
 import type { LineDrawSeriesScope, LineDrawModelOption } from './LineDraw';
@@ -41,7 +41,7 @@ type LineECSymbol = ECSymbol & {
     __specifiedRotation: number
 };
 
-type LineList = List<SeriesModel, LineDataVisual>;
+type LineList = SeriesData<SeriesModel, LineDataVisual>;
 
 export interface LineLabel extends graphic.Text {
     lineLabelOriginalOpacity: number
@@ -133,7 +133,7 @@ class Line extends graphic.Group {
     private _fromSymbolType: string;
     private _toSymbolType: string;
 
-    constructor(lineData: List, idx: number, seriesScope?: LineDrawSeriesScope) {
+    constructor(lineData: SeriesData, idx: number, seriesScope?: LineDrawSeriesScope) {
         super();
         this._createLine(lineData as LineList, idx, seriesScope);
     }
@@ -164,7 +164,7 @@ class Line extends graphic.Group {
     }
 
     // TODO More strict on the List type in parameters?
-    updateData(lineData: List, idx: number, seriesScope: LineDrawSeriesScope) {
+    updateData(lineData: SeriesData, idx: number, seriesScope: LineDrawSeriesScope) {
         const seriesModel = lineData.hostModel;
 
         const line = this.childOfName('line') as ECLinePath;
@@ -195,7 +195,7 @@ class Line extends graphic.Group {
         return this.childAt(0) as graphic.Line;
     }
 
-    _updateCommonStl(lineData: List, idx: number, seriesScope?: LineDrawSeriesScope) {
+    _updateCommonStl(lineData: SeriesData, idx: number, seriesScope?: LineDrawSeriesScope) {
         const seriesModel = lineData.hostModel as SeriesModel;
 
         const line = this.childOfName('line') as ECLinePath;
@@ -307,7 +307,7 @@ class Line extends graphic.Group {
         leaveEmphasis(this);
     }
 
-    updateLayout(lineData: List, idx: number) {
+    updateLayout(lineData: SeriesData, idx: number) {
         this.setLinePoints(lineData.getItemLayout(idx));
     }
 
diff --git a/src/chart/helper/LineDraw.ts b/src/chart/helper/LineDraw.ts
index 1dde5d3..60317bc 100644
--- a/src/chart/helper/LineDraw.ts
+++ b/src/chart/helper/LineDraw.ts
@@ -19,7 +19,7 @@
 
 import * as graphic from '../../util/graphic';
 import LineGroup from './Line';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import {
     StageHandlerProgressParams,
     LineStyleOption,
@@ -36,13 +36,13 @@ import Model from '../../model/Model';
 import { getLabelStatesModels } from '../../label/labelStyle';
 
 interface LineLike extends graphic.Group {
-    updateData(data: List, idx: number, scope?: LineDrawSeriesScope): void
-    updateLayout(data: List, idx: number): void
+    updateData(data: SeriesData, idx: number, scope?: LineDrawSeriesScope): void
+    updateLayout(data: SeriesData, idx: number): void
     fadeOut?(cb: () => void): void
 }
 
 interface LineLikeCtor {
-    new(data: List, idx: number, scope?: LineDrawSeriesScope): LineLike
+    new(data: SeriesData, idx: number, scope?: LineDrawSeriesScope): LineLike
 }
 
 interface LineDrawStateOption {
@@ -76,7 +76,7 @@ export interface LineDrawModelOption extends LineDrawStateOption, StatesOptionMi
     }
 }
 
-type ListForLineDraw = List<Model<LineDrawModelOption & AnimationOptionMixin>>;
+type ListForLineDraw = SeriesData<Model<LineDrawModelOption & AnimationOptionMixin>>;
 
 export interface LineDrawSeriesScope {
     lineStyle?: ZRStyleProps
diff --git a/src/chart/helper/Polyline.ts b/src/chart/helper/Polyline.ts
index 29d0715..a371edc 100644
--- a/src/chart/helper/Polyline.ts
+++ b/src/chart/helper/Polyline.ts
@@ -20,15 +20,15 @@
 import * as graphic from '../../util/graphic';
 import { enableHoverEmphasis } from '../../util/states';
 import type { LineDrawSeriesScope, LineDrawModelOption } from './LineDraw';
-import type List from '../../data/List';
+import type SeriesData from '../../data/SeriesData';
 
 class Polyline extends graphic.Group {
-    constructor(lineData: List, idx: number, seriesScope: LineDrawSeriesScope) {
+    constructor(lineData: SeriesData, idx: number, seriesScope: LineDrawSeriesScope) {
         super();
         this._createPolyline(lineData, idx, seriesScope);
     }
 
-    private _createPolyline(lineData: List, idx: number, seriesScope: LineDrawSeriesScope) {
+    private _createPolyline(lineData: SeriesData, idx: number, seriesScope: LineDrawSeriesScope) {
         // let seriesModel = lineData.hostModel;
         const points = lineData.getItemLayout(idx);
 
@@ -43,7 +43,7 @@ class Polyline extends graphic.Group {
         this._updateCommonStl(lineData, idx, seriesScope);
     };
 
-    updateData(lineData: List, idx: number, seriesScope: LineDrawSeriesScope) {
+    updateData(lineData: SeriesData, idx: number, seriesScope: LineDrawSeriesScope) {
         const seriesModel = lineData.hostModel;
 
         const line = this.childAt(0) as graphic.Polyline;
@@ -57,7 +57,7 @@ class Polyline extends graphic.Group {
         this._updateCommonStl(lineData, idx, seriesScope);
     };
 
-    _updateCommonStl(lineData: List, idx: number, seriesScope: LineDrawSeriesScope) {
+    _updateCommonStl(lineData: SeriesData, idx: number, seriesScope: LineDrawSeriesScope) {
         const line = this.childAt(0) as graphic.Polyline;
         const itemModel = lineData.getItemModel<LineDrawModelOption>(idx);
 
@@ -77,7 +77,7 @@ class Polyline extends graphic.Group {
         enableHoverEmphasis(this);
     };
 
-    updateLayout(lineData: List, idx: number) {
+    updateLayout(lineData: SeriesData, idx: number) {
         const polyline = this.childAt(0) as graphic.Polyline;
         polyline.setShape('points', lineData.getItemLayout(idx));
     };
diff --git a/src/chart/helper/Symbol.ts b/src/chart/helper/Symbol.ts
index 222776f..be2a5a4 100644
--- a/src/chart/helper/Symbol.ts
+++ b/src/chart/helper/Symbol.ts
@@ -22,7 +22,7 @@ import * as graphic from '../../util/graphic';
 import {getECData} from '../../util/innerStore';
 import { enterEmphasis, leaveEmphasis, enableHoverEmphasis } from '../../util/states';
 import {getDefaultLabel} from './labelHelper';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import { ColorString, BlurScope, AnimationOption, ZRColor } from '../../util/types';
 import SeriesModel from '../../model/Series';
 import { PathProps } from 'zrender/src/graphic/Path';
@@ -55,14 +55,14 @@ class Symbol extends graphic.Group {
 
     private _z2: number;
 
-    constructor(data: List, idx: number, seriesScope?: SymbolDrawSeriesScope, opts?: SymbolOpts) {
+    constructor(data: SeriesData, idx: number, seriesScope?: SymbolDrawSeriesScope, opts?: SymbolOpts) {
         super();
         this.updateData(data, idx, seriesScope, opts);
     }
 
     _createSymbol(
         symbolType: string,
-        data: List,
+        data: SeriesData,
         idx: number,
         symbolSize: number[],
         keepAspect: boolean
@@ -151,7 +151,7 @@ class Symbol extends graphic.Group {
     /**
      * Update symbol properties
      */
-    updateData(data: List, idx: number, seriesScope?: SymbolDrawSeriesScope, opts?: SymbolOpts) {
+    updateData(data: SeriesData, idx: number, seriesScope?: SymbolDrawSeriesScope, opts?: SymbolOpts) {
         this.silent = false;
 
         const symbolType = data.getItemVisual(idx, 'symbol') || 'circle';
@@ -206,7 +206,7 @@ class Symbol extends graphic.Group {
     }
 
     _updateCommon(
-        data: List,
+        data: SeriesData,
         idx: number,
         symbolSize: number[],
         seriesScope?: SymbolDrawSeriesScope,
@@ -395,7 +395,7 @@ class Symbol extends graphic.Group {
         );
     }
 
-    static getSymbolSize(data: List, idx: number) {
+    static getSymbolSize(data: SeriesData, idx: number) {
         return normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize'));
     }
 }
diff --git a/src/chart/helper/SymbolDraw.ts b/src/chart/helper/SymbolDraw.ts
index 9f53db8..28d10e7 100644
--- a/src/chart/helper/SymbolDraw.ts
+++ b/src/chart/helper/SymbolDraw.ts
@@ -20,7 +20,7 @@
 import * as graphic from '../../util/graphic';
 import SymbolClz from './Symbol';
 import { isObject } from 'zrender/src/core/util';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import type Displayable from 'zrender/src/graphic/Displayable';
 import {
     StageHandlerProgressParams,
@@ -49,15 +49,15 @@ interface UpdateOpt {
 }
 
 interface SymbolLike extends graphic.Group {
-    updateData(data: List, idx: number, scope?: SymbolDrawSeriesScope, opt?: UpdateOpt): void
+    updateData(data: SeriesData, idx: number, scope?: SymbolDrawSeriesScope, opt?: UpdateOpt): void
     fadeOut?(cb: () => void): void
 }
 
 interface SymbolLikeCtor {
-    new(data: List, idx: number, scope?: SymbolDrawSeriesScope, opt?: UpdateOpt): SymbolLike
+    new(data: SeriesData, idx: number, scope?: SymbolDrawSeriesScope, opt?: UpdateOpt): SymbolLike
 }
 
-function symbolNeedsDraw(data: List, point: number[], idx: number, opt: UpdateOpt) {
+function symbolNeedsDraw(data: SeriesData, point: number[], idx: number, opt: UpdateOpt) {
     return point && !isNaN(point[0]) && !isNaN(point[1])
         && !(opt.isIgnore && opt.isIgnore(idx))
         // We do not set clipShape on group, because it will cut part of
@@ -125,7 +125,7 @@ export interface SymbolDrawSeriesScope {
     fadeIn?: boolean
 }
 
-function makeSeriesScope(data: List): SymbolDrawSeriesScope {
+function makeSeriesScope(data: SeriesData): SymbolDrawSeriesScope {
     const seriesModel = data.hostModel as Model<ScatterSeriesOption>;
     const emphasisModel = seriesModel.getModel('emphasis');
     return {
@@ -144,7 +144,7 @@ function makeSeriesScope(data: List): SymbolDrawSeriesScope {
     };
 }
 
-export type ListForSymbolDraw = List<Model<SymbolDrawItemModelOption & AnimationOptionMixin>>;
+export type ListForSymbolDraw = SeriesData<Model<SymbolDrawItemModelOption & AnimationOptionMixin>>;
 
 class SymbolDraw {
     group = new graphic.Group();
diff --git a/src/chart/helper/createGraphFromNodeEdge.ts b/src/chart/helper/createGraphFromNodeEdge.ts
index e2a0fca..80a43a3 100644
--- a/src/chart/helper/createGraphFromNodeEdge.ts
+++ b/src/chart/helper/createGraphFromNodeEdge.ts
@@ -19,12 +19,12 @@
 
 
 import * as zrUtil from 'zrender/src/core/util';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import Graph from '../../data/Graph';
-import linkList from '../../data/helper/linkList';
+import linkSeriesData from '../../data/helper/linkSeriesData';
 import createDimensions from '../../data/helper/createDimensions';
 import CoordinateSystem from '../../core/CoordinateSystem';
-import createListFromArray from './createListFromArray';
+import createListFromArray from './createSeriesDataFromArray';
 import {
     OptionSourceDataOriginal, GraphEdgeItemObject, OptionDataValue,
     OptionDataItemObject
@@ -37,7 +37,7 @@ export default function createGraphFromNodeEdge(
     edges: OptionSourceDataOriginal<OptionDataValue, GraphEdgeItemObject<OptionDataValue>>,
     seriesModel: SeriesModel,
     directed: boolean,
-    beforeLink: (nodeData: List, edgeData: List) => void
+    beforeLink: (nodeData: SeriesData, edgeData: SeriesData) => void
 ): Graph {
     // ??? TODO
     // support dataset?
@@ -86,16 +86,16 @@ export default function createGraphFromNodeEdge(
         const dimensionNames = createDimensions(nodes, {
             coordDimensions: coordDimensions
         });
-        nodeData = new List(dimensionNames, seriesModel);
+        nodeData = new SeriesData(dimensionNames, seriesModel);
         nodeData.initData(nodes);
     }
 
-    const edgeData = new List(['value'], seriesModel);
+    const edgeData = new SeriesData(['value'], seriesModel);
     edgeData.initData(validEdges, linkNameList);
 
     beforeLink && beforeLink(nodeData, edgeData);
 
-    linkList({
+    linkSeriesData({
         mainData: nodeData,
         struct: graph,
         structAttr: 'graph',
diff --git a/src/chart/helper/createListFromArray.ts b/src/chart/helper/createSeriesDataFromArray.ts
similarity index 95%
rename from src/chart/helper/createListFromArray.ts
rename to src/chart/helper/createSeriesDataFromArray.ts
index a9adc2f..4746787 100644
--- a/src/chart/helper/createListFromArray.ts
+++ b/src/chart/helper/createSeriesDataFromArray.ts
@@ -18,7 +18,7 @@
 */
 
 import * as zrUtil from 'zrender/src/core/util';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import createDimensions from '../../data/helper/createDimensions';
 import {getDimensionTypeByAxis} from '../../data/helper/dimensionHelper';
 import {getDataItemValue} from '../../util/model';
@@ -31,13 +31,14 @@ import {
     SOURCE_FORMAT_ORIGINAL, DimensionDefinitionLoose, DimensionDefinition, OptionSourceData, EncodeDefaulter
 } from '../../util/types';
 import SeriesModel from '../../model/Series';
+import DataStorage from '../../data/DataStorage';
 
 function createListFromArray(source: Source | OptionSourceData, seriesModel: SeriesModel, opt?: {
     generateCoord?: string
     useEncodeDefaulter?: boolean | EncodeDefaulter
     // By default: auto. If `true`, create inverted indices for all ordinal dimension on coordSys.
     createInvertedIndices?: boolean
-}): List {
+}): SeriesData {
     opt = opt || {};
 
     if (!isSourceInstance(source)) {
@@ -110,12 +111,12 @@ function createListFromArray(source: Source | OptionSourceData, seriesModel: Ser
 
     const stackCalculationInfo = enableDataStack(seriesModel, dimInfoList);
 
-    const list = new List(dimInfoList, seriesModel);
+    const list = new SeriesData(dimInfoList, seriesModel);
 
     list.setCalculationInfo(stackCalculationInfo);
 
     const dimValueGetter = (firstCategoryDimIndex != null && isNeedCompleteOrdinalData(source))
-        ? function (this: List, itemOpt: any, dimName: string, dataIndex: number, dimIndex: number) {
+        ? function (this: DataStorage, itemOpt: any, dimName: string, dataIndex: number, dimIndex: number) {
             // Use dataIndex as ordinal value in categoryAxis
             return dimIndex === firstCategoryDimIndex
                 ? dataIndex
diff --git a/src/chart/helper/createListSimply.ts b/src/chart/helper/createSeriesDataSimply.ts
similarity index 89%
rename from src/chart/helper/createListSimply.ts
rename to src/chart/helper/createSeriesDataSimply.ts
index b38d3cd..3a8c403 100644
--- a/src/chart/helper/createListSimply.ts
+++ b/src/chart/helper/createSeriesDataSimply.ts
@@ -18,7 +18,7 @@
 */
 
 import createDimensions, {CreateDimensionsParams} from '../../data/helper/createDimensions';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import {extend, isArray} from 'zrender/src/core/util';
 import SeriesModel from '../../model/Series';
 
@@ -32,18 +32,18 @@ import SeriesModel from '../../model/Series';
  *     dimensionsCount: 5
  * });
  */
-export default function createListSimply(
+export default function createSeriesDataSimply(
     seriesModel: SeriesModel,
     opt: CreateDimensionsParams | CreateDimensionsParams['coordDimensions'],
     nameList?: string[]
-): List {
+): SeriesData {
     opt = isArray(opt) && {coordDimensions: opt} || extend({}, opt);
 
     const source = seriesModel.getSource();
 
     const dimensionsInfo = createDimensions(source, opt as CreateDimensionsParams);
 
-    const list = new List(dimensionsInfo, seriesModel);
+    const list = new SeriesData(dimensionsInfo, seriesModel);
     list.initData(source, nameList);
 
     return list;
diff --git a/src/chart/helper/labelHelper.ts b/src/chart/helper/labelHelper.ts
index 58a0b0d..8dc7905 100644
--- a/src/chart/helper/labelHelper.ts
+++ b/src/chart/helper/labelHelper.ts
@@ -19,7 +19,7 @@
 
 
 import {retrieveRawValue} from '../../data/helper/dataProvider';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import { InterpolatableValue } from '../../util/types';
 import { isArray } from 'zrender/src/core/util';
 
@@ -27,7 +27,7 @@ import { isArray } from 'zrender/src/core/util';
  * @return label string. Not null/undefined
  */
 export function getDefaultLabel(
-    data: List,
+    data: SeriesData,
     dataIndex: number
 ): string {
     const labelDims = data.mapDimensionsAll('defaultedLabel');
@@ -48,7 +48,7 @@ export function getDefaultLabel(
 }
 
 export function getDefaultInterpolatedLabel(
-    data: List,
+    data: SeriesData,
     interpolatedValue: InterpolatableValue
 ): string {
     const labelDims = data.mapDimensionsAll('defaultedLabel');
diff --git a/src/chart/helper/whiskerBoxCommon.ts b/src/chart/helper/whiskerBoxCommon.ts
index 679360d..0aa61a1 100644
--- a/src/chart/helper/whiskerBoxCommon.ts
+++ b/src/chart/helper/whiskerBoxCommon.ts
@@ -17,7 +17,7 @@
 * under the License.
 */
 
-import createListSimply from '../helper/createListSimply';
+import createSeriesDataSimply from './createSeriesDataSimply';
 import * as zrUtil from 'zrender/src/core/util';
 import {getDimensionTypeByAxis} from '../../data/helper/dimensionHelper';
 import {makeSeriesEncodeForAxisCoordSys} from '../../data/helper/sourceHelper';
@@ -25,7 +25,7 @@ import type { SeriesOption, SeriesOnCartesianOptionMixin, LayoutOrient } from '.
 import type GlobalModel from '../../model/Global';
 import type SeriesModel from '../../model/Series';
 import type CartesianAxisModel from '../../coord/cartesian/AxisModel';
-import type List from '../../data/List';
+import type SeriesData from '../../data/SeriesData';
 import type Axis2D from '../../coord/cartesian/Axis2D';
 import { CoordDimensionDefinition } from '../../data/helper/createDimensions';
 
@@ -55,7 +55,7 @@ class WhiskerBoxCommonMixin<Opts extends CommonOption> {
     /**
      * @override
      */
-    getInitialData(option: Opts, ecModel: GlobalModel): List {
+    getInitialData(option: Opts, ecModel: GlobalModel): SeriesData {
         // When both types of xAxis and yAxis are 'value', layout is
         // needed to be specified by user. Otherwise, layout can be
         // judged by which axis is category.
@@ -132,7 +132,7 @@ class WhiskerBoxCommonMixin<Opts extends CommonOption> {
             dimsDef: defaultValueDimensions.slice()
         }];
 
-        return createListSimply(
+        return createSeriesDataSimply(
             this,
             {
                 coordDimensions: coordDimensions,
diff --git a/src/chart/line/LineSeries.ts b/src/chart/line/LineSeries.ts
index 0b685eb..6360c89 100644
--- a/src/chart/line/LineSeries.ts
+++ b/src/chart/line/LineSeries.ts
@@ -17,7 +17,7 @@
 * under the License.
 */
 
-import createListFromArray from '../helper/createListFromArray';
+import createListFromArray from '../helper/createSeriesDataFromArray';
 import SeriesModel from '../../model/Series';
 import {
     SeriesOnCartesianOptionMixin,
@@ -36,7 +36,7 @@ import {
     CallbackDataParams,
     DefaultEmphasisFocus
 } from '../../util/types';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import type Cartesian2D from '../../coord/cartesian/Cartesian2D';
 import type Polar from '../../coord/polar/Polar';
 import {createSymbol, ECSymbol} from '../../util/symbol';
@@ -130,7 +130,7 @@ class LineSeriesModel extends SeriesModel<LineSeriesOption> {
 
     hasSymbolVisual = true;
 
-    getInitialData(option: LineSeriesOption): List {
+    getInitialData(option: LineSeriesOption): SeriesData {
         if (__DEV__) {
             const coordSys = option.coordinateSystem;
             if (coordSys !== 'polar' && coordSys !== 'cartesian2d') {
diff --git a/src/chart/line/LineView.ts b/src/chart/line/LineView.ts
index b40f589..c25bcc0 100644
--- a/src/chart/line/LineView.ts
+++ b/src/chart/line/LineView.ts
@@ -29,13 +29,13 @@ import {ECPolyline, ECPolygon} from './poly';
 import ChartView from '../../view/Chart';
 import {prepareDataCoordInfo, getStackedOnPoint} from './helper';
 import {createGridClipPath, createPolarClipPath} from '../helper/createClipPathFromCoordSys';
-import LineSeriesModel, { LineEndLabelOption, LineSeriesOption } from './LineSeries';
+import LineSeriesModel, { LineSeriesOption } from './LineSeries';
 import type GlobalModel from '../../model/Global';
 import type ExtensionAPI from '../../core/ExtensionAPI';
 // TODO
 import Cartesian2D from '../../coord/cartesian/Cartesian2D';
 import Polar from '../../coord/polar/Polar';
-import type List from '../../data/List';
+import type SeriesData from '../../data/SeriesData';
 import type {
     Payload,
     Dictionary,
@@ -121,7 +121,7 @@ function getSmooth(smooth: number | boolean) {
 
 function getStackedOnPoints(
     coordSys: Cartesian2D | Polar,
-    data: List,
+    data: SeriesData,
     dataCoordInfo: ReturnType<typeof prepareDataCoordInfo>
 ) {
     if (!dataCoordInfo.valueDim) {
@@ -187,7 +187,7 @@ function turnPointsIntoStep(
 }
 
 function getVisualGradient(
-    data: List,
+    data: SeriesData,
     coordSys: Cartesian2D | Polar
 ) {
     const visualMetaList = data.getVisual('visualMeta');
@@ -290,7 +290,7 @@ function getVisualGradient(
 
 function getIsIgnoreFunc(
     seriesModel: LineSeriesModel,
-    data: List,
+    data: SeriesData,
     coordSys: Cartesian2D
 ) {
     const showAllSymbol = seriesModel.get('showAllSymbol');
@@ -332,7 +332,7 @@ function getIsIgnoreFunc(
 
 function canShowAllSymbolForCategory(
     categoryAxis: Axis2D,
-    data: List
+    data: SeriesData
 ) {
     // In mose cases, line is monotonous on category axis, and the label size
     // is close with each other. So we check the symbol size and some of the
@@ -544,7 +544,7 @@ class LineView extends ChartView {
 
     _clipShapeForSymbol: CoordinateSystemClipArea;
 
-    _data: List;
+    _data: SeriesData;
 
     init() {
         const lineGroup = new graphic.Group();
@@ -962,7 +962,7 @@ class LineView extends ChartView {
     }
 
     _initSymbolLabelAnimation(
-        data: List,
+        data: SeriesData,
         coordSys: Polar | Cartesian2D,
         clipShape: PolarArea | Cartesian2DArea
     ) {
@@ -1113,7 +1113,7 @@ class LineView extends ChartView {
     _endLabelOnDuring(
         percent: number,
         clipRect: graphic.Rect,
-        data: List,
+        data: SeriesData,
         animationRecord: EndLabelAnimationRecord,
         valueAnimation: boolean,
         endLabelModel: Model<LabelOption>,
@@ -1200,7 +1200,7 @@ class LineView extends ChartView {
      */
     // FIXME Two value axis
     _doUpdateAnimation(
-        data: List,
+        data: SeriesData,
         stackedOnPoints: ArrayLike<number>,
         coordSys: Cartesian2D | Polar,
         api: ExtensionAPI,
diff --git a/src/chart/line/helper.ts b/src/chart/line/helper.ts
index 200824f..e58d2e7 100644
--- a/src/chart/line/helper.ts
+++ b/src/chart/line/helper.ts
@@ -21,7 +21,7 @@ import {isDimensionStacked} from '../../data/helper/dataStackHelper';
 import {map} from 'zrender/src/core/util';
 import type Polar from '../../coord/polar/Polar';
 import type Cartesian2D from '../../coord/cartesian/Cartesian2D';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import Axis from '../../coord/Axis';
 import type { LineSeriesOption } from './LineSeries';
 
@@ -39,7 +39,7 @@ interface CoordInfo {
 
 export function prepareDataCoordInfo(
     coordSys: Cartesian2D | Polar,
-    data: List,
+    data: SeriesData,
     valueOrigin?: LineSeriesOption['areaStyle']['origin']
 ): CoordInfo {
     const baseAxis = coordSys.getBaseAxis();
@@ -109,7 +109,7 @@ function getValueStart(valueAxis: Axis, valueOrigin: LineSeriesOption['areaStyle
 export function getStackedOnPoint(
     dataCoordInfo: CoordInfo,
     coordSys: Cartesian2D | Polar,
-    data: List,
+    data: SeriesData,
     idx: number
 ) {
     let value = NaN;
diff --git a/src/chart/line/lineAnimationDiff.ts b/src/chart/line/lineAnimationDiff.ts
index b491665..60a93a1 100644
--- a/src/chart/line/lineAnimationDiff.ts
+++ b/src/chart/line/lineAnimationDiff.ts
@@ -18,7 +18,7 @@
 */
 
 import {prepareDataCoordInfo, getStackedOnPoint} from './helper';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import type Cartesian2D from '../../coord/cartesian/Cartesian2D';
 import type Polar from '../../coord/polar/Polar';
 import { LineSeriesOption } from './LineSeries';
@@ -30,7 +30,7 @@ interface DiffItem {
     idx1?: number
 }
 
-function diffData(oldData: List, newData: List) {
+function diffData(oldData: SeriesData, newData: SeriesData) {
     const diffResult: DiffItem[] = [];
 
     newData.diff(oldData)
@@ -49,7 +49,7 @@ function diffData(oldData: List, newData: List) {
 }
 
 export default function lineAnimationDiff(
-    oldData: List, newData: List,
+    oldData: SeriesData, newData: SeriesData,
     oldStackedOnPoints: ArrayLike<number>, newStackedOnPoints: ArrayLike<number>,
     oldCoordSys: Cartesian2D | Polar, newCoordSys: Cartesian2D | Polar,
     oldValueOrigin: LineSeriesOption['areaStyle']['origin'],
diff --git a/src/chart/lines/LinesSeries.ts b/src/chart/lines/LinesSeries.ts
index 98cb2dd..f6ec965 100644
--- a/src/chart/lines/LinesSeries.ts
+++ b/src/chart/lines/LinesSeries.ts
@@ -20,7 +20,7 @@
 /* global Uint32Array, Float64Array, Float32Array */
 
 import SeriesModel from '../../model/Series';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import { concatArray, mergeAll, map } from 'zrender/src/core/util';
 import CoordinateSystem from '../../core/CoordinateSystem';
 import {
@@ -297,7 +297,7 @@ class LinesSeriesModel extends SeriesModel<LinesSeriesOption> {
             }
         }
 
-        const lineData = new List(['value'], this);
+        const lineData = new SeriesData(['value'], this);
         lineData.hasItemOption = false;
 
         lineData.initData(option.data, [], function (dataItem, dimName, dataIndex, dimIndex) {
diff --git a/src/chart/lines/LinesView.ts b/src/chart/lines/LinesView.ts
index ad1089e..6c4479d 100644
--- a/src/chart/lines/LinesView.ts
+++ b/src/chart/lines/LinesView.ts
@@ -31,7 +31,7 @@ import GlobalModel from '../../model/Global';
 import ExtensionAPI from '../../core/ExtensionAPI';
 import CanvasPainter from 'zrender/src/canvas/Painter';
 import { StageHandlerProgressParams, StageHandlerProgressExecutor } from '../../util/types';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import type Polar from '../../coord/polar/Polar';
 import type Cartesian2D from '../../coord/cartesian/Cartesian2D';
 
@@ -90,7 +90,7 @@ class LinesView extends ChartView {
             }
         }
 
-        lineDraw.updateData(data as List);
+        lineDraw.updateData(data as SeriesData);
 
         const clipPath = seriesModel.get('clip', true) && createClipPath(
             (seriesModel.coordinateSystem as Polar | Cartesian2D), false, seriesModel
@@ -156,7 +156,7 @@ class LinesView extends ChartView {
         }
     }
 
-    _updateLineDraw(data: List, seriesModel: LinesSeriesModel) {
+    _updateLineDraw(data: SeriesData, seriesModel: LinesSeriesModel) {
         let lineDraw = this._lineDraw;
         const hasEffect = this._showEffect(seriesModel);
         const isPolyline = !!seriesModel.get('polyline');
diff --git a/src/chart/lines/linesVisual.ts b/src/chart/lines/linesVisual.ts
index 1e4c931..0e18b69 100644
--- a/src/chart/lines/linesVisual.ts
+++ b/src/chart/lines/linesVisual.ts
@@ -18,7 +18,7 @@
 */
 
 import { StageHandler } from '../../util/types';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import LinesSeriesModel, { LinesDataItemOption } from './LinesSeries';
 import Model from '../../model/Model';
 import { LineDataVisual } from '../../visual/commonVisualTypes';
@@ -37,7 +37,7 @@ const linesVisual: StageHandler = {
     reset(seriesModel: LinesSeriesModel) {
         const symbolType = normalize(seriesModel.get('symbol'));
         const symbolSize = normalize(seriesModel.get('symbolSize'));
-        const data = seriesModel.getData() as List<LinesSeriesModel, LineDataVisual>;
+        const data = seriesModel.getData() as SeriesData<LinesSeriesModel, LineDataVisual>;
 
         data.setVisual('fromSymbol', symbolType && symbolType[0]);
         data.setVisual('toSymbol', symbolType && symbolType[1]);
@@ -45,7 +45,7 @@ const linesVisual: StageHandler = {
         data.setVisual('toSymbolSize', symbolSize && symbolSize[1]);
 
         function dataEach(
-            data: List<LinesSeriesModel, LineDataVisual>,
+            data: SeriesData<LinesSeriesModel, LineDataVisual>,
             idx: number
         ): void {
             const itemModel = data.getItemModel(idx) as Model<LinesDataItemOption>;
diff --git a/src/chart/map/MapSeries.ts b/src/chart/map/MapSeries.ts
index f0049de..582b55c 100644
--- a/src/chart/map/MapSeries.ts
+++ b/src/chart/map/MapSeries.ts
@@ -19,7 +19,7 @@
 
 
 import * as zrUtil from 'zrender/src/core/util';
-import createListSimply from '../helper/createListSimply';
+import createSeriesDataSimply from '../helper/createSeriesDataSimply';
 import SeriesModel from '../../model/Series';
 import geoSourceManager from '../../coord/geo/geoSourceManager';
 import {makeSeriesEncodeForNameBased} from '../../data/helper/sourceHelper';
@@ -36,7 +36,7 @@ import {
 } from '../../util/types';
 import { Dictionary } from 'zrender/src/core/types';
 import GeoModel, { GeoCommonOptionMixin, GeoItemStyleOption } from '../../coord/geo/GeoModel';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import Model from '../../model/Model';
 import Geo from '../../coord/geo/Geo';
 import { createTooltipMarkup } from '../../component/tooltip/tooltipMarkup';
@@ -101,7 +101,7 @@ class MapSeries extends SeriesModel<MapSeriesOption> {
 
     // -----------------
     // Injected outside
-    originalData: List;
+    originalData: SeriesData;
     mainSeries: MapSeries;
     // Only first map series of same mapType will drawMap.
     needsDrawMap: boolean = false;
@@ -109,8 +109,8 @@ class MapSeries extends SeriesModel<MapSeriesOption> {
     seriesGroup: MapSeries[] = [];
 
 
-    getInitialData(this: MapSeries, option: MapSeriesOption): List {
-        const data = createListSimply(this, {
+    getInitialData(this: MapSeries, option: MapSeriesOption): SeriesData {
+        const data = createSeriesDataSimply(this, {
             coordDimensions: ['value'],
             encodeDefaulter: zrUtil.curry(makeSeriesEncodeForNameBased, this)
         });
@@ -133,7 +133,10 @@ class MapSeries extends SeriesModel<MapSeriesOption> {
         // Complete data with missing regions. The consequent processes (like visual
         // map and render) can not be performed without a "full data". For example,
         // find `dataIndex` by name.
-        data.appendValues([], toAppendNames);
+        data.appendData(zrUtil.map(toAppendNames, name => ({
+            name,
+            value: NaN
+        })));
 
         return data;
     }
diff --git a/src/chart/map/mapDataStatistic.ts b/src/chart/map/mapDataStatistic.ts
index 0df4040..504f548 100644
--- a/src/chart/map/mapDataStatistic.ts
+++ b/src/chart/map/mapDataStatistic.ts
@@ -19,12 +19,12 @@
 
 
 import * as zrUtil from 'zrender/src/core/util';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import MapSeries, { MapValueCalculationType } from './MapSeries';
 import GlobalModel from '../../model/Global';
 
 // FIXME 公用?
-function dataStatistics(datas: List[], statisticType: MapValueCalculationType): List {
+function dataStatistics(datas: SeriesData[], statisticType: MapValueCalculationType): SeriesData {
     const dataNameMap = {} as {[mapKey: string]: number[]};
 
     zrUtil.each(datas, function (data) {
diff --git a/src/chart/parallel/ParallelSeries.ts b/src/chart/parallel/ParallelSeries.ts
index aaca025..20140c9 100644
--- a/src/chart/parallel/ParallelSeries.ts
+++ b/src/chart/parallel/ParallelSeries.ts
@@ -20,7 +20,7 @@
 
 import {each, bind} from 'zrender/src/core/util';
 import SeriesModel from '../../model/Series';
-import createListFromArray from '../helper/createListFromArray';
+import createListFromArray from '../helper/createSeriesDataFromArray';
 import {
     SeriesOption,
     SeriesEncodeOptionMixin,
@@ -35,7 +35,7 @@ import {
     OptionEncode
  } from '../../util/types';
 import GlobalModel from '../../model/Global';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import { ParallelActiveState, ParallelAxisOption } from '../../coord/parallel/AxisModel';
 import Parallel from '../../coord/parallel/Parallel';
 import ParallelModel from '../../coord/parallel/ParallelModel';
@@ -91,7 +91,7 @@ class ParallelSeriesModel extends SeriesModel<ParallelSeriesOption> {
     coordinateSystem: Parallel;
 
 
-    getInitialData(this: ParallelSeriesModel, option: ParallelSeriesOption, ecModel: GlobalModel): List {
+    getInitialData(this: ParallelSeriesModel, option: ParallelSeriesOption, ecModel: GlobalModel): SeriesData {
         return createListFromArray(this.getSource(), this, {
             useEncodeDefaulter: bind(makeDefaultEncode, null, this)
         });
diff --git a/src/chart/parallel/ParallelView.ts b/src/chart/parallel/ParallelView.ts
index c0b5714..1704b09 100644
--- a/src/chart/parallel/ParallelView.ts
+++ b/src/chart/parallel/ParallelView.ts
@@ -20,7 +20,7 @@
 import * as graphic from '../../util/graphic';
 import { setStatesStylesFromModel, enableHoverEmphasis } from '../../util/states';
 import ChartView from '../../view/Chart';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import ParallelSeriesModel, { ParallelSeriesDataItemOption } from './ParallelSeries';
 import GlobalModel from '../../model/Global';
 import ExtensionAPI from '../../core/ExtensionAPI';
@@ -42,7 +42,7 @@ class ParallelView extends ChartView {
 
     private _dataGroup = new graphic.Group();
 
-    private _data: List;
+    private _data: SeriesData;
 
     private _initialized = false;
 
@@ -160,7 +160,7 @@ function createGridClipShape(coordSys: Parallel, seriesModel: ParallelSeriesMode
     return rectEl;
 }
 
-function createLinePoints(data: List, dataIndex: number, dimensions: string[], coordSys: Parallel) {
+function createLinePoints(data: SeriesData, dataIndex: number, dimensions: string[], coordSys: Parallel) {
     const points = [];
     for (let i = 0; i < dimensions.length; i++) {
         const dimName = dimensions[i];
@@ -172,7 +172,7 @@ function createLinePoints(data: List, dataIndex: number, dimensions: string[], c
     return points;
 }
 
-function addEl(data: List, dataGroup: graphic.Group, dataIndex: number, dimensions: string[], coordSys: Parallel) {
+function addEl(data: SeriesData, dataGroup: graphic.Group, dataIndex: number, dimensions: string[], coordSys: Parallel) {
     const points = createLinePoints(data, dataIndex, dimensions, coordSys);
     const line = new graphic.Polyline({
         shape: {points: points},
@@ -195,7 +195,7 @@ function makeSeriesScope(seriesModel: ParallelSeriesModel): ParallelDrawSeriesSc
 
 function updateElCommon(
     el: graphic.Polyline,
-    data: List,
+    data: SeriesData,
     dataIndex: number,
     seriesScope: ParallelDrawSeriesScope
 ) {
diff --git a/src/chart/pie/PieSeries.ts b/src/chart/pie/PieSeries.ts
index 0391923..85784e7 100644
--- a/src/chart/pie/PieSeries.ts
+++ b/src/chart/pie/PieSeries.ts
@@ -17,7 +17,7 @@
 * under the License.
 */
 
-import createListSimply from '../helper/createListSimply';
+import createSeriesDataSimply from '../helper/createSeriesDataSimply';
 import * as zrUtil from 'zrender/src/core/util';
 import * as modelUtil from '../../util/model';
 import { getPercentWithPrecision } from '../../util/number';
@@ -38,7 +38,7 @@ import {
     SeriesLabelOption,
     DefaultEmphasisFocus
 } from '../../util/types';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 
 interface PieItemStyleOption extends ItemStyleOption {
     // can be 10
@@ -156,8 +156,8 @@ class PieSeriesModel extends SeriesModel<PieSeriesOption> {
     /**
      * @overwrite
      */
-    getInitialData(this: PieSeriesModel): List {
-        return createListSimply(this, {
+    getInitialData(this: PieSeriesModel): SeriesData {
+        return createSeriesDataSimply(this, {
             coordDimensions: ['value'],
             encodeDefaulter: zrUtil.curry(makeSeriesEncodeForNameBased, this)
         });
diff --git a/src/chart/pie/PieView.ts b/src/chart/pie/PieView.ts
index 043654d..66c07e8 100644
--- a/src/chart/pie/PieView.ts
+++ b/src/chart/pie/PieView.ts
@@ -26,7 +26,7 @@ import ChartView from '../../view/Chart';
 import GlobalModel from '../../model/Global';
 import ExtensionAPI from '../../core/ExtensionAPI';
 import { Payload, ColorString } from '../../util/types';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import PieSeriesModel, {PieDataItemOption} from './PieSeries';
 import labelLayout from './labelLayout';
 import { setLabelLineStyle, getLabelLineStatesModels } from '../../label/labelGuideHelper';
@@ -40,7 +40,7 @@ import { getBasicPieLayout } from './pieLayout';
  */
 class PiePiece extends graphic.Sector {
 
-    constructor(data: List, idx: number, startAngle: number) {
+    constructor(data: SeriesData, idx: number, startAngle: number) {
         super();
 
         this.z2 = 2;
@@ -52,7 +52,7 @@ class PiePiece extends graphic.Sector {
         this.updateData(data, idx, startAngle, true);
     }
 
-    updateData(data: List, idx: number, startAngle?: number, firstCreate?: boolean): void {
+    updateData(data: SeriesData, idx: number, startAngle?: number, firstCreate?: boolean): void {
         const sector = this;
 
         const seriesModel = data.hostModel as PieSeriesModel;
@@ -158,7 +158,7 @@ class PiePiece extends graphic.Sector {
         enableHoverEmphasis(this, emphasisModel.get('focus'), emphasisModel.get('blurScope'));
     }
 
-    private _updateLabel(seriesModel: PieSeriesModel, data: List, idx: number): void {
+    private _updateLabel(seriesModel: PieSeriesModel, data: SeriesData, idx: number): void {
         const sector = this;
         const itemModel = data.getItemModel<PieDataItemOption>(idx);
         const labelLineModel = itemModel.getModel('labelLine');
@@ -223,7 +223,7 @@ class PieView extends ChartView {
     ignoreLabelLineUpdate = true;
 
     private _sectorGroup: graphic.Group;
-    private _data: List;
+    private _data: SeriesData;
     private _emptyCircleSector: graphic.Sector;
 
     init(): void {
diff --git a/src/chart/radar/RadarSeries.ts b/src/chart/radar/RadarSeries.ts
index f768195..0f57ef6 100644
--- a/src/chart/radar/RadarSeries.ts
+++ b/src/chart/radar/RadarSeries.ts
@@ -18,7 +18,7 @@
 */
 
 import SeriesModel from '../../model/Series';
-import createListSimply from '../helper/createListSimply';
+import createSeriesDataSimply from '../helper/createSeriesDataSimply';
 import * as zrUtil from 'zrender/src/core/util';
 import LegendVisualProvider from '../../visual/LegendVisualProvider';
 import {
@@ -35,7 +35,7 @@ import {
     CallbackDataParams
 } from '../../util/types';
 import GlobalModel from '../../model/Global';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import Radar from '../../coord/radar/Radar';
 import {
     createTooltipMarkup, retrieveVisualColorForTooltipMarker
@@ -90,8 +90,8 @@ class RadarSeriesModel extends SeriesModel<RadarSeriesOption> {
 
     }
 
-    getInitialData(option: RadarSeriesOption, ecModel: GlobalModel): List {
-        return createListSimply(this, {
+    getInitialData(option: RadarSeriesOption, ecModel: GlobalModel): SeriesData {
+        return createSeriesDataSimply(this, {
             generateCoord: 'indicator_',
             generateCoordCount: Infinity
         });
diff --git a/src/chart/radar/RadarView.ts b/src/chart/radar/RadarView.ts
index c4e28d0..3eb6da2 100644
--- a/src/chart/radar/RadarView.ts
+++ b/src/chart/radar/RadarView.ts
@@ -24,7 +24,7 @@ import * as symbolUtil from '../../util/symbol';
 import ChartView from '../../view/Chart';
 import RadarSeriesModel, { RadarSeriesDataItemOption } from './RadarSeries';
 import ExtensionAPI from '../../core/ExtensionAPI';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import { ColorString } from '../../util/types';
 import GlobalModel from '../../model/Global';
 import { VectorArray } from 'zrender/src/core/vector';
@@ -40,7 +40,7 @@ class RadarView extends ChartView {
     static type = 'radar';
     type = RadarView.type;
 
-    private _data: List<RadarSeriesModel>;
+    private _data: SeriesData<RadarSeriesModel>;
 
     render(seriesModel: RadarSeriesModel, ecModel: GlobalModel, api: ExtensionAPI) {
         const polar = seriesModel.coordinateSystem;
@@ -49,7 +49,7 @@ class RadarView extends ChartView {
         const data = seriesModel.getData();
         const oldData = this._data;
 
-        function createSymbol(data: List<RadarSeriesModel>, idx: number) {
+        function createSymbol(data: SeriesData<RadarSeriesModel>, idx: number) {
             const symbolType = data.getItemVisual(idx, 'symbol') as string || 'circle';
             if (symbolType === 'none') {
                 return;
@@ -77,7 +77,7 @@ class RadarView extends ChartView {
             oldPoints: VectorArray[],
             newPoints: VectorArray[],
             symbolGroup: graphic.Group,
-            data: List<RadarSeriesModel>,
+            data: SeriesData<RadarSeriesModel>,
             idx: number,
             isInit?: boolean
         ) {
diff --git a/src/chart/sankey/SankeySeries.ts b/src/chart/sankey/SankeySeries.ts
index a915ef6..3261ad1 100644
--- a/src/chart/sankey/SankeySeries.ts
+++ b/src/chart/sankey/SankeySeries.ts
@@ -36,7 +36,7 @@ import {
     DefaultEmphasisFocus
 } from '../../util/types';
 import GlobalModel from '../../model/Global';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import { LayoutRect } from '../../util/layout';
 import { createTooltipMarkup } from '../../component/tooltip/tooltipMarkup';
 
@@ -150,7 +150,7 @@ class SankeySeriesModel extends SeriesModel<SankeySeriesOption> {
      * Init a graph data structure from data in option series
      *
      * @param  {Object} option  the object used to config echarts view
-     * @return {module:echarts/data/List} storage initial data
+     * @return {module:echarts/data/SeriesData} storage initial data
      */
     getInitialData(option: SankeySeriesOption, ecModel: GlobalModel) {
         const links = option.edges || option.links;
@@ -173,7 +173,7 @@ class SankeySeriesModel extends SeriesModel<SankeySeriesOption> {
             const graph = createGraphFromNodeEdge(nodes, links, this, true, beforeLink);
             return graph.data;
         }
-        function beforeLink(nodeData: List, edgeData: List) {
+        function beforeLink(nodeData: SeriesData, edgeData: SeriesData) {
             nodeData.wrapMethod('getItemModel', function (model: Model, idx: number) {
                 const seriesModel = model.parentModel as SankeySeriesModel;
                 const layout = seriesModel.getData().getItemLayout(idx);
diff --git a/src/chart/sankey/SankeyView.ts b/src/chart/sankey/SankeyView.ts
index 480aeb5..abd589c 100644
--- a/src/chart/sankey/SankeyView.ts
+++ b/src/chart/sankey/SankeyView.ts
@@ -25,7 +25,7 @@ import SankeySeriesModel, { SankeyEdgeItemOption, SankeyNodeItemOption } from '.
 import ChartView from '../../view/Chart';
 import GlobalModel from '../../model/Global';
 import ExtensionAPI from '../../core/ExtensionAPI';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import { RectLike } from 'zrender/src/core/BoundingRect';
 import { setLabelStyle, getLabelStatesModels } from '../../label/labelStyle';
 import { getECData } from '../../util/innerStore';
@@ -107,7 +107,7 @@ class SankeyView extends ChartView {
 
     private _focusAdjacencyDisabled = false;
 
-    private _data: List;
+    private _data: SeriesData;
 
     render(seriesModel: SankeySeriesModel, ecModel: GlobalModel, api: ExtensionAPI) {
         const sankeyView = this;
diff --git a/src/chart/scatter/ScatterSeries.ts b/src/chart/scatter/ScatterSeries.ts
index a217c5a..6248ecc 100644
--- a/src/chart/scatter/ScatterSeries.ts
+++ b/src/chart/scatter/ScatterSeries.ts
@@ -17,7 +17,7 @@
 * under the License.
 */
 
-import createListFromArray from '../helper/createListFromArray';
+import createListFromArray from '../helper/createSeriesDataFromArray';
 import SeriesModel from '../../model/Series';
 import {
     SeriesOption,
@@ -39,7 +39,7 @@ import {
     DefaultEmphasisFocus
 } from '../../util/types';
 import GlobalModel from '../../model/Global';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import { BrushCommonSelectorsForSeries } from '../../component/brush/selector';
 
 interface ScatterStateOption {
@@ -84,7 +84,7 @@ class ScatterSeriesModel extends SeriesModel<ScatterSeriesOption> {
 
     hasSymbolVisual = true;
 
-    getInitialData(option: ScatterSeriesOption, ecModel: GlobalModel): List {
+    getInitialData(option: ScatterSeriesOption, ecModel: GlobalModel): SeriesData {
         return createListFromArray(this.getSource(), this, {
             useEncodeDefaulter: true
         });
@@ -109,7 +109,7 @@ class ScatterSeriesModel extends SeriesModel<ScatterSeriesOption> {
         return progressiveThreshold;
     }
 
-    brushSelector(dataIndex: number, data: List, selectors: BrushCommonSelectorsForSeries): boolean {
+    brushSelector(dataIndex: number, data: SeriesData, selectors: BrushCommonSelectorsForSeries): boolean {
         return selectors.point(data.getItemLayout(dataIndex));
     }
 
diff --git a/src/chart/scatter/ScatterView.ts b/src/chart/scatter/ScatterView.ts
index 4a7fbb0..1bf6e3c 100644
--- a/src/chart/scatter/ScatterView.ts
+++ b/src/chart/scatter/ScatterView.ts
@@ -25,7 +25,7 @@ import ChartView from '../../view/Chart';
 import ScatterSeriesModel from './ScatterSeries';
 import GlobalModel from '../../model/Global';
 import ExtensionAPI from '../../core/ExtensionAPI';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import { TaskProgressParams } from '../../core/task';
 import type { StageHandlerProgressExecutor } from '../../util/types';
 
@@ -99,7 +99,7 @@ class ScatterView extends ChartView {
         return seriesModel.get('clip', true) ? clipArea : null;
     }
 
-    _updateSymbolDraw(data: List, seriesModel: ScatterSeriesModel) {
+    _updateSymbolDraw(data: SeriesData, seriesModel: ScatterSeriesModel) {
         let symbolDraw = this._symbolDraw;
         const pipelineContext = seriesModel.pipelineContext;
         const isLargeDraw = pipelineContext.large;
diff --git a/src/chart/sunburst/SunburstSeries.ts b/src/chart/sunburst/SunburstSeries.ts
index a1e38bf..9082acc 100644
--- a/src/chart/sunburst/SunburstSeries.ts
+++ b/src/chart/sunburst/SunburstSeries.ts
@@ -33,7 +33,7 @@ import {
     DefaultEmphasisFocus
 } from '../../util/types';
 import GlobalModel from '../../model/Global';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import Model from '../../model/Model';
 import enableAriaDecalForTree from '../helper/enableAriaDecalForTree';
 
@@ -164,7 +164,7 @@ class SunburstSeriesModel extends SeriesModel<SunburstSeriesOption> {
         // to choose mappings approach among old shapes and new shapes.
         const tree = Tree.createTree(root, this, beforeLink);
 
-        function beforeLink(nodeData: List) {
+        function beforeLink(nodeData: SeriesData) {
             nodeData.wrapMethod('getItemModel', function (model, idx) {
                 const node = tree.getNodeByDataIndex(idx);
                 const levelModel = levelModels[node.depth];
diff --git a/src/chart/themeRiver/ThemeRiverSeries.ts b/src/chart/themeRiver/ThemeRiverSeries.ts
index 6eb4f72..724526d 100644
--- a/src/chart/themeRiver/ThemeRiverSeries.ts
+++ b/src/chart/themeRiver/ThemeRiverSeries.ts
@@ -20,7 +20,7 @@
 import SeriesModel from '../../model/Series';
 import createDimensions from '../../data/helper/createDimensions';
 import {getDimensionTypeByAxis} from '../../data/helper/dimensionHelper';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import * as zrUtil from 'zrender/src/core/util';
 import {groupData, SINGLE_REFERRING} from '../../util/model';
 import LegendVisualProvider from '../../visual/LegendVisualProvider';
@@ -154,7 +154,7 @@ class ThemeRiverSeriesModel extends SeriesModel<ThemeRiverSeriesOption> {
      * @param  option  the initial option that user gived
      * @param  ecModel  the model object for themeRiver option
      */
-    getInitialData(option: ThemeRiverSeriesOption, ecModel: GlobalModel): List {
+    getInitialData(option: ThemeRiverSeriesOption, ecModel: GlobalModel): SeriesData {
 
         const singleAxisModel = this.getReferringComponents('singleAxis', SINGLE_REFERRING).models[0];
 
@@ -202,7 +202,7 @@ class ThemeRiverSeriesModel extends SeriesModel<ThemeRiverSeriesOption> {
             }
         });
 
-        const list = new List(dimensionsInfo, this);
+        const list = new SeriesData(dimensionsInfo, this);
         list.initData(data);
 
         return list;
diff --git a/src/chart/themeRiver/themeRiverLayout.ts b/src/chart/themeRiver/themeRiverLayout.ts
index 6de3e65..ec51217 100644
--- a/src/chart/themeRiver/themeRiverLayout.ts
+++ b/src/chart/themeRiver/themeRiverLayout.ts
@@ -23,7 +23,7 @@ import GlobalModel from '../../model/Global';
 import ExtensionAPI from '../../core/ExtensionAPI';
 import ThemeRiverSeriesModel, { ThemeRiverSeriesOption } from './ThemeRiverSeries';
 import { RectLike } from 'zrender/src/core/BoundingRect';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 
 export interface ThemeRiverLayoutInfo {
     rect: RectLike
@@ -75,7 +75,7 @@ export default function themeRiverLayout(ecModel: GlobalModel, api: ExtensionAPI
  * @param seriesModel  the model object of themeRiver series
  * @param height  value used to compute every series height
  */
-function doThemeRiverLayout(data: List<ThemeRiverSeriesModel>, seriesModel: ThemeRiverSeriesModel, height: number) {
+function doThemeRiverLayout(data: SeriesData<ThemeRiverSeriesModel>, seriesModel: ThemeRiverSeriesModel, height: number) {
     if (!data.count()) {
         return;
     }
diff --git a/src/chart/tree/TreeSeries.ts b/src/chart/tree/TreeSeries.ts
index 2c197e4..4f1e1b1 100644
--- a/src/chart/tree/TreeSeries.ts
+++ b/src/chart/tree/TreeSeries.ts
@@ -33,7 +33,7 @@ import {
     CallbackDataParams,
     DefaultEmphasisFocus
 } from '../../util/types';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import View from '../../coord/View';
 import { LayoutRect } from '../../util/layout';
 import Model from '../../model/Model';
@@ -144,7 +144,7 @@ class TreeSeriesModel extends SeriesModel<TreeSeriesOption> {
      * @param  option  the object used to config echarts view
      * @return storage initial data
      */
-    getInitialData(option: TreeSeriesOption): List {
+    getInitialData(option: TreeSeriesOption): SeriesData {
 
         //create an virtual root
         const root: TreeSeriesNodeItemOption = {
@@ -157,7 +157,7 @@ class TreeSeriesModel extends SeriesModel<TreeSeriesOption> {
 
         const tree = Tree.createTree(root, this, beforeLink);
 
-        function beforeLink(nodeData: List) {
+        function beforeLink(nodeData: SeriesData) {
             nodeData.wrapMethod('getItemModel', function (model, idx) {
                 const node = tree.getNodeByDataIndex(idx);
                 if (!(node && node.children.length && node.isExpand)) {
diff --git a/src/chart/tree/TreeView.ts b/src/chart/tree/TreeView.ts
index bcc8be8..e86d0b2 100644
--- a/src/chart/tree/TreeView.ts
+++ b/src/chart/tree/TreeView.ts
@@ -34,7 +34,7 @@ import Path, { PathProps, PathStyleProps } from 'zrender/src/graphic/Path';
 import GlobalModel from '../../model/Global';
 import ExtensionAPI from '../../core/ExtensionAPI';
 import { TreeNode } from '../../data/Tree';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import { setStatesStylesFromModel, setStatesFlag, setDefaultStateProxy, HOVER_STATE_BLUR } from '../../util/states';
 import { ECElement } from '../../util/types';
 
@@ -134,7 +134,7 @@ class TreeView extends ChartView {
     private _controller: RoamController;
     private _controllerHost: RoamControllerHost;
 
-    private _data: List<TreeSeriesModel>;
+    private _data: SeriesData<TreeSeriesModel>;
 
     private _nodeScaleRatio: number;
     private _min: number[];
@@ -358,7 +358,7 @@ class TreeView extends ChartView {
 
 }
 
-function symbolNeedsDraw(data: List, dataIndex: number) {
+function symbolNeedsDraw(data: SeriesData, dataIndex: number) {
     const layout = data.getItemLayout(dataIndex);
 
     return layout
@@ -367,7 +367,7 @@ function symbolNeedsDraw(data: List, dataIndex: number) {
 
 
 function updateNode(
-    data: List,
+    data: SeriesData,
     dataIndex: number,
     symbolEl: TreeSymbol,
     group: graphic.Group,
@@ -596,7 +596,7 @@ function drawEdge(
 }
 
 function removeNode(
-    data: List,
+    data: SeriesData,
     dataIndex: number,
     symbolEl: TreeSymbol,
     group: graphic.Group,
diff --git a/src/chart/treemap/TreemapSeries.ts b/src/chart/treemap/TreemapSeries.ts
index e84fe85..cde6a0c 100644
--- a/src/chart/treemap/TreemapSeries.ts
+++ b/src/chart/treemap/TreemapSeries.ts
@@ -40,7 +40,7 @@ import {
 } from '../../util/types';
 import GlobalModel from '../../model/Global';
 import { LayoutRect } from '../../util/layout';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import { normalizeToArray } from '../../util/model';
 import { createTooltipMarkup } from '../../component/tooltip/tooltipMarkup';
 import enableAriaDecalForTree from '../helper/enableAriaDecalForTree';
@@ -367,7 +367,7 @@ class TreemapSeriesModel extends SeriesModel<TreemapSeriesOption> {
         // to choose mappings approach among old shapes and new shapes.
         const tree = Tree.createTree(root, this, beforeLink);
 
-        function beforeLink(nodeData: List) {
+        function beforeLink(nodeData: SeriesData) {
             nodeData.wrapMethod('getItemModel', function (model, idx) {
                 const node = tree.getNodeByDataIndex(idx);
                 const levelModel = node ? levelModels[node.depth] : null;
diff --git a/src/component/helper/MapDraw.ts b/src/component/helper/MapDraw.ts
index 31ff42d..56bf2c5 100644
--- a/src/component/helper/MapDraw.ts
+++ b/src/component/helper/MapDraw.ts
@@ -46,7 +46,7 @@ import { ViewCoordSysTransformInfoPart } from '../../coord/View';
 import { GeoSVGGraphicRecord, GeoSVGResource } from '../../coord/geo/GeoSVGResource';
 import Displayable from 'zrender/src/graphic/Displayable';
 import Element from 'zrender/src/Element';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import { GeoJSONRegion } from '../../coord/geo/Region';
 import { SVGNodeTagLower } from 'zrender/src/tool/parseSVG';
 import { makeInner } from '../../util/model';
@@ -62,7 +62,7 @@ interface ViewBuildContext {
     api: ExtensionAPI;
     geo: Geo;
     mapOrGeoModel: GeoModel | MapSeries;
-    data: List;
+    data: SeriesData;
     isVisualEncodedByVisualMap: boolean;
     isGeo: boolean;
     transformInfoRaw: ViewCoordSysTransformInfoPart;
diff --git a/src/component/marker/MarkAreaView.ts b/src/component/marker/MarkAreaView.ts
index bb20679..e4faafd 100644
--- a/src/component/marker/MarkAreaView.ts
+++ b/src/component/marker/MarkAreaView.ts
@@ -20,7 +20,7 @@
 // TODO Optimize on polar
 
 import * as colorUtil from 'zrender/src/tool/color';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import * as numberUtil from '../../util/number';
 import * as graphic from '../../util/graphic';
 import { enableHoverEmphasis, setStatesStylesFromModel } from '../../util/states';
@@ -47,7 +47,7 @@ interface MarkAreaDrawGroup {
 }
 
 const inner = makeInner<{
-    data: List<MarkAreaModel>
+    data: SeriesData<MarkAreaModel>
 }, MarkAreaDrawGroup>();
 
 // Merge two ends option into one.
@@ -137,7 +137,7 @@ function markAreaFilter(coordSys: CoordinateSystem, item: MarkAreaMergedItemOpti
 
 // dims can be ['x0', 'y0'], ['x1', 'y1'], ['x0', 'y1'], ['x1', 'y0']
 function getSingleMarkerEndPoint(
-    data: List<MarkAreaModel>,
+    data: SeriesData<MarkAreaModel>,
     idx: number,
     dims: typeof dimPermutations[number],
     seriesModel: SeriesModel,
@@ -363,7 +363,7 @@ function createList(
 ) {
 
     let coordDimsInfos: DataDimensionInfo[];
-    let areaData: List<MarkAreaModel>;
+    let areaData: SeriesData<MarkAreaModel>;
     const dims = ['x0', 'y0', 'x1', 'y1'];
     if (coordSys) {
         coordDimsInfos = map(coordSys && coordSys.dimensions, function (coordDim) {
@@ -376,7 +376,7 @@ function createList(
                 name: coordDim
             }, info);
         });
-        areaData = new List(map(dims, function (dim, idx) {
+        areaData = new SeriesData(map(dims, function (dim, idx) {
             return {
                 name: dim,
                 type: coordDimsInfos[idx % 2].type
@@ -388,7 +388,7 @@ function createList(
             name: 'value',
             type: 'float'
         }];
-        areaData = new List(coordDimsInfos, maModel);
+        areaData = new SeriesData(coordDimsInfos, maModel);
     }
 
     let optData = map(maModel.get('data'), curry(
diff --git a/src/component/marker/MarkLineView.ts b/src/component/marker/MarkLineView.ts
index d4ed839..23901cc 100644
--- a/src/component/marker/MarkLineView.ts
+++ b/src/component/marker/MarkLineView.ts
@@ -17,7 +17,7 @@
 * under the License.
 */
 
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import * as numberUtil from '../../util/number';
 import * as markerHelper from './markerHelper';
 import LineDraw from '../../chart/helper/LineDraw';
@@ -57,9 +57,9 @@ type MarkLineMergedItemOption = MarkLine2DDataItemOption[number];
 
 const inner = makeInner<{
     // from data
-    from: List<MarkLineModel>
+    from: SeriesData<MarkLineModel>
     // to data
-    to: List<MarkLineModel>
+    to: SeriesData<MarkLineModel>
 }, MarkLineModel>();
 
 const markLineTransform = function (
@@ -196,7 +196,7 @@ function markLineFilter(
 }
 
 function updateSingleMarkerEndLayout(
-    data: List<MarkLineModel>,
+    data: SeriesData<MarkLineModel>,
     idx: number,
     isFrom: boolean,
     seriesModel: SeriesModel,
@@ -311,7 +311,7 @@ class MarkLineView extends MarkerView {
 
         const fromData = mlData.from;
         const toData = mlData.to;
-        const lineData = mlData.line as List<MarkLineModel, LineDataVisual>;
+        const lineData = mlData.line as SeriesData<MarkLineModel, LineDataVisual>;
 
         inner(mlModel).from = fromData;
         inner(mlModel).to = toData;
@@ -388,7 +388,7 @@ class MarkLineView extends MarkerView {
         });
 
         function updateDataVisualAndLayout(
-            data: List<MarkLineModel>,
+            data: SeriesData<MarkLineModel>,
             idx: number,
             isFrom: boolean
         ) {
@@ -452,10 +452,10 @@ function createList(coordSys: CoordinateSystem, seriesModel: SeriesModel, mlMode
         }];
     }
 
-    const fromData = new List(coordDimsInfos, mlModel);
-    const toData = new List(coordDimsInfos, mlModel);
+    const fromData = new SeriesData(coordDimsInfos, mlModel);
+    const toData = new SeriesData(coordDimsInfos, mlModel);
     // No dimensions
-    const lineData = new List([], mlModel);
+    const lineData = new SeriesData([], mlModel);
 
     let optData = map(mlModel.get('data'), curry(
         markLineTransform, seriesModel, coordSys, mlModel
diff --git a/src/component/marker/MarkPointView.ts b/src/component/marker/MarkPointView.ts
index b7cf0a2..e82fe73 100644
--- a/src/component/marker/MarkPointView.ts
+++ b/src/component/marker/MarkPointView.ts
@@ -20,7 +20,7 @@
 
 import SymbolDraw from '../../chart/helper/SymbolDraw';
 import * as numberUtil from '../../util/number';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import * as markerHelper from './markerHelper';
 import MarkerView from './MarkerView';
 import { CoordinateSystem } from '../../coord/CoordinateSystem';
@@ -35,7 +35,7 @@ import { getVisualFromData } from '../../visual/helper';
 import { ZRColor } from '../../util/types';
 
 function updateMarkerLayout(
-    mpData: List<MarkPointModel>,
+    mpData: SeriesData<MarkPointModel>,
     seriesModel: SeriesModel,
     api: ExtensionAPI
 ) {
@@ -198,7 +198,7 @@ function createList(
         }];
     }
 
-    const mpData = new List(coordDimsInfos, mpModel);
+    const mpData = new SeriesData(coordDimsInfos, mpModel);
     let dataOpt = map(mpModel.get('data'), curry(
             markerHelper.dataTransform, seriesModel
         ));
diff --git a/src/component/marker/MarkerModel.ts b/src/component/marker/MarkerModel.ts
index 341bf5b..6a68558 100644
--- a/src/component/marker/MarkerModel.ts
+++ b/src/component/marker/MarkerModel.ts
@@ -32,7 +32,7 @@ import {
 } from '../../util/types';
 import Model from '../../model/Model';
 import GlobalModel from '../../model/Global';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import { makeInner, defaultEmphasis } from '../../util/model';
 import { createTooltipMarkup } from '../tooltip/tooltipMarkup';
 
@@ -109,7 +109,7 @@ abstract class MarkerModel<Opts extends MarkerOption = MarkerOption> extends Com
 
     __hostSeries: SeriesModel;
 
-    private _data: List;
+    private _data: SeriesData;
 
     /**
      * @overrite
@@ -217,11 +217,11 @@ abstract class MarkerModel<Opts extends MarkerOption = MarkerOption> extends Com
         });
     }
 
-    getData(): List<this> {
-        return this._data as List<this>;
+    getData(): SeriesData<this> {
+        return this._data as SeriesData<this>;
     }
 
-    setData(data: List) {
+    setData(data: SeriesData) {
         this._data = data;
     }
 
diff --git a/src/component/marker/markerHelper.ts b/src/component/marker/markerHelper.ts
index 4ae4b75..ab4a7a9 100644
--- a/src/component/marker/markerHelper.ts
+++ b/src/component/marker/markerHelper.ts
@@ -20,7 +20,7 @@
 import * as numberUtil from '../../util/number';
 import {isDimensionStacked} from '../../data/helper/dataStackHelper';
 import SeriesModel from '../../model/Series';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import { MarkerStatisticType, MarkerPositionOption } from './MarkerModel';
 import { indexOf, curry, clone, isArray } from 'zrender/src/core/util';
 import Axis from '../../coord/Axis';
@@ -68,7 +68,7 @@ function hasXAndY(item: MarkerPositionOption) {
 
 function markerTypeCalculatorWithExtent(
     markerType: MarkerStatisticType,
-    data: List,
+    data: SeriesData,
     otherDataDim: string,
     targetDataDim: string,
     otherCoordIndex: number,
@@ -171,7 +171,7 @@ export function dataTransform(
 
 export function getAxisInfo(
     item: MarkerPositionOption,
-    data: List,
+    data: SeriesData,
     coordSys: CoordinateSystem,
     seriesModel: SeriesModel
 ) {
@@ -236,7 +236,7 @@ export function dimValueGetter(
 }
 
 export function numCalculate(
-    data: List,
+    data: SeriesData,
     valueDataDim: string,
     type: MarkerStatisticType
 ) {
diff --git a/src/component/timeline/SliderTimelineModel.ts b/src/component/timeline/SliderTimelineModel.ts
index 618e49c..ec20bc9 100644
--- a/src/component/timeline/SliderTimelineModel.ts
+++ b/src/component/timeline/SliderTimelineModel.ts
@@ -20,7 +20,7 @@
 import TimelineModel, { TimelineOption } from './TimelineModel';
 import { DataFormatMixin } from '../../model/mixin/dataFormat';
 import { mixin } from 'zrender/src/core/util';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import { inheritDefaultOption } from '../../util/component';
 
 export interface SliderTimelineOption extends TimelineOption {
@@ -149,7 +149,7 @@ class SliderTimelineModel extends TimelineModel {
 }
 
 interface SliderTimelineModel extends DataFormatMixin {
-    getData(): List<SliderTimelineModel>
+    getData(): SeriesData<SliderTimelineModel>
 }
 
 mixin(SliderTimelineModel, DataFormatMixin.prototype);
diff --git a/src/component/timeline/TimelineModel.ts b/src/component/timeline/TimelineModel.ts
index 619f468..ecfd963 100644
--- a/src/component/timeline/TimelineModel.ts
+++ b/src/component/timeline/TimelineModel.ts
@@ -18,7 +18,7 @@
 */
 
 import ComponentModel from '../../model/Component';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import {
     ComponentOption,
     BoxLayoutOptionMixin,
@@ -171,7 +171,7 @@ class TimelineModel extends ComponentModel<TimelineOption> {
 
     layoutMode = 'box';
 
-    private _data: List<TimelineModel>;
+    private _data: SeriesData<TimelineModel>;
 
     private _names: string[];
 
@@ -275,7 +275,7 @@ class TimelineModel extends ComponentModel<TimelineOption> {
             value: 'number'
         })[axisType] || 'number';
 
-        const data = this._data = new List([{
+        const data = this._data = new SeriesData([{
             name: 'value', type: dimType
         }], this);
 
diff --git a/src/component/visualMap/VisualMapModel.ts b/src/component/visualMap/VisualMapModel.ts
index 2ef3c33..0ec6cef 100644
--- a/src/component/visualMap/VisualMapModel.ts
+++ b/src/component/visualMap/VisualMapModel.ts
@@ -38,7 +38,7 @@ import ComponentModel from '../../model/Component';
 import Model from '../../model/Model';
 import GlobalModel from '../../model/Global';
 import SeriesModel from '../../model/Series';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 
 const mapVisual = VisualMapping.mapVisual;
 const eachVisual = VisualMapping.eachVisual;
@@ -379,7 +379,7 @@ class VisualMapModel<Opts extends VisualMapOption = VisualMapOption> extends Com
     /**
      * Return  Concrete dimention. If return null/undefined, no dimension used.
      */
-    getDataDimension(list: List) {
+    getDataDimension(list: SeriesData) {
         const optDim = this.option.dimension;
         const listDimensions = list.dimensions;
         if (optDim == null && !listDimensions.length) {
diff --git a/src/coord/axisHelper.ts b/src/coord/axisHelper.ts
index 3bc6860..d058db3 100644
--- a/src/coord/axisHelper.ts
+++ b/src/coord/axisHelper.ts
@@ -35,7 +35,7 @@ import LogScale from '../scale/Log';
 import Axis from './Axis';
 import { AxisBaseOption, TimeAxisLabelFormatterOption } from './axisCommonTypes';
 import type CartesianAxisModel from './cartesian/AxisModel';
-import List from '../data/List';
+import SeriesData from '../data/SeriesData';
 import { getStackedDimension } from '../data/helper/dataStackHelper';
 import { Dictionary, DimensionName, ScaleTick, TimeScaleTick } from '../util/types';
 import { ensureScaleRawExtentInfo } from './scaleRawExtentInfo';
@@ -361,7 +361,7 @@ export function shouldShowAllLabels(axis: Axis): boolean {
         && getOptionCategoryInterval(axis.getLabelModel()) === 0;
 }
 
-export function getDataDimensionsOnAxis(data: List, axisDim: string): DimensionName[] {
+export function getDataDimensionsOnAxis(data: SeriesData, axisDim: string): DimensionName[] {
     // Remove duplicated dat dimensions caused by `getStackedDimension`.
     const dataDimMap = {} as Dictionary<boolean>;
     // Currently `mapDimensionsAll` will contain stack result dimension ('__\0ecstackresult').
@@ -379,7 +379,7 @@ export function getDataDimensionsOnAxis(data: List, axisDim: string): DimensionN
     return zrUtil.keys(dataDimMap);
 }
 
-export function unionAxisExtentFromData(dataExtent: number[], data: List, axisDim: string): void {
+export function unionAxisExtentFromData(dataExtent: number[], data: SeriesData, axisDim: string): void {
     if (data) {
         zrUtil.each(getDataDimensionsOnAxis(data, axisDim), function (dim) {
             const seriesExtent = data.getApproximateExtent(dim);
diff --git a/src/coord/cartesian/Grid.ts b/src/coord/cartesian/Grid.ts
index 32c616d..6eef23e 100644
--- a/src/coord/cartesian/Grid.ts
+++ b/src/coord/cartesian/Grid.ts
@@ -44,7 +44,7 @@ import ExtensionAPI from '../../core/ExtensionAPI';
 import { Dictionary } from 'zrender/src/core/types';
 import {CoordinateSystemMaster} from '../CoordinateSystem';
 import { ScaleDataValue } from '../../util/types';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 import OrdinalScale from '../../scale/Ordinal';
 import { isCartesian2DSeries, findAxisModels } from './cartesianAxisHelper';
 
@@ -450,7 +450,7 @@ class Grid implements CoordinateSystemMaster {
             }
         }, this);
 
-        function unionExtent(data: List, axis: Axis2D): void {
+        function unionExtent(data: SeriesData, axis: Axis2D): void {
             each(getDataDimensionsOnAxis(data, axis.dim), function (dim) {
                 axis.scale.unionExtentFromData(data, dim);
             });
diff --git a/src/coord/parallel/Parallel.ts b/src/coord/parallel/Parallel.ts
index 6080b85..e22cf9a 100644
--- a/src/coord/parallel/Parallel.ts
+++ b/src/coord/parallel/Parallel.ts
@@ -37,7 +37,7 @@ import ExtensionAPI from '../../core/ExtensionAPI';
 import { Dictionary, DimensionName, ScaleDataValue } from '../../util/types';
 import { CoordinateSystem, CoordinateSystemMaster } from '../CoordinateSystem';
 import ParallelAxisModel, { ParallelActiveState } from './AxisModel';
-import List from '../../data/List';
+import SeriesData from '../../data/SeriesData';
 
 const each = zrUtil.each;
 const mathMin = Math.min;
@@ -357,7 +357,7 @@ class Parallel implements CoordinateSystemMaster, CoordinateSystem {
      * @param end the next dataIndex of the last dataIndex will be travel.
      */
     eachActiveState(
-        data: List,
+        data: SeriesData,
         callback: (activeState: ParallelActiveState, dataIndex: number) => void,
         start?: number,
         end?: number
diff --git a/src/core/Scheduler.ts b/src/core/Scheduler.ts
index 2081e4a..4d67411 100644
--- a/src/core/Scheduler.ts
+++ b/src/core/Scheduler.ts
@@ -33,7 +33,7 @@ import {
 import { EChartsType } from './echarts';
 import SeriesModel from '../model/Series';
 import ChartView from '../view/Chart';
-import List from '../data/List';
+import SeriesData from '../data/SeriesData';
 
 export type GeneralTask = Task<TaskContext>;
 export type SeriesTask = Task<SeriesTaskContext>;
@@ -76,7 +76,7 @@ type PerformStageTaskOpt = {
 
 export interface SeriesTaskContext extends TaskContext {
     model?: SeriesModel;
-    data?: List;
+    data?: SeriesData;
     view?: ChartView;
     ecModel?: GlobalModel;
     api?: ExtensionAPI;
diff --git a/src/core/task.ts b/src/core/task.ts
index 97bcaeb..08d7e23 100644
--- a/src/core/task.ts
+++ b/src/core/task.ts
@@ -21,12 +21,12 @@ import {assert, isArray} from 'zrender/src/core/util';
 import SeriesModel from '../model/Series';
 import { Pipeline } from './Scheduler';
 import { Payload } from '../util/types';
-import List from '../data/List';
+import SeriesData from '../data/SeriesData';
 
 
 export interface TaskContext {
-    outputData?: List;
-    data?: List;
+    outputData?: SeriesData;
+    data?: SeriesData;
     payload?: Payload;
     model?: SeriesModel;
 };
diff --git a/src/data/DataDimensionInfo.ts b/src/data/DataDimensionInfo.ts
index 2857ef8..2b95198 100644
--- a/src/data/DataDimensionInfo.ts
+++ b/src/data/DataDimensionInfo.ts
@@ -25,7 +25,6 @@ class DataDimensionInfo {
 
     /**
      * Dimension type. The enumerable values are the key of
-     * `dataCtors` of `data/List`.
      * Optional.
      */
     type?: DimensionType;
@@ -63,8 +62,8 @@ class DataDimensionInfo {
     coordDimIndex?: number;
 
     /**
-     * This index of this dimension info in `data/List#_dimensionInfos`.
-     * Mandatory after added to `data/List`.
+     * This index of this dimension info in `data/SeriesData#_dimensionInfos`.
+     * Mandatory after added to `data/SeriesData`.
      */
     index?: number;
 
diff --git a/src/data/DataStorage.ts b/src/data/DataStorage.ts
new file mode 100644
index 0000000..2a9da9e
--- /dev/null
+++ b/src/data/DataStorage.ts
@@ -0,0 +1,1141 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+import { assert, clone, isFunction, keys, map, reduce } from 'zrender/src/core/util';
+import {
+    Dictionary,
+    DimensionIndex,
+    DimensionName,
+    OptionDataItem,
+    OrdinalNumber,
+    ParsedValue,
+    ParsedValueNumeric
+} from '../util/types';
+import { DataProvider } from './helper/dataProvider';
+import { parseDataValue } from './helper/dataValueHelper';
+import OrdinalMeta from './OrdinalMeta';
+
+const UNDEFINED = 'undefined';
+/* global Float64Array, Int32Array, Uint32Array, Uint16Array */
+
+/**
+ * Multi dimensional data storage
+ */
+ const dataCtors = {
+    'float': typeof Float64Array === UNDEFINED
+        ? Array : Float64Array,
+    'int': typeof Int32Array === UNDEFINED
+        ? Array : Int32Array,
+    // Ordinal data type can be string or int
+    'ordinal': Array,
+    'number': Array,
+    'time': Array
+} as const;
+
+export type DataStoreDimensionType = keyof typeof dataCtors;
+
+type DataTypedArray = Uint32Array | Int32Array | Uint16Array | Float64Array;
+type DataTypedArrayConstructor = typeof Uint32Array | typeof Int32Array | typeof Uint16Array | typeof Float64Array;
+type DataArrayLikeConstructor = typeof Array | DataTypedArrayConstructor;
+
+// Caution: MUST not use `new CtorUint32Array(arr, 0, len)`, because the Ctor of array is
+// different from the Ctor of typed array.
+const CtorUint32Array = typeof Uint32Array === UNDEFINED ? Array : Uint32Array;
+const CtorUint16Array = typeof Uint16Array === UNDEFINED ? Array : Uint16Array;
+
+type DataValueChunk = ArrayLike<ParsedValue>;
+
+// If Ctx not specified, use List as Ctx
+type EachCb0 = (idx: number) => void;
+type EachCb1 = (x: ParsedValue, idx: number) => void;
+type EachCb2 = (x: ParsedValue, y: ParsedValue, idx: number) => void;
+type EachCb = (...args: any) => void;
+type FilterCb0 = (idx: number) => boolean;
+type FilterCb1 = (x: ParsedValue, idx: number) => boolean;
+type FilterCb = (...args: any) => boolean;
+type MapArrayCb = (...args: any) => any;
+type MapCb = (...args: any) => ParsedValue | ParsedValue[];
+
+export type DimValueGetter = (
+    this: DataStorage,
+    dataItem: any,
+    /**
+     * @deprecated
+     * dimName is not used anymore
+     */
+    dimName: string,
+    dataIndex: number,
+    dimIndex: DimensionIndex
+) => ParsedValue;
+
+interface StoreDimensionInfo {
+    type?: DataStoreDimensionType;  // Default to be float.
+    name?: string;
+    ordinalMeta?: OrdinalMeta;
+}
+
+let defaultDimValueGetters: {[sourceFormat: string]: DimValueGetter};
+
+function getIndicesCtor(rawCount: number): DataArrayLikeConstructor {
+    // The possible max value in this._indicies is always this._rawCount despite of filtering.
+    return rawCount > 65535 ? CtorUint32Array : CtorUint16Array;
+};
+function getInitialExtent(): [number, number] {
+    return [Infinity, -Infinity];
+};
+function cloneChunk(originalChunk: DataValueChunk): DataValueChunk {
+    const Ctor = originalChunk.constructor;
+    // Only shallow clone is enough when Array.
+    return Ctor === Array
+        ? (originalChunk as Array<ParsedValue>).slice()
+        : new (Ctor as DataTypedArrayConstructor)(originalChunk as DataTypedArray);
+}
+
+function prepareStorage(
+    storage: DataValueChunk[],
+    dimIdx: number,
+    dimType: DataStoreDimensionType,
+    end: number,
+    append?: boolean
+): void {
+    const DataCtor = dataCtors[dimType || 'float'];
+
+    if (append) {
+        const oldStore = storage[dimIdx];
+        const oldLen = oldStore && oldStore.length;
+        if (!(oldLen === end)) {
+            const newStore = new DataCtor(end);
+            // The cost of the copy is probably inconsiderable
+            // within the initial chunkSize.
+            for (let j = 0; j < oldLen; j++) {
+                newStore[j] = oldStore[j];
+            }
+            storage[dimIdx] = newStore;
+        }
+    }
+    else {
+        storage[dimIdx] = new DataCtor(end);
+    }
+};
+class DataStorage {
+    private _chunks: DataValueChunk[] = [];
+
+    private _provider: DataProvider;
+
+    // Raw extent will not be cloned, but only transfered.
+    // It will not be calculated util needed.
+    private _rawExtent: [number, number][] = [];
+
+    private _extent: [number, number][] = [];
+
+    // Indices stores the indices of data subset after filtered.
+    // This data subset will be used in chart.
+    private _indices: ArrayLike<any>;
+
+    private _count: number = 0;
+    private _rawCount: number = 0;
+
+    private _dimensions: StoreDimensionInfo[];
+    private _dimValueGetter: DimValueGetter;
+
+    defaultDimValueGetter: DimValueGetter;
+    /**
+     * Initialize from data
+     * @param data source or data or data provider.
+     */
+    initData(
+        provider: DataProvider,
+        dimensions: StoreDimensionInfo[],
+        dimValueGetter?: DimValueGetter
+    ): void {
+        if (__DEV__) {
+            assert(
+                isFunction(provider.getItem) && isFunction(provider.count),
+                'Inavlid data provider.'
+            );
+        }
+
+        this._provider = provider;
+
+        // Clear
+        this._chunks = [];
+        this._indices = null;
+        this.getRawIndex = this._getRawIdxIdentity;
+
+        const defaultGetter = this.defaultDimValueGetter =
+             defaultDimValueGetters[provider.getSource().sourceFormat];
+        // Default dim value getter
+        this._dimValueGetter = dimValueGetter || defaultGetter;
+
+        const emptyObj = {};
+
+        // Reset raw extent.
+        this._rawExtent = [];
+        this._dimensions = dimensions;
+
+        const dimensionsIdxMap: Dictionary<number> = {};
+        let prefix = '';
+
+        // Needs to add prefix if key is used in object prototype
+        for (let i = 0; i < dimensions.length; i++) {
+            const name = dimensions[i].name;
+            if ((emptyObj as any)[name] != null) {
+                prefix = '$$';
+            }
+        }
+        for (let i = 0; i < dimensions.length; i++) {
+            const name = dimensions[i].name;
+            dimensionsIdxMap[prefix + name] = i;
+        }
+
+        // We use different functions because it may be a hotspot code.
+        this.getDimensionIndex = prefix ? function (dim) {
+            return dimensionsIdxMap[prefix + dim];
+        } : function (dim) {
+            return dimensionsIdxMap[dim];
+        };
+
+        this._initDataFromProvider(0, provider.count());
+    }
+
+    getProvider(): DataProvider {
+        return this._provider;
+    }
+
+    getDimensionIndex: (dim: DimensionName) => number;
+
+    /**
+     * Caution: Can be only called on raw data (before `this._indices` created).
+     */
+    appendData(data: ArrayLike<any>) {
+        if (__DEV__) {
+            assert(!this._indices, 'appendData can only be called on raw data.');
+        }
+
+        const provider = this._provider;
+        const start = this.count();
+        provider.appendData(data);
+        let end = provider.count();
+        if (!provider.persistent) {
+            end += start;
+        }
+        this._initDataFromProvider(start, end, true);
+
+        return [start, end];
+    }
+
+    private _initDataFromProvider(
+        start: number,
+        end: number,
+        append?: boolean
+    ): void {
+        if (start >= end) {
+            return;
+        }
+
+        const provider = this._provider;
+        const chunks = this._chunks;
+        const dimensions = this._dimensions;
+        const dimLen = dimensions.length;
+        const rawExtent = this._rawExtent;
+        const dimNames = map(dimensions, dim => dim.name);
+
+        for (let i = 0; i < dimLen; i++) {
+            const dim = dimensions[i];
+            if (!rawExtent[i]) {
+                rawExtent[i] = getInitialExtent();
+            }
+            prepareStorage(chunks, i, dim.type, end, append);
+        }
+
+        if (provider.fillStorage) {
+            provider.fillStorage(start, end, chunks, rawExtent);
+        }
+        else {
+            let dataItem = [] as OptionDataItem;
+            for (let idx = start; idx < end; idx++) {
+                // NOTICE: Try not to write things into dataItem
+                dataItem = provider.getItem(idx, dataItem);
+                // Each data item is value
+                // [1, 2]
+                // 2
+                // Bar chart, line chart which uses category axis
+                // only gives the 'y' value. 'x' value is the indices of category
+                // Use a tempValue to normalize the value to be a (x, y) value
+
+                // Store the data by dimensions
+                for (let dimIdx = 0; dimIdx < dimLen; dimIdx++) {
+                    const dimStorage = chunks[dimIdx];
+                    // PENDING NULL is empty or zero
+                    const val = this._dimValueGetter(
+                        dataItem, dimNames[dimIdx], idx, dimIdx
+                    ) as ParsedValueNumeric;
+                    (dimStorage as ParsedValue[])[idx] = val;
+
+                    const dimRawExtent = rawExtent[dimIdx];
+                    val < dimRawExtent[0] && (dimRawExtent[0] = val);
+                    val > dimRawExtent[1] && (dimRawExtent[1] = val);
+                }
+            }
+        }
+
+        if (!provider.persistent && provider.clean) {
+            // Clean unused data if data source is typed array.
+            provider.clean();
+        }
+
+        this._rawCount = this._count = end;
+        // Reset data extent
+        this._extent = [];
+    }
+
+    count(): number {
+        return this._count;
+    }
+
+    /**
+     * Get value. Return NaN if idx is out of range.
+     */
+    get(dim: number, idx: number): ParsedValue {
+        if (!(idx >= 0 && idx < this._count)) {
+            return NaN;
+        }
+        const dimStore = this._chunks[dim];
+        return dimStore ? dimStore[this.getRawIndex(idx)] : NaN;
+    }
+
+    /**
+     * @param dim concrete dim
+     */
+    getByRawIndex(dim: number, rawIdx: number): ParsedValue {
+        if (!(rawIdx >= 0 && rawIdx < this._rawCount)) {
+            return NaN;
+        }
+        const dimStore = this._chunks[dim];
+        return dimStore ? dimStore[rawIdx] : NaN;
+    }
+
+    /**
+     * Get sum of data in one dimension
+     */
+    getSum(dim: DimensionIndex): number {
+        const dimData = this._chunks[dim];
+        let sum = 0;
+        if (dimData) {
+            for (let i = 0, len = this.count(); i < len; i++) {
+                const value = this.get(dim, i) as number;
+                if (!isNaN(value)) {
+                    sum += value;
+                }
+            }
+        }
+        return sum;
+    }
+
+    /**
+     * Get median of data in one dimension
+     */
+    getMedian(dim: DimensionIndex): number {
+        const dimDataArray: ParsedValue[] = [];
+        // map all data of one dimension
+        this.each([dim], function (val) {
+            if (!isNaN(val as number)) {
+                dimDataArray.push(val);
+            }
+        });
+
+        // TODO
+        // Use quick select?
+        const sortedDimDataArray = dimDataArray.sort(function (a: number, b: number) {
+            return a - b;
+        }) as number[];
+        const len = this.count();
+        // calculate median
+        return len === 0
+            ? 0
+            : len % 2 === 1
+            ? sortedDimDataArray[(len - 1) / 2]
+            : (sortedDimDataArray[len / 2] + sortedDimDataArray[len / 2 - 1]) / 2;
+    }
+
+    /**
+     * Retreive the index with given raw data index
+     */
+     indexOfRawIndex(rawIndex: number): number {
+        if (rawIndex >= this._rawCount || rawIndex < 0) {
+            return -1;
+        }
+
+        if (!this._indices) {
+            return rawIndex;
+        }
+
+        // Indices are ascending
+        const indices = this._indices;
+
+        // If rawIndex === dataIndex
+        const rawDataIndex = indices[rawIndex];
+        if (rawDataIndex != null && rawDataIndex < this._count && rawDataIndex === rawIndex) {
+            return rawIndex;
+        }
+
+        let left = 0;
+        let right = this._count - 1;
+        while (left <= right) {
+            const mid = (left + right) / 2 | 0;
+            if (indices[mid] < rawIndex) {
+                left = mid + 1;
+            }
+            else if (indices[mid] > rawIndex) {
+                right = mid - 1;
+            }
+            else {
+                return mid;
+            }
+        }
+        return -1;
+    }
+
+
+    /**
+     * Retreive the index of nearest value
+     * @param dim
+     * @param value
+     * @param [maxDistance=Infinity]
+     * @return If and only if multiple indices has
+     *         the same value, they are put to the result.
+     */
+    indicesOfNearest(
+        dim: DimensionIndex, value: number, maxDistance?: number
+    ): number[] {
+        const chunks = this._chunks;
+        const dimData = chunks[dim];
+        const nearestIndices: number[] = [];
+
+        if (!dimData) {
+            return nearestIndices;
+        }
+
+        if (maxDistance == null) {
+            maxDistance = Infinity;
+        }
+
+        let minDist = Infinity;
+        let minDiff = -1;
+        let nearestIndicesLen = 0;
+
+        // Check the test case of `test/ut/spec/data/SeriesData.js`.
+        for (let i = 0, len = this.count(); i < len; i++) {
+            const dataIndex = this.getRawIndex(i);
+            const diff = value - (dimData[dataIndex] as number);
+            const dist = Math.abs(diff);
+            if (dist <= maxDistance) {
+                // When the `value` is at the middle of `this.get(dim, i)` and `this.get(dim, i+1)`,
+                // we'd better not push both of them to `nearestIndices`, otherwise it is easy to
+                // get more than one item in `nearestIndices` (more specifically, in `tooltip`).
+                // So we chose the one that `diff >= 0` in this csae.
+                // But if `this.get(dim, i)` and `this.get(dim, j)` get the same value, both of them
+                // should be push to `nearestIndices`.
+                if (dist < minDist
+                    || (dist === minDist && diff >= 0 && minDiff < 0)
+                ) {
+                    minDist = dist;
+                    minDiff = diff;
+                    nearestIndicesLen = 0;
+                }
+                if (diff === minDiff) {
+                    nearestIndices[nearestIndicesLen++] = i;
+                }
+            }
+        }
+        nearestIndices.length = nearestIndicesLen;
+
+        return nearestIndices;
+    }
+
+    getIndices(): ArrayLike<number> {
+        let newIndices;
+
+        const indices = this._indices;
+        if (indices) {
+            const Ctor = indices.constructor as DataArrayLikeConstructor;
+            const thisCount = this._count;
+            // `new Array(a, b, c)` is different from `new Uint32Array(a, b, c)`.
+            if (Ctor === Array) {
+                newIndices = new Ctor(thisCount);
+                for (let i = 0; i < thisCount; i++) {
+                    newIndices[i] = indices[i];
+                }
+            }
+            else {
+                newIndices = new (Ctor as DataTypedArrayConstructor)(
+                    (indices as DataTypedArray).buffer, 0, thisCount
+                );
+            }
+        }
+        else {
+            const Ctor = getIndicesCtor(this._rawCount);
+            newIndices = new Ctor(this.count());
+            for (let i = 0; i < newIndices.length; i++) {
+                newIndices[i] = i;
+            }
+        }
+
+        return newIndices;
+    }
+
+    /**
+     * Data filter
+     */
+     filterSelf(
+         dims: DimensionIndex[],
+         cb: FilterCb
+     ) {
+         if (!this._count) {
+             return;
+         }
+
+         const count = this.count();
+         const Ctor = getIndicesCtor(this._rawCount);
+         const newIndices = new Ctor(count);
+         const value = [];
+         const dimSize = dims.length;
+
+         let offset = 0;
+         const dim0 = dims[0];
+         const chunks = this._chunks;
+
+         for (let i = 0; i < count; i++) {
+             let keep;
+             const rawIdx = this.getRawIndex(i);
+             // Simple optimization
+             if (dimSize === 0) {
+                 keep = (cb as FilterCb0)(i);
+             }
+             else if (dimSize === 1) {
+                 const val = chunks[dim0][rawIdx];
+                 keep = (cb as FilterCb1)(val, i);
+             }
+             else {
+                 let k = 0;
+                 for (; k < dimSize; k++) {
+                     value[k] = chunks[dims[k]][rawIdx];
+                 }
+                 value[k] = i;
+                 keep = (cb as FilterCb).apply(null, value);
+             }
+             if (keep) {
+                 newIndices[offset++] = rawIdx;
+             }
+         }
+
+         // Set indices after filtered.
+         if (offset < count) {
+             this._indices = newIndices;
+         }
+         this._count = offset;
+         // Reset data extent
+         this._extent = [];
+
+         this._updateGetRawIdx();
+     }
+
+     /**
+      * Select data in range. (For optimization of filter)
+      * (Manually inline code, support 5 million data filtering in data zoom.)
+      */
+     selectRange(range: {[dimIdx: number]: [number, number]}) {
+
+         const len = this._count;
+
+         if (!len) {
+             return;
+         }
+
+         const dims = keys(range);
+         const dimSize = dims.length;
+         if (!dimSize) {
+             return;
+         }
+
+         const originalCount = this.count();
+         const Ctor = getIndicesCtor(this._rawCount);
+         const newIndices = new Ctor(originalCount);
+
+         let offset = 0;
+         const dim0 = dims[0];
+
+         const min = range[dim0][0];
+         const max = range[dim0][1];
+         const storeArr = this._chunks;
+
+         let quickFinished = false;
+         if (!this._indices) {
+             // Extreme optimization for common case. About 2x faster in chrome.
+             let idx = 0;
+             if (dimSize === 1) {
+                 const dimStorage = storeArr[dims[0]];
+                 for (let i = 0; i < len; i++) {
+                     const val = dimStorage[i];
+                     // NaN will not be filtered. Consider the case, in line chart, empty
+                     // value indicates the line should be broken. But for the case like
+                     // scatter plot, a data item with empty value will not be rendered,
+                     // but the axis extent may be effected if some other dim of the data
+                     // item has value. Fortunately it is not a significant negative effect.
+                     if (
+                         (val >= min && val <= max) || isNaN(val as any)
+                     ) {
+                         newIndices[offset++] = idx;
+                     }
+                     idx++;
+                 }
+                 quickFinished = true;
+             }
+             else if (dimSize === 2) {
+                 const dimStorage = storeArr[dims[0]];
+                 const dimStorage2 = storeArr[dims[1]];
+                 const min2 = range[dims[1]][0];
+                 const max2 = range[dims[1]][1];
+                 for (let i = 0; i < len; i++) {
+                     const val = dimStorage[i];
+                     const val2 = dimStorage2[i];
+                     // Do not filter NaN, see comment above.
+                     if ((
+                             (val >= min && val <= max) || isNaN(val as any)
+                         )
+                         && (
+                             (val2 >= min2 && val2 <= max2) || isNaN(val2 as any)
+                         )
+                     ) {
+                         newIndices[offset++] = idx;
+                     }
+                     idx++;
+                 }
+                 quickFinished = true;
+             }
+         }
+         if (!quickFinished) {
+             if (dimSize === 1) {
+                 for (let i = 0; i < originalCount; i++) {
+                     const rawIndex = this.getRawIndex(i);
+                     const val = storeArr[dims[0]][rawIndex];
+                     // Do not filter NaN, see comment above.
+                     if (
+                         (val >= min && val <= max) || isNaN(val as any)
+                     ) {
+                         newIndices[offset++] = rawIndex;
+                     }
+                 }
+             }
+             else {
+                 for (let i = 0; i < originalCount; i++) {
+                     let keep = true;
+                     const rawIndex = this.getRawIndex(i);
+                     for (let k = 0; k < dimSize; k++) {
+                         const dimk = dims[k];
+                         const val = storeArr[dimk][rawIndex];
+                         // Do not filter NaN, see comment above.
+                         if (val < range[dimk][0] || val > range[dimk][1]) {
+                             keep = false;
+                         }
+                     }
+                     if (keep) {
+                         newIndices[offset++] = this.getRawIndex(i);
+                     }
+                 }
+             }
+         }
+
+         // Set indices after filtered.
+         if (offset < originalCount) {
+             this._indices = newIndices;
+         }
+         this._count = offset;
+         // Reset data extent
+         this._extent = [];
+
+         this._updateGetRawIdx();
+     }
+
+     /**
+      * Data mapping to a plain array
+      */
+     /* eslint-enable */
+     mapArray(dims: DimensionIndex[], cb: MapArrayCb): any[] {
+         const result: any[] = [];
+         this.each(dims, function () {
+             result.push(cb && (cb as MapArrayCb).apply(null, arguments));
+         });
+         return result;
+     }
+
+     /**
+      * Data mapping to a new List with given dimensions
+      */
+     map(dims: DimensionIndex[], cb: MapCb): DataStorage {
+        // TODO only clone picked chunks.
+         const target = this.clone(dims);
+         const targetChunks = target._chunks;
+
+         // Following properties are all immutable.
+         // So we can reference to the same value
+         target._indices = this._indices;
+         target.getRawIndex = this.getRawIndex;
+
+         const tmpRetValue = [];
+         const dimSize = dims.length;
+         const dataCount = this.count();
+         const values = [];
+         const rawExtent = target._rawExtent;
+
+         for (let dataIndex = 0; dataIndex < dataCount; dataIndex++) {
+            const rawIndex = this.getRawIndex(dataIndex);
+
+             for (let k = 0; k < dimSize; k++) {
+                 values[k] = targetChunks[dims[k]][rawIndex];
+             }
+             values[dimSize] = dataIndex;
+
+             let retValue = cb && cb.apply(null, values);
+             if (retValue != null) {
+                 // a number or string (in oridinal dimension)?
+                 if (typeof retValue !== 'object') {
+                     tmpRetValue[0] = retValue;
+                     retValue = tmpRetValue;
+                 }
+
+                 for (let i = 0; i < retValue.length; i++) {
+                     const dim = dims[i];
+                     const val = retValue[i];
+                     const rawExtentOnDim = rawExtent[dim];
+
+                     const dimStore = targetChunks[dim];
+                     if (dimStore) {
+                         (dimStore as ParsedValue[])[rawIndex] = val;
+                     }
+
+                     if (val < rawExtentOnDim[0]) {
+                         rawExtentOnDim[0] = val as number;
+                     }
+                     if (val > rawExtentOnDim[1]) {
+                         rawExtentOnDim[1] = val as number;
+                     }
+                 }
+             }
+         }
+
+         return target;
+     }
+
+     /**
+      * Large data down sampling using largest-triangle-three-buckets
+      * @param {string} valueDimension
+      * @param {number} targetCount
+      */
+     lttbDownSample(
+         valueDimension: DimensionIndex,
+         rate: number
+     ) {
+         const target = this.clone([valueDimension]);
+         const targetStorage = target._chunks;
+         const dimStore = targetStorage[valueDimension];
+         const len = this.count();
+         const newIndices = new (getIndicesCtor(this._rawCount))(len);
+
+         let sampledIndex = 0;
+
+         const frameSize = Math.floor(1 / rate);
+
+         let currentRawIndex = this.getRawIndex(0);
+         let maxArea;
+         let area;
+         let nextRawIndex;
+
+         // First frame use the first data.
+         newIndices[sampledIndex++] = currentRawIndex;
+         for (let i = 1; i < len - 1; i += frameSize) {
+             const nextFrameStart = Math.min(i + frameSize, len - 1);
+             const nextFrameEnd = Math.min(i + frameSize * 2, len);
+
+             const avgX = (nextFrameEnd + nextFrameStart) / 2;
+             let avgY = 0;
+
+             for (let idx = nextFrameStart; idx < nextFrameEnd; idx++) {
+                 const rawIndex = this.getRawIndex(idx);
+                 const y = dimStore[rawIndex] as number;
+                 if (isNaN(y)) {
+                     continue;
+                 }
+                 avgY += y as number;
+             }
+             avgY /= (nextFrameEnd - nextFrameStart);
+
+             const frameStart = i;
+             const frameEnd = Math.min(i + frameSize, len);
+
+             const pointAX = i - 1;
+             const pointAY = dimStore[currentRawIndex] as number;
+
+             maxArea = -1;
+
+             nextRawIndex = frameStart;
+             // Find a point from current frame that construct a triangel with largest area with previous selected point
+             // And the average of next frame.
+             for (let idx = frameStart; idx < frameEnd; idx++) {
+                 const rawIndex = this.getRawIndex(idx);
+                 const y = dimStore[rawIndex] as number;
+                 if (isNaN(y)) {
+                     continue;
+                 }
+                 // Calculate triangle area over three buckets
+                 area = Math.abs((pointAX - avgX) * (y - pointAY)
+                     - (pointAX - idx) * (avgY - pointAY)
+                 );
+                 if (area > maxArea) {
+                     maxArea = area;
+                     nextRawIndex = rawIndex; // Next a is this b
+                 }
+             }
+
+             newIndices[sampledIndex++] = nextRawIndex;
+
+             currentRawIndex = nextRawIndex; // This a is the next a (chosen b)
+         }
+
+         // First frame use the last data.
+         newIndices[sampledIndex++] = this.getRawIndex(len - 1);
+         target._count = sampledIndex;
+         target._indices = newIndices;
+
+         target.getRawIndex = this._getRawIdx;
+         return target;
+     }
+
+
+    /**
+     * Large data down sampling on given dimension
+     * @param sampleIndex Sample index for name and id
+     */
+     downSample(
+        dimension: DimensionIndex,
+        rate: number,
+        sampleValue: (frameValues: ArrayLike<ParsedValue>) => ParsedValueNumeric,
+        sampleIndex: (frameValues: ArrayLike<ParsedValue>, value: ParsedValueNumeric) => number
+    ): DataStorage {
+        const target = this.clone([dimension]);
+        const targetStorage = target._chunks;
+
+        const frameValues = [];
+        let frameSize = Math.floor(1 / rate);
+
+        const dimStore = targetStorage[dimension];
+        const len = this.count();
+        const rawExtentOnDim = target._rawExtent[dimension];
+
+        const newIndices = new (getIndicesCtor(this._rawCount))(len);
+
+        let offset = 0;
+        for (let i = 0; i < len; i += frameSize) {
+            // Last frame
+            if (frameSize > len - i) {
+                frameSize = len - i;
+                frameValues.length = frameSize;
+            }
+            for (let k = 0; k < frameSize; k++) {
+                const dataIdx = this.getRawIndex(i + k);
+                frameValues[k] = dimStore[dataIdx];
+            }
+            const value = sampleValue(frameValues);
+            const sampleFrameIdx = this.getRawIndex(
+                Math.min(i + sampleIndex(frameValues, value) || 0, len - 1)
+            );
+            // Only write value on the filtered data
+            (dimStore as number[])[sampleFrameIdx] = value;
+
+            if (value < rawExtentOnDim[0]) {
+                rawExtentOnDim[0] = value;
+            }
+            if (value > rawExtentOnDim[1]) {
+                rawExtentOnDim[1] = value;
+            }
+
+            newIndices[offset++] = sampleFrameIdx;
+        }
+
+        target._count = offset;
+
+        this._updateGetRawIdx();
+
+        return target;
+    }
+
+    /**
+     * Data iteration
+     * @param ctx default this
+     * @example
+     *  list.each('x', function (x, idx) {});
+     *  list.each(['x', 'y'], function (x, y, idx) {});
+     *  list.each(function (idx) {})
+     */
+     each(dims: DimensionIndex[], cb: EachCb): void {
+         if (!this._count) {
+             return;
+         }
+         const dimSize = dims.length;
+         const chunks = this._chunks;
+
+         for (let i = 0, len = this.count(); i < len; i++) {
+             const rawIdx = this.getRawIndex(i);
+             // Simple optimization
+             switch (dimSize) {
+                 case 0:
+                     (cb as EachCb0)(i);
+                     break;
+                 case 1:
+                     (cb as EachCb1)(chunks[dims[0]][rawIdx], i);
+                     break;
+                 case 2:
+                     (cb as EachCb2)(
+                         chunks[dims[0]][rawIdx], chunks[dims[1]][rawIdx], i
+                     );
+                     break;
+                 default:
+                     let k = 0;
+                     const value = [];
+                     for (; k < dimSize; k++) {
+                         value[k] = chunks[dims[k]][rawIdx];
+                     }
+                     // Index
+                     value[k] = i;
+                     (cb as EachCb).apply(null, value);
+             }
+         }
+     }
+
+    /**
+     * Get extent of data in one dimension
+     */
+    getDataExtent(dim: DimensionIndex): [number, number] {
+        // Make sure use concrete dim as cache name.
+        const dimData = this._chunks[dim];
+        const initialExtent = getInitialExtent();
+
+        if (!dimData) {
+            return initialExtent;
+        }
+
+        // Make more strict checkings to ensure hitting cache.
+        const currEnd = this.count();
+
+        // Consider the most cases when using data zoom, `getDataExtent`
+        // happened before filtering. We cache raw extent, which is not
+        // necessary to be cleared and recalculated when restore data.
+        const useRaw = !this._indices;
+        let dimExtent: [number, number];
+
+        if (useRaw) {
+            return this._rawExtent[dim].slice() as [number, number];
+        }
+        dimExtent = this._extent[dim];
+        if (dimExtent) {
+            return dimExtent.slice() as [number, number];
+        }
+        dimExtent = initialExtent;
+
+        let min = dimExtent[0];
+        let max = dimExtent[1];
+
+        for (let i = 0; i < currEnd; i++) {
+            const rawIdx = this.getRawIndex(i);
+            const value = dimData[rawIdx] as ParsedValueNumeric;
+            value < min && (min = value);
+            value > max && (max = value);
+        }
+
+        dimExtent = [min, max];
+
+        this._extent[dim] = dimExtent;
+
+        return dimExtent;
+    }
+
+    /**
+     * Get raw data index.
+     * Do not initialize.
+     * Default `getRawIndex`. And it can be changed.
+     */
+     getRawIndex: (idx: number) => number;
+
+     /**
+      * Get raw data item
+      */
+     getRawDataItem(idx: number): OptionDataItem {
+         const rawIdx = this.getRawIndex(idx);
+         if (!this._provider.persistent) {
+             const val = [];
+             const chunks = this._chunks;
+             for (let i = 0; i < chunks.length; i++) {
+                 val.push(chunks[i][rawIdx]);
+             }
+             return val;
+         }
+         else {
+             return this._provider.getItem(this.getRawIndex(idx));
+         }
+     }
+
+     /**
+      *
+      * @param clonedDims Determine which dims to clone. Will share the data if not specified.
+      */
+     clone(clonedDims?: number[]): DataStorage {
+        const target = new DataStorage();
+        const chunks = this._chunks;
+        const clonedDimsMap = clonedDims && reduce(clonedDims, (obj, dimIdx) => {
+            obj[dimIdx] = true;
+            return obj;
+        }, {} as Record<string, boolean>);
+
+        if (clonedDimsMap) {
+            for (let i = 0; i < chunks.length; i++) {
+                // Not clone if dim is not picked.
+                target._chunks[i] = (clonedDimsMap && !clonedDimsMap[i])
+                    ? chunks[i] : cloneChunk(chunks[i]);
+            }
+        }
+        else {
+            target._chunks = chunks;
+        }
+        this._copyCommonProps(target);
+        return target;
+     }
+
+    /**
+     * Get category data.
+     */
+    getCategory(dimIdx: number, idx: number) {
+        const rawIdx = this.getRawIndex(idx);
+        const chunk = this._chunks[dimIdx];
+
+        let val;
+        if (chunk) {
+            const dimInfo = this._dimensions[dimIdx];
+            const ordinalMeta = dimInfo.ordinalMeta;
+            val = chunk[rawIdx];
+            if (ordinalMeta) {
+                val = ordinalMeta.categories[val as OrdinalNumber];
+            }
+        }
+        return val;
+     }
+
+     private _copyCommonProps(target: DataStorage) {
+        target._count = this._count;
+        target._rawCount = this._rawCount;
+        target._extent = clone(this._extent);
+        target._rawExtent = clone(this._rawExtent);
+        target._indices = this._cloneIndices();
+        target._provider = this._provider;
+        target.getDimensionIndex = this.getDimensionIndex;
+        target._updateGetRawIdx();
+     }
+
+     private _cloneIndices() {
+        if (this._indices) {
+            const Ctor = this._indices.constructor as DataArrayLikeConstructor;
+            let indices;
+            if (Ctor === Array) {
+                const thisCount = this._indices.length;
+                indices = new Ctor(thisCount);
+                for (let i = 0; i < thisCount; i++) {
+                    indices[i] = this._indices[i];
+                }
+            }
+            else {
+                indices = new (Ctor as DataTypedArrayConstructor)(this._indices);
+            }
+            return indices;
+        }
+        return null;
+     }
+
+     private _getRawIdxIdentity(idx: number) {
+         return idx;
+     }
+     private _getRawIdx(idx: number) {
+        if (idx < this._count && idx >= 0) {
+            return this._indices[idx];
+        }
+        return -1;
+     }
+
+     private _updateGetRawIdx() {
+        this.getRawIndex = this._indices ? this._getRawIdx : this._getRawIdxIdentity;
+     }
+
+    private static internalField = (function () {
+
+        function getDimValueSimply(
+            this: DataStorage, dataItem: any, dimName: string, dataIndex: number, dimIndex: number
+        ): ParsedValue {
+            return parseDataValue(dataItem[dimIndex], this._dimensions[dimIndex]);
+        }
+
+        defaultDimValueGetters = {
+
+            arrayRows: getDimValueSimply,
+
+            objectRows(
+                this: DataStorage, dataItem: any, dimName: string, dataIndex: number, dimIndex: number
+            ): ParsedValue {
+                return parseDataValue(dataItem[dimIndex], this._dimensions[dataIndex]);
+            },
+
+            keyedColumns: getDimValueSimply,
+
+            original(
+                this: DataStorage, dataItem: any, dimName: string, dataIndex: number, dimIndex: number
+            ): ParsedValue {
+                // Performance sensitive, do not use modelUtil.getDataItemValue.
+                // If dataItem is an plain object with no value field, the let `value`
+                // will be assigned with the object, but it will be tread correctly
+                // in the `convertValue`.
+                const value = dataItem && (dataItem.value == null ? dataItem : dataItem.value);
+
+                return parseDataValue(
+                    (value instanceof Array)
+                        ? value[dimIndex]
+                        // If value is a single number or something else not array.
+                        : value,
+                    this._dimensions[dimIndex]
+                );
+            },
+
+            typedArray: function (
+                this: DataStorage, dataItem: any, dimName: string, dataIndex: number, dimIndex: number
+            ): ParsedValue {
+                return dataItem[dimIndex];
+            }
+
+        };
+
+    })();
+}
+
+export default DataStorage;
\ No newline at end of file
diff --git a/src/data/Graph.ts b/src/data/Graph.ts
index 75ec38f..0ab5f21 100644
--- a/src/data/Graph.ts
+++ b/src/data/Graph.ts
@@ -19,7 +19,7 @@
 
 import * as zrUtil from 'zrender/src/core/util';
 import { Dictionary } from 'zrender/src/core/types';
-import List from './List';
+import SeriesData from './SeriesData';
 import Model from '../model/Model';
 import Element from 'zrender/src/Element';
 import { DimensionLoose, ParsedValue } from '../util/types';
@@ -36,9 +36,9 @@ class Graph {
 
     readonly edges: GraphEdge[] = [];
 
-    data: List;
+    data: SeriesData;
 
-    edgeData: List;
+    edgeData: SeriesData;
 
     /**
      * Whether directed graph.
diff --git a/src/data/List.ts b/src/data/List.ts
deleted file mode 100644
index 0ad2792..0000000
--- a/src/data/List.ts
+++ /dev/null
@@ -1,2278 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one
-* or more contributor license agreements.  See the NOTICE file
-* distributed with this work for additional information
-* regarding copyright ownership.  The ASF licenses this file
-* to you under the Apache License, Version 2.0 (the
-* "License"); you may not use this file except in compliance
-* with the License.  You may obtain a copy of the License at
-*
-*   http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing,
-* software distributed under the License is distributed on an
-* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-* KIND, either express or implied.  See the License for the
-* specific language governing permissions and limitations
-* under the License.
-*/
-
-/* global Float64Array, Int32Array, Uint32Array, Uint16Array */
-
-/**
- * List for data storage
- */
-
-import * as zrUtil from 'zrender/src/core/util';
-import {PathStyleProps} from 'zrender/src/graphic/Path';
-import Model from '../model/Model';
-import DataDiffer from './DataDiffer';
-import {DefaultDataProvider, DataProvider} from './helper/dataProvider';
-import {summarizeDimensions, DimensionSummary} from './helper/dimensionHelper';
-import DataDimensionInfo from './DataDimensionInfo';
-import {ArrayLike, Dictionary, FunctionPropertyNames} from 'zrender/src/core/types';
-import Element from 'zrender/src/Element';
-import {
-    DimensionIndex, DimensionName, DimensionLoose, OptionDataItem,
-    ParsedValue, ParsedValueNumeric, OrdinalNumber, DimensionUserOuput,
-    ModelOption, SeriesDataType, OptionSourceData, SOURCE_FORMAT_TYPED_ARRAY, SOURCE_FORMAT_ORIGINAL,
-    DecalObject
-} from '../util/types';
-import {isDataItemOption, convertOptionIdName} from '../util/model';
-import { getECData } from '../util/innerStore';
-import type Graph from './Graph';
-import type Tree from './Tree';
-import type { VisualMeta } from '../component/visualMap/VisualMapModel';
-import { parseDataValue } from './helper/dataValueHelper';
-import {isSourceInstance, Source} from './Source';
-import OrdinalMeta from './OrdinalMeta';
-import { LineStyleProps } from '../model/mixin/lineStyle';
-
-const mathFloor = Math.floor;
-const isObject = zrUtil.isObject;
-const map = zrUtil.map;
-
-const UNDEFINED = 'undefined';
-const INDEX_NOT_FOUND = -1;
-
-// Use prefix to avoid index to be the same as otherIdList[idx],
-// which will cause weird udpate animation.
-const ID_PREFIX = 'e\0\0';
-
-const dataCtors = {
-    'float': typeof Float64Array === UNDEFINED
-        ? Array : Float64Array,
-    'int': typeof Int32Array === UNDEFINED
-        ? Array : Int32Array,
-    // Ordinal data type can be string or int
-    'ordinal': Array,
-    'number': Array,
-    'time': Array
-};
-
-export type ListDimensionType = keyof typeof dataCtors;
-
-// Caution: MUST not use `new CtorUint32Array(arr, 0, len)`, because the Ctor of array is
-// different from the Ctor of typed array.
-const CtorUint32Array = typeof Uint32Array === UNDEFINED ? Array : Uint32Array;
-const CtorInt32Array = typeof Int32Array === UNDEFINED ? Array : Int32Array;
-const CtorUint16Array = typeof Uint16Array === UNDEFINED ? Array : Uint16Array;
-
-type DataTypedArray = Uint32Array | Int32Array | Uint16Array | Float64Array;
-type DataTypedArrayConstructor = typeof Uint32Array | typeof Int32Array | typeof Uint16Array | typeof Float64Array;
-type DataArrayLikeConstructor = typeof Array | DataTypedArrayConstructor;
-
-
-type DimValueGetter = (
-    this: List,
-    dataItem: any,
-    dimName: DimensionName,
-    dataIndex: number,
-    dimIndex: DimensionIndex
-) => ParsedValue;
-
-type DataValueChunk = ArrayLike<ParsedValue>;
-type DataStorage = {[dimName: string]: DataValueChunk};
-type NameRepeatCount = {[name: string]: number};
-
-
-type ItrParamDims = DimensionLoose | Array<DimensionLoose>;
-// If Ctx not specified, use List as Ctx
-type CtxOrList<Ctx> = unknown extends Ctx ? List : Ctx;
-type EachCb0<Ctx> = (this: CtxOrList<Ctx>, idx: number) => void;
-type EachCb1<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, idx: number) => void;
-type EachCb2<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, y: ParsedValue, idx: number) => void;
-type EachCb<Ctx> = (this: CtxOrList<Ctx>, ...args: any) => void;
-type FilterCb0<Ctx> = (this: CtxOrList<Ctx>, idx: number) => boolean;
-type FilterCb1<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, idx: number) => boolean;
-type FilterCb2<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, y: ParsedValue, idx: number) => boolean;
-type FilterCb<Ctx> = (this: CtxOrList<Ctx>, ...args: any) => boolean;
-type MapArrayCb0<Ctx> = (this: CtxOrList<Ctx>, idx: number) => any;
-type MapArrayCb1<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, idx: number) => any;
-type MapArrayCb2<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, y: ParsedValue, idx: number) => any;
-type MapArrayCb<Ctx> = (this: CtxOrList<Ctx>, ...args: any) => any;
-type MapCb1<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, idx: number) => ParsedValue | ParsedValue[];
-type MapCb2<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, y: ParsedValue, idx: number) =>
-    ParsedValue | ParsedValue[];
-type MapCb<Ctx> = (this: CtxOrList<Ctx>, ...args: any) => ParsedValue | ParsedValue[];
-
-
-const TRANSFERABLE_PROPERTIES = [
-    'hasItemOption', '_nameList', '_idList', '_invertedIndicesMap',
-    '_rawData', '_dimValueGetter',
-    '_count', '_rawCount', '_nameDimIdx', '_idDimIdx', '_nameRepeatCount'
-];
-const CLONE_PROPERTIES = [
-    '_extent', '_approximateExtent', '_rawExtent'
-];
-
-export interface DefaultDataVisual {
-    style: PathStyleProps
-    // Draw type determined which prop should be set with encoded color.
-    // It's only available on the global visual. Use getVisual('drawType') to access it.
-    // It will be set in visual/style.ts module in the first priority.
-    drawType: 'fill' | 'stroke'
-
-    symbol?: string
-    symbolSize?: number | number[]
-    symbolRotate?: number
-    symbolKeepAspect?: boolean
-    symbolOffset?: string | number | (string | number)[]
-
-    liftZ?: number
-    // For legend.
-    legendIcon?: string
-    legendLineStyle?: LineStyleProps
-
-    // visualMap will inject visualMeta data
-    visualMeta?: VisualMeta[]
-
-    // If color is encoded from palette
-    colorFromPalette?: boolean
-
-    decal?: DecalObject
-}
-
-export interface DataCalculationInfo<SERIES_MODEL> {
-    stackedDimension: string;
-    stackedByDimension: string;
-    isStackedByIndex: boolean;
-    stackedOverDimension: string;
-    stackResultDimension: string;
-    stackedOnSeries?: SERIES_MODEL;
-}
-
-// -----------------------------
-// Internal method declarations:
-// -----------------------------
-let defaultDimValueGetters: {[sourceFormat: string]: DimValueGetter};
-let prepareInvertedIndex: (list: List) => void;
-let getIndicesCtor: (list: List) => DataArrayLikeConstructor;
-let prepareStorage: (
-    storage: DataStorage, dimInfo: DataDimensionInfo, end: number, append?: boolean
-) => void;
-let getRawIndexWithoutIndices: (this: List, idx: number) => number;
-let getRawIndexWithIndices: (this: List, idx: number) => number;
-let getId: (list: List, rawIndex: number) => string;
-let getIdNameFromStore: (list: List, dimIdx: number, ordinalMeta: OrdinalMeta, rawIndex: number) => string;
-let makeIdFromName: (list: List, idx: number) => void;
-let normalizeDimensions: (dimensions: ItrParamDims) => Array<DimensionLoose>;
-let validateDimensions: (list: List, dims: DimensionName[]) => void;
-let cloneListForMapAndSample: (original: List, excludeDimensions: DimensionName[]) => List;
-let getInitialExtent: () => [number, number];
-let setItemDataAndSeriesIndex: (this: Element, child: Element) => void;
-let transferProperties: (target: List, source: List) => void;
-
-
-class List<
-    HostModel extends Model = Model,
-    Visual extends DefaultDataVisual = DefaultDataVisual
-> {
-
-    readonly type = 'list';
-
-    readonly dimensions: string[];
-
-    // Infomation of each data dimension, like data type.
-    private _dimensionInfos: {[dimName: string]: DataDimensionInfo};
-
-    readonly hostModel: HostModel;
-
-    /**
-     * @readonly
-     */
-    dataType: SeriesDataType;
-
-    /**
-     * @readonly
-     * Host graph if List is used to store graph nodes / edges.
-     */
-    graph?: Graph;
-
-    /**
-     * @readonly
-     * Host tree if List is used to store tree ndoes.
-     */
-    tree?: Tree;
-
-    // Indices stores the indices of data subset after filtered.
-    // This data subset will be used in chart.
-    private _indices: ArrayLike<any>;
-
-    private _count: number = 0;
-    private _rawCount: number = 0;
-    private _storage: DataStorage = {};
-    // We have an extra array store here. It's faster to be acessed than KV structured `_storage`.
-    // We profile the code `storage[dim]` and it seems to be KeyedLoadIC_Megamorphic instead of fast property access.
-    // Not sure why this happens. But using an extra array seems leads to faster `initData`
-    // See https://github.com/apache/incubator-echarts/pull/13314 for more explanation.
-    private _storageArr: DataValueChunk[] = [];
-    private _nameList: string[] = [];
-    private _idList: string[] = [];
-
-    // Models of data option is stored sparse for optimizing memory cost
-    // Never used yet (not used yet).
-    // private _optionModels: Model[] = [];
-
-    // Global visual properties after visual coding
-    private _visual: Dictionary<any> = {};
-
-    // Globel layout properties.
-    private _layout: Dictionary<any> = {};
-
-    // Item visual properties after visual coding
-    private _itemVisuals: Dictionary<any>[] = [];
-
-    // Item layout properties after layout
-    private _itemLayouts: any[] = [];
-
-    // Graphic elemnents
-    private _graphicEls: Element[] = [];
-
-    private _rawData: DataProvider;
-
-    // Raw extent will not be cloned, but only transfered.
-    // It will not be calculated util needed.
-    private _rawExtent: {[dimName: string]: [number, number]} = {};
-
-    private _extent: {[dimName: string]: [number, number]} = {};
-
-    // key: dim, value: extent
-    private _approximateExtent: {[dimName: string]: [number, number]} = {};
-
-    private _dimensionsSummary: DimensionSummary;
-
-    private _invertedIndicesMap: {[dimName: string]: ArrayLike<number>};
-
-    private _calculationInfo: DataCalculationInfo<HostModel> = {} as DataCalculationInfo<HostModel>;
-
-    // User output info of this data.
-    // DO NOT use it in other places!
-    // When preparing user params for user callbacks, we have
-    // to clone these inner data structures to prevent users
-    // from modifying them to effect built-in logic. And for
-    // performance consideration we make this `userOutput` to
-    // avoid clone them too many times.
-    readonly userOutput: DimensionUserOuput;
-
-    // Having detected that there is data item is non primitive type
-    // (in type `OptionDataItemObject`).
-    // Like `data: [ { value: xx, itemStyle: {...} }, ...]`
-    // At present it only happen in `SOURCE_FORMAT_ORIGINAL`.
-    hasItemOption: boolean = true;
-
-    // @readonly
-    defaultDimValueGetter: DimValueGetter;
-    private _dimValueGetter: DimValueGetter;
-    private _dimValueGetterArrayRows: DimValueGetter;
-
-    // id or name is used on dynamic data, mapping old and new items.
-    // When generating id from name, avoid repeat.
-    private _nameRepeatCount: NameRepeatCount;
-    private _nameDimIdx: number;
-    private _nameOrdinalMeta: OrdinalMeta;
-    private _idDimIdx: number;
-    private _idOrdinalMeta: OrdinalMeta;
-    private _dontMakeIdFromName: boolean;
-
-    private __wrappedMethods: string[];
-
-    // Methods that create a new list based on this list should be listed here.
-    // Notice that those method should `RETURN` the new list.
-    TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'lttbDownSample', 'map'] as const;
-    // Methods that change indices of this list should be listed here.
-    CHANGABLE_METHODS = ['filterSelf', 'selectRange'] as const;
-    DOWNSAMPLE_METHODS = ['downSample', 'lttbDownSample'] as const;
-
-    /**
-     * @param dimensions
-     *        For example, ['someDimName', {name: 'someDimName', type: 'someDimType'}, ...].
-     *        Dimensions should be concrete names like x, y, z, lng, lat, angle, radius
-     */
-    constructor(dimensions: Array<string | object | DataDimensionInfo>, hostModel: HostModel) {
-        dimensions = dimensions || ['x', 'y'];
-
-        const dimensionInfos: Dictionary<DataDimensionInfo> = {};
-        const dimensionNames = [];
-        const invertedIndicesMap: Dictionary<number[]> = {};
-
-        for (let i = 0; i < dimensions.length; i++) {
-            // Use the original dimensions[i], where other flag props may exists.
-            const dimInfoInput = dimensions[i];
-
-            const dimensionInfo: DataDimensionInfo =
-                zrUtil.isString(dimInfoInput)
-                ? new DataDimensionInfo({name: dimInfoInput})
-                : !(dimInfoInput instanceof DataDimensionInfo)
-                ? new DataDimensionInfo(dimInfoInput)
-                : dimInfoInput;
-
-            const dimensionName = dimensionInfo.name;
-            dimensionInfo.type = dimensionInfo.type || 'float';
-            if (!dimensionInfo.coordDim) {
-                dimensionInfo.coordDim = dimensionName;
-                dimensionInfo.coordDimIndex = 0;
-            }
-
-            const otherDims = dimensionInfo.otherDims = dimensionInfo.otherDims || {};
-            dimensionNames.push(dimensionName);
-            dimensionInfos[dimensionName] = dimensionInfo;
-
-            dimensionInfo.index = i;
-
-            if (dimensionInfo.createInvertedIndices) {
-                invertedIndicesMap[dimensionName] = [];
-            }
-            if (otherDims.itemName === 0) {
-                this._nameDimIdx = i;
-                this._nameOrdinalMeta = dimensionInfo.ordinalMeta;
-            }
-            if (otherDims.itemId === 0) {
-                this._idDimIdx = i;
-                this._idOrdinalMeta = dimensionInfo.ordinalMeta;
-            }
-        }
-
-        this.dimensions = dimensionNames;
-        this._dimensionInfos = dimensionInfos;
-        this.hostModel = hostModel;
-
-        // Cache summary info for fast visit. See "dimensionHelper".
-        this._dimensionsSummary = summarizeDimensions(this);
-
-        this._invertedIndicesMap = invertedIndicesMap;
-
-        this.userOutput = this._dimensionsSummary.userOutput;
-    }
-
-    /**
-     * The meanings of the input parameter `dim`:
-     *
-     * + If dim is a number (e.g., `1`), it means the index of the dimension.
-     *   For example, `getDimension(0)` will return 'x' or 'lng' or 'radius'.
-     * + If dim is a number-like string (e.g., `"1"`):
-     *     + If there is the same concrete dim name defined in `this.dimensions`, it means that concrete name.
-     *     + If not, it will be converted to a number, which means the index of the dimension.
-     *        (why? because of the backward compatbility. We have been tolerating number-like string in
-     *        dimension setting, although now it seems that it is not a good idea.)
-     *     For example, `visualMap[i].dimension: "1"` is the same meaning as `visualMap[i].dimension: 1`,
-     *     if no dimension name is defined as `"1"`.
-     * + If dim is a not-number-like string, it means the concrete dim name.
-     *   For example, it can be be default name `"x"`, `"y"`, `"z"`, `"lng"`, `"lat"`, `"angle"`, `"radius"`,
-     *   or customized in `dimensions` property of option like `"age"`.
-     *
-     * Get dimension name
-     * @param dim See above.
-     * @return Concrete dim name.
-     */
-    getDimension(dim: DimensionLoose): DimensionName {
-        if (typeof dim === 'number'
-            // If being a number-like string but not being defined a dimension name.
-            || (!isNaN(dim as any) && !this._dimensionInfos.hasOwnProperty(dim))
-        ) {
-            dim = this.dimensions[dim as DimensionIndex];
-        }
-        return dim as DimensionName;
-    }
-
-    /**
-     * Get type and calculation info of particular dimension
-     * @param dim
-     *        Dimension can be concrete names like x, y, z, lng, lat, angle, radius
-     *        Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'
-     */
-    getDimensionInfo(dim: DimensionLoose): DataDimensionInfo {
-        // Do not clone, because there may be categories in dimInfo.
-        return this._dimensionInfos[this.getDimension(dim)];
-    }
-
-    /**
-     * concrete dimension name list on coord.
-     */
-    getDimensionsOnCoord(): DimensionName[] {
-        return this._dimensionsSummary.dataDimsOnCoord.slice();
-    }
-
-    /**
-     * @param coordDim
-     * @param idx A coordDim may map to more than one data dim.
-     *        If not specified, return the first dim not extra.
-     * @return concrete data dim. If not found, return null/undefined
-     */
-    mapDimension(coordDim: DimensionName): DimensionName;
-    mapDimension(coordDim: DimensionName, idx: number): DimensionName;
-    mapDimension(coordDim: DimensionName, idx?: number): DimensionName {
-        const dimensionsSummary = this._dimensionsSummary;
-
-        if (idx == null) {
-            return dimensionsSummary.encodeFirstDimNotExtra[coordDim] as any;
-        }
-
-        const dims = dimensionsSummary.encode[coordDim];
-        return dims ? dims[idx as number] as any : null;
-    }
-
-    mapDimensionsAll(coordDim: DimensionName): DimensionName[] {
-        const dimensionsSummary = this._dimensionsSummary;
-        const dims = dimensionsSummary.encode[coordDim];
-        return (dims || []).slice();
-    }
-
-    /**
-     * Initialize from data
-     * @param data source or data or data provider.
-     * @param nameList The name of a datum is used on data diff and
-     *        default label/tooltip.
-     *        A name can be specified in encode.itemName,
-     *        or dataItem.name (only for series option data),
-     *        or provided in nameList from outside.
-     */
-    initData(
-        data: Source | OptionSourceData | DataProvider,
-        nameList?: string[],
-        dimValueGetter?: DimValueGetter
-    ): void {
-
-        const notProvider = isSourceInstance(data) || zrUtil.isArrayLike(data);
-        const provider: DataProvider = notProvider
-            ? new DefaultDataProvider(data as Source | OptionSourceData, this.dimensions.length)
-            : data as DataProvider;
-
-        if (__DEV__) {
-            zrUtil.assert(
-                notProvider || (
-                    zrUtil.isFunction(provider.getItem)
-                    && zrUtil.isFunction(provider.count)
-                ),
-                'Inavlid data provider.'
-            );
-        }
-
-        this._rawData = provider;
-        const sourceFormat = provider.getSource().sourceFormat;
-
-        // Clear
-        this._storage = {};
-        this._indices = null;
-        this._dontMakeIdFromName =
-            this._idDimIdx != null
-            || sourceFormat === SOURCE_FORMAT_TYPED_ARRAY // Cosndier performance.
-            || !!provider.fillStorage;
-
-        this._nameList = (nameList || []).slice();
-        this._idList = [];
-
-        this._nameRepeatCount = {};
-
-        if (!dimValueGetter) {
-            this.hasItemOption = false;
-        }
-
-        this.defaultDimValueGetter = defaultDimValueGetters[sourceFormat];
-        // Default dim value getter
-        this._dimValueGetter = dimValueGetter = dimValueGetter
-            || this.defaultDimValueGetter;
-        this._dimValueGetterArrayRows = defaultDimValueGetters.arrayRows;
-
-        // Reset raw extent.
-        this._rawExtent = {};
-
-        this._initDataFromProvider(0, provider.count());
-
-        // If data has no item option.
-        if (provider.pure) {
-            this.hasItemOption = false;
-        }
-    }
-
-    getProvider(): DataProvider {
-        return this._rawData;
-    }
-
-    /**
-     * Caution: Can be only called on raw data (before `this._indices` created).
-     */
-    appendData(data: ArrayLike<any>): void {
-        if (__DEV__) {
-            zrUtil.assert(!this._indices, 'appendData can only be called on raw data.');
-        }
-
-        const rawData = this._rawData;
-        const start = this.count();
-        rawData.appendData(data);
-        let end = rawData.count();
-        if (!rawData.persistent) {
-            end += start;
-        }
-        this._initDataFromProvider(start, end, true);
-    }
-
-    /**
-     * Caution: Can be only called on raw data (before `this._indices` created).
-     * This method does not modify `rawData` (`dataProvider`), but only
-     * add values to storage.
-     *
-     * The final count will be increased by `Math.max(values.length, names.length)`.
-     *
-     * @param values That is the SourceType: 'arrayRows', like
-     *        [
-     *            [12, 33, 44],
-     *            [NaN, 43, 1],
-     *            ['-', 'asdf', 0]
-     *        ]
-     *        Each item is exaclty cooresponding to a dimension.
-     */
-    appendValues(values: any[][], names?: string[]): void {
-        const storage = this._storage;
-        const dimensions = this.dimensions;
-        const dimLen = dimensions.length;
-        const rawExtent = this._rawExtent;
-
-        const start = this.count();
-        const end = start + Math.max(values.length, names ? names.length : 0);
-
-        for (let i = 0; i < dimLen; i++) {
-            const dim = dimensions[i];
-            if (!rawExtent[dim]) {
-                rawExtent[dim] = getInitialExtent();
-            }
-            prepareStorage(storage, this._dimensionInfos[dim], end, true);
-        }
-
-        const rawExtentArr = map(dimensions, (dim) => {
-            return rawExtent[dim];
-        });
-
-        const storageArr = this._storageArr = map(dimensions, (dim) => {
-            return storage[dim];
-        });
-
-        const emptyDataItem: number[] = [];
-        for (let idx = start; idx < end; idx++) {
-            const sourceIdx = idx - start;
-            // Store the data by dimensions
-            for (let dimIdx = 0; dimIdx < dimLen; dimIdx++) {
-                const dim = dimensions[dimIdx];
-                const val = this._dimValueGetterArrayRows(
-                    values[sourceIdx] || emptyDataItem, dim, sourceIdx, dimIdx
-                ) as ParsedValueNumeric;
-                storageArr[dimIdx][idx] = val;
-
-                const dimRawExtent = rawExtentArr[dimIdx];
-                val < dimRawExtent[0] && (dimRawExtent[0] = val);
-                val > dimRawExtent[1] && (dimRawExtent[1] = val);
-            }
-
-            if (names) {
-                this._nameList[idx] = names[sourceIdx];
-                if (!this._dontMakeIdFromName) {
-                    makeIdFromName(this, idx);
-                }
-            }
-        }
-
-        this._rawCount = this._count = end;
-
-        // Reset data extent
-        this._extent = {};
-
-        prepareInvertedIndex(this);
-    }
-
-    private _initDataFromProvider(start: number, end: number, append?: boolean): void {
-        if (start >= end) {
-            return;
-        }
-
-        const rawData = this._rawData;
-        const storage = this._storage;
-        const dimensions = this.dimensions;
-        const dimLen = dimensions.length;
-        const dimensionInfoMap = this._dimensionInfos;
-        const nameList = this._nameList;
-        const idList = this._idList;
-        const rawExtent = this._rawExtent;
-        const sourceFormat = rawData.getSource().sourceFormat;
-        const isFormatOriginal = sourceFormat === SOURCE_FORMAT_ORIGINAL;
-
-        for (let i = 0; i < dimLen; i++) {
-            const dim = dimensions[i];
-            if (!rawExtent[dim]) {
-                rawExtent[dim] = getInitialExtent();
-            }
-            prepareStorage(storage, dimensionInfoMap[dim], end, append);
-        }
-
-        const storageArr = this._storageArr = map(dimensions, (dim) => {
-            return storage[dim];
-        });
-
-        const rawExtentArr = map(dimensions, (dim) => {
-            return rawExtent[dim];
-        });
-
-        if (rawData.fillStorage) {
-            rawData.fillStorage(start, end, storageArr, rawExtentArr);
-        }
-        else {
-            let dataItem = [] as OptionDataItem;
-            for (let idx = start; idx < end; idx++) {
-                // NOTICE: Try not to write things into dataItem
-                dataItem = rawData.getItem(idx, dataItem);
-                // Each data item is value
-                // [1, 2]
-                // 2
-                // Bar chart, line chart which uses category axis
-                // only gives the 'y' value. 'x' value is the indices of category
-                // Use a tempValue to normalize the value to be a (x, y) value
-
-                // Store the data by dimensions
-                for (let dimIdx = 0; dimIdx < dimLen; dimIdx++) {
-                    const dim = dimensions[dimIdx];
-                    const dimStorage = storageArr[dimIdx];
-                    // PENDING NULL is empty or zero
-                    const val = this._dimValueGetter(dataItem, dim, idx, dimIdx) as ParsedValueNumeric;
-                    dimStorage[idx] = val;
-
-                    const dimRawExtent = rawExtentArr[dimIdx];
-                    val < dimRawExtent[0] && (dimRawExtent[0] = val);
-                    val > dimRawExtent[1] && (dimRawExtent[1] = val);
-                }
-
-                // If dataItem is {name: ...} or {id: ...}, it has highest priority.
-                // This kind of ids and names are always stored `_nameList` and `_idList`.
-                if (isFormatOriginal && !rawData.pure && dataItem) {
-                    const itemName = (dataItem as any).name;
-                    if (nameList[idx] == null && itemName != null) {
-                        nameList[idx] = convertOptionIdName(itemName, null);
-                    }
-                    const itemId = (dataItem as any).id;
-                    if (idList[idx] == null && itemId != null) {
-                        idList[idx] = convertOptionIdName(itemId, null);
-                    }
-                }
-
-                if (!this._dontMakeIdFromName) {
-                    makeIdFromName(this, idx);
-                }
-            }
-        }
-
-        if (!rawData.persistent && rawData.clean) {
-            // Clean unused data if data source is typed array.
-            rawData.clean();
-        }
-
-        this._rawCount = this._count = end;
-
-        // Reset data extent
-        this._extent = {};
-
-        prepareInvertedIndex(this);
-    }
-
-    count(): number {
-        return this._count;
-    }
-
-    getIndices(): ArrayLike<number> {
-        let newIndices;
-
-        const indices = this._indices;
-        if (indices) {
-            const Ctor = indices.constructor as DataArrayLikeConstructor;
-            const thisCount = this._count;
-            // `new Array(a, b, c)` is different from `new Uint32Array(a, b, c)`.
-            if (Ctor === Array) {
-                newIndices = new Ctor(thisCount);
-                for (let i = 0; i < thisCount; i++) {
-                    newIndices[i] = indices[i];
-                }
-            }
-            else {
-                newIndices = new (Ctor as DataTypedArrayConstructor)(
-                    (indices as DataTypedArray).buffer, 0, thisCount
-                );
-            }
-        }
-        else {
-            const Ctor = getIndicesCtor(this);
-            newIndices = new Ctor(this.count());
-            for (let i = 0; i < newIndices.length; i++) {
-                newIndices[i] = i;
-            }
-        }
-
-        return newIndices;
-    }
-
-    // Get data by index of dimension.
-    // Because in v8 access array by number variable is faster than access object by string variable
-    // Not sure why but the optimization just works.
-    getByDimIdx(dimIdx: number, idx: number): ParsedValue {
-        if (!(idx >= 0 && idx < this._count)) {
-            return NaN;
-        }
-        const dimStore = this._storageArr[dimIdx];
-        return dimStore ? dimStore[this.getRawIndex(idx)] : NaN;
-    }
-
-    /**
-     * Get value. Return NaN if idx is out of range.
-     * @param dim Dim must be concrete name.
-     */
-    get(dim: DimensionName, idx: number): ParsedValue {
-        if (!(idx >= 0 && idx < this._count)) {
-            return NaN;
-        }
-        const dimStore = this._storage[dim];
-        return dimStore ? dimStore[this.getRawIndex(idx)] : NaN;
-    }
-
-    /**
-     * @param dim concrete dim
-     */
-    getByRawIndex(dim: DimensionName, rawIdx: number): ParsedValue {
-        if (!(rawIdx >= 0 && rawIdx < this._rawCount)) {
-            return NaN;
-        }
-        const dimStore = this._storage[dim];
-        return dimStore ? dimStore[rawIdx] : NaN;
-    }
-
-    /**
-     * Get value for multi dimensions.
-     * @param dimensions If ignored, using all dimensions.
-     */
-    getValues(idx: number): ParsedValue[];
-    getValues(dimensions: readonly DimensionName[], idx: number): ParsedValue[];
-    getValues(dimensions: readonly DimensionName[] | number, idx?: number): ParsedValue[] {
-        const values = [];
-
-        if (!zrUtil.isArray(dimensions)) {
-            // stack = idx;
-            idx = dimensions as number;
-            dimensions = this.dimensions;
-        }
-
-        for (let i = 0, len = dimensions.length; i < len; i++) {
-            values.push(this.get(dimensions[i], idx /*, stack */));
-        }
-
-        return values;
-    }
-
-    /**
-     * If value is NaN. Inlcuding '-'
-     * Only check the coord dimensions.
-     */
-    hasValue(idx: number): boolean {
-        const dataDimsOnCoord = this._dimensionsSummary.dataDimsOnCoord;
-        for (let i = 0, len = dataDimsOnCoord.length; i < len; i++) {
-            // Ordinal type originally can be string or number.
-            // But when an ordinal type is used on coord, it can
-            // not be string but only number. So we can also use isNaN.
-            if (isNaN(this.get(dataDimsOnCoord[i], idx) as any)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Get extent of data in one dimension
-     */
-    getDataExtent(dim: DimensionLoose): [number, number] {
-        // Make sure use concrete dim as cache name.
-        dim = this.getDimension(dim);
-        const dimData = this._storage[dim];
-        const initialExtent = getInitialExtent();
-
-        // stack = !!((stack || false) && this.getCalculationInfo(dim));
-
-        if (!dimData) {
-            return initialExtent;
-        }
-
-        // Make more strict checkings to ensure hitting cache.
-        const currEnd = this.count();
-        // let cacheName = [dim, !!stack].join('_');
-        // let cacheName = dim;
-
-        // Consider the most cases when using data zoom, `getDataExtent`
-        // happened before filtering. We cache raw extent, which is not
-        // necessary to be cleared and recalculated when restore data.
-        const useRaw = !this._indices; // && !stack;
-        let dimExtent: [number, number];
-
-        if (useRaw) {
-            return this._rawExtent[dim].slice() as [number, number];
-        }
-        dimExtent = this._extent[dim];
-        if (dimExtent) {
-            return dimExtent.slice() as [number, number];
-        }
-        dimExtent = initialExtent;
-
-        let min = dimExtent[0];
-        let max = dimExtent[1];
-
-        for (let i = 0; i < currEnd; i++) {
-            const rawIdx = this.getRawIndex(i);
-            const value = dimData[rawIdx] as ParsedValueNumeric;
-            value < min && (min = value);
-            value > max && (max = value);
-        }
-
-        dimExtent = [min, max];
-
-        this._extent[dim] = dimExtent;
-
-        return dimExtent;
-    }
-
-    /**
-     * PENDING: In fact currently this function is only used to short-circuit
-     * the calling of `scale.unionExtentFromData` when data have been filtered by modules
-     * like "dataZoom". `scale.unionExtentFromData` is used to calculate data extent for series on
-     * an axis, but if a "axis related data filter module" is used, the extent of the axis have
-     * been fixed and no need to calling `scale.unionExtentFromData` actually.
-     * But if we add "custom data filter" in future, which is not "axis related", this method may
-     * be still needed.
-     *
-     * Optimize for the scenario that data is filtered by a given extent.
-     * Consider that if data amount is more than hundreds of thousand,
-     * extent calculation will cost more than 10ms and the cache will
-     * be erased because of the filtering.
-     */
-    getApproximateExtent(dim: DimensionLoose): [number, number] {
-        dim = this.getDimension(dim);
-        return this._approximateExtent[dim] || this.getDataExtent(dim);
-    }
-
-    /**
-     * Calculate extent on a filtered data might be time consuming.
-     * Approximate extent is only used for: calculte extent of filtered data outside.
-     */
-    setApproximateExtent(extent: [number, number], dim: DimensionLoose): void {
-        dim = this.getDimension(dim);
-        this._approximateExtent[dim] = extent.slice() as [number, number];
-    }
-
-    getCalculationInfo<CALC_INFO_KEY extends keyof DataCalculationInfo<HostModel>>(
-        key: CALC_INFO_KEY
-    ): DataCalculationInfo<HostModel>[CALC_INFO_KEY] {
-        return this._calculationInfo[key];
-    }
-
-    /**
-     * @param key or k-v object
-     */
-    setCalculationInfo(
-        key: DataCalculationInfo<HostModel>
-    ): void;
-    setCalculationInfo<CALC_INFO_KEY extends keyof DataCalculationInfo<HostModel>>(
-        key: CALC_INFO_KEY,
-        value: DataCalculationInfo<HostModel>[CALC_INFO_KEY]
-    ): void;
-    setCalculationInfo(
-        key: (keyof DataCalculationInfo<HostModel>) | DataCalculationInfo<HostModel>,
-        value?: DataCalculationInfo<HostModel>[keyof DataCalculationInfo<HostModel>]
-    ): void {
-        isObject(key)
-            ? zrUtil.extend(this._calculationInfo, key as object)
-            : ((this._calculationInfo as any)[key] = value);
-    }
-
-    /**
-     * Get sum of data in one dimension
-     */
-    getSum(dim: DimensionName): number {
-        const dimData = this._storage[dim];
-        let sum = 0;
-        if (dimData) {
-            for (let i = 0, len = this.count(); i < len; i++) {
-                const value = this.get(dim, i) as number;
-                if (!isNaN(value)) {
-                    sum += value;
-                }
-            }
-        }
-        return sum;
-    }
-
-    /**
-     * Get median of data in one dimension
-     */
-    getMedian(dim: DimensionLoose): number {
-        const dimDataArray: ParsedValue[] = [];
-        // map all data of one dimension
-        this.each(dim, function (val) {
-            if (!isNaN(val as number)) {
-                dimDataArray.push(val);
-            }
-        });
-
-        // TODO
-        // Use quick select?
-        const sortedDimDataArray = dimDataArray.sort(function (a: number, b: number) {
-            return a - b;
-        }) as number[];
-        const len = this.count();
-        // calculate median
-        return len === 0
-            ? 0
-            : len % 2 === 1
-            ? sortedDimDataArray[(len - 1) / 2]
-            : (sortedDimDataArray[len / 2] + sortedDimDataArray[len / 2 - 1]) / 2;
-    }
-
-    // /**
-    //  * Retreive the index with given value
-    //  * @param {string} dim Concrete dimension.
-    //  * @param {number} value
-    //  * @return {number}
-    //  */
-    // Currently incorrect: should return dataIndex but not rawIndex.
-    // Do not fix it until this method is to be used somewhere.
-    // FIXME Precision of float value
-    // indexOf(dim, value) {
-    //     let storage = this._storage;
-    //     let dimData = storage[dim];
-    //     let chunkSize = this._chunkSize;
-    //     if (dimData) {
-    //         for (let i = 0, len = this.count(); i < len; i++) {
-    //             let chunkIndex = mathFloor(i / chunkSize);
-    //             let chunkOffset = i % chunkSize;
-    //             if (dimData[chunkIndex][chunkOffset] === value) {
-    //                 return i;
-    //             }
-    //         }
-    //     }
-    //     return -1;
-    // }
-
-    /**
-     * Only support the dimension which inverted index created.
-     * Do not support other cases until required.
-     * @param dim concrete dim
-     * @param value ordinal index
-     * @return rawIndex
-     */
-    rawIndexOf(dim: DimensionName, value: OrdinalNumber): number {
-        const invertedIndices = dim && this._invertedIndicesMap[dim];
-        if (__DEV__) {
-            if (!invertedIndices) {
-                throw new Error('Do not supported yet');
-            }
-        }
-        const rawIndex = invertedIndices[value];
-        if (rawIndex == null || isNaN(rawIndex)) {
-            return INDEX_NOT_FOUND;
-        }
-        return rawIndex;
-    }
-
-    /**
-     * Retreive the index with given name
-     */
-    indexOfName(name: string): number {
-        for (let i = 0, len = this.count(); i < len; i++) {
-            if (this.getName(i) === name) {
-                return i;
-            }
-        }
-
-        return -1;
-    }
-
-    /**
-     * Retreive the index with given raw data index
-     */
-    indexOfRawIndex(rawIndex: number): number {
-        if (rawIndex >= this._rawCount || rawIndex < 0) {
-            return -1;
-        }
-
-        if (!this._indices) {
-            return rawIndex;
-        }
-
-        // Indices are ascending
-        const indices = this._indices;
-
-        // If rawIndex === dataIndex
-        const rawDataIndex = indices[rawIndex];
-        if (rawDataIndex != null && rawDataIndex < this._count && rawDataIndex === rawIndex) {
-            return rawIndex;
-        }
-
-        let left = 0;
-        let right = this._count - 1;
-        while (left <= right) {
-            const mid = (left + right) / 2 | 0;
-            if (indices[mid] < rawIndex) {
-                left = mid + 1;
-            }
-            else if (indices[mid] > rawIndex) {
-                right = mid - 1;
-            }
-            else {
-                return mid;
-            }
-        }
-        return -1;
-    }
-
-    /**
-     * Retreive the index of nearest value
-     * @param dim
-     * @param value
-     * @param [maxDistance=Infinity]
-     * @return If and only if multiple indices has
-     *         the same value, they are put to the result.
-     */
-    indicesOfNearest(
-        dim: DimensionName, value: number, maxDistance?: number
-    ): number[] {
-        const storage = this._storage;
-        const dimData = storage[dim];
-        const nearestIndices: number[] = [];
-
-        if (!dimData) {
-            return nearestIndices;
-        }
-
-        if (maxDistance == null) {
-            maxDistance = Infinity;
-        }
-
-        let minDist = Infinity;
-        let minDiff = -1;
-        let nearestIndicesLen = 0;
-
-
-        // Check the test case of `test/ut/spec/data/List.js`.
-        for (let i = 0, len = this.count(); i < len; i++) {
-            const dataIndex = this.getRawIndex(i);
-            const diff = value - (dimData[dataIndex] as number);
-            const dist = Math.abs(diff);
-            if (dist <= maxDistance) {
-                // When the `value` is at the middle of `this.get(dim, i)` and `this.get(dim, i+1)`,
-                // we'd better not push both of them to `nearestIndices`, otherwise it is easy to
-                // get more than one item in `nearestIndices` (more specifically, in `tooltip`).
-                // So we chose the one that `diff >= 0` in this csae.
-                // But if `this.get(dim, i)` and `this.get(dim, j)` get the same value, both of them
-                // should be push to `nearestIndices`.
-                if (dist < minDist
-                    || (dist === minDist && diff >= 0 && minDiff < 0)
-                ) {
-                    minDist = dist;
-                    minDiff = diff;
-                    nearestIndicesLen = 0;
-                }
-                if (diff === minDiff) {
-                    nearestIndices[nearestIndicesLen++] = i;
-                }
-            }
-        }
-        nearestIndices.length = nearestIndicesLen;
-
-        return nearestIndices;
-    }
-
-    /**
-     * Get raw data index.
-     * Do not initialize.
-     * Default `getRawIndex`. And it can be changed.
-     */
-    getRawIndex: (idx: number) => number = getRawIndexWithoutIndices;
-
-    /**
-     * Get raw data item
-     */
-    getRawDataItem(idx: number): OptionDataItem {
-        if (!this._rawData.persistent) {
-            const val = [];
-            for (let i = 0; i < this.dimensions.length; i++) {
-                const dim = this.dimensions[i];
-                val.push(this.get(dim, idx));
-            }
-            return val;
-        }
-        else {
-            return this._rawData.getItem(this.getRawIndex(idx));
-        }
-    }
-
-    /**
-     * @return Never be null/undefined. `number` will be converted to string. Becuase:
-     * In most cases, name is used in display, where returning a string is more convenient.
-     * In other cases, name is used in query (see `indexOfName`), where we can keep the
-     * rule that name `2` equals to name `'2'`.
-     */
-    getName(idx: number): string {
-        const rawIndex = this.getRawIndex(idx);
-        let name = this._nameList[rawIndex];
-        if (name == null && this._nameDimIdx != null) {
-            name = getIdNameFromStore(this, this._nameDimIdx, this._nameOrdinalMeta, rawIndex);
-        }
-        if (name == null) {
-            name = '';
-        }
-        return name;
-    }
-
-    /**
-     * @return Never null/undefined. `number` will be converted to string. Becuase:
-     * In all cases having encountered at present, id is used in making diff comparison, which
-     * are usually based on hash map. We can keep the rule that the internal id are always string
-     * (treat `2` is the same as `'2'`) to make the related logic simple.
-     */
-    getId(idx: number): string {
-        return getId(this, this.getRawIndex(idx));
-    }
-
-    /**
-     * Data iteration
-     * @param ctx default this
-     * @example
-     *  list.each('x', function (x, idx) {});
-     *  list.each(['x', 'y'], function (x, y, idx) {});
-     *  list.each(function (idx) {})
-     */
-    each<Ctx>(cb: EachCb0<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): void;
-    each<Ctx>(dims: DimensionLoose, cb: EachCb1<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): void;
-    each<Ctx>(dims: [DimensionLoose], cb: EachCb1<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): void;
-    each<Ctx>(dims: [DimensionLoose, DimensionLoose], cb: EachCb2<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): void;
-    each<Ctx>(dims: ItrParamDims, cb: EachCb<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): void;
-    each<Ctx>(
-        dims: ItrParamDims | EachCb<Ctx>,
-        cb: EachCb<Ctx> | Ctx,
-        ctx?: Ctx,
-        ctxCompat?: Ctx
-    ): void {
-        'use strict';
-
-        if (!this._count) {
-            return;
-        }
-
-        if (typeof dims === 'function') {
-            ctxCompat = ctx;
-            ctx = cb as Ctx;
-            cb = dims;
-            dims = [];
-        }
-
-        // ctxCompat just for compat echarts3
-        const fCtx = (ctx || ctxCompat || this) as CtxOrList<Ctx>;
-
-        const dimNames = map(normalizeDimensions(dims), this.getDimension, this);
-
-        if (__DEV__) {
-            validateDimensions(this, dimNames);
-        }
-
-        const dimSize = dimNames.length;
-        const dimIndices = map(dimNames, (dimName) => {
-            return this._dimensionInfos[dimName].index;
-        });
-        const storageArr = this._storageArr;
-
-        for (let i = 0, len = this.count(); i < len; i++) {
-            const rawIdx = this.getRawIndex(i);
-            // Simple optimization
-            switch (dimSize) {
-                case 0:
-                    (cb as EachCb0<Ctx>).call(fCtx, i);
-                    break;
-                case 1:
-                    (cb as EachCb1<Ctx>).call(fCtx, storageArr[dimIndices[0]][rawIdx], i);
-                    break;
-                case 2:
-                    (cb as EachCb2<Ctx>).call(
-                        fCtx, storageArr[dimIndices[0]][rawIdx], storageArr[dimIndices[1]][rawIdx], i
-                    );
-                    break;
-                default:
-                    let k = 0;
-                    const value = [];
-                    for (; k < dimSize; k++) {
-                        value[k] = storageArr[dimIndices[k]][rawIdx];
-                    }
-                    // Index
-                    value[k] = i;
-                    (cb as EachCb<Ctx>).apply(fCtx, value);
-            }
-        }
-    }
-
-    /**
-     * Data filter
-     */
-    filterSelf<Ctx>(cb: FilterCb0<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): this;
-    filterSelf<Ctx>(dims: DimensionLoose, cb: FilterCb1<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): this;
-    filterSelf<Ctx>(dims: [DimensionLoose], cb: FilterCb1<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): this;
-    filterSelf<Ctx>(dims: [DimensionLoose, DimensionLoose], cb: FilterCb2<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): this;
-    filterSelf<Ctx>(dims: ItrParamDims, cb: FilterCb<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): this;
-    filterSelf<Ctx>(
-        dims: ItrParamDims | FilterCb<Ctx>,
-        cb: FilterCb<Ctx> | Ctx,
-        ctx?: Ctx,
-        ctxCompat?: Ctx
-    ): List {
-        'use strict';
-
-        if (!this._count) {
-            return;
-        }
-
-        if (typeof dims === 'function') {
-            ctxCompat = ctx;
-            ctx = cb as Ctx;
-            cb = dims;
-            dims = [];
-        }
-
-        // ctxCompat just for compat echarts3
-        const fCtx = (ctx || ctxCompat || this) as CtxOrList<Ctx>;
-
-        const dimNames = map(
-            normalizeDimensions(dims), this.getDimension, this
-        );
-
-        if (__DEV__) {
-            validateDimensions(this, dimNames);
-        }
-
-
-        const count = this.count();
-        const Ctor = getIndicesCtor(this);
-        const newIndices = new Ctor(count);
-        const value = [];
-        const dimSize = dimNames.length;
-
-        let offset = 0;
-        const dimIndices = map(dimNames, (dimName) => {
-            return this._dimensionInfos[dimName].index;
-        });
-        const dim0 = dimIndices[0];
-        const storageArr = this._storageArr;
-
-        for (let i = 0; i < count; i++) {
-            let keep;
-            const rawIdx = this.getRawIndex(i);
-            // Simple optimization
-            if (dimSize === 0) {
-                keep = (cb as FilterCb0<Ctx>).call(fCtx, i);
-            }
-            else if (dimSize === 1) {
-                const val = storageArr[dim0][rawIdx];
-                keep = (cb as FilterCb1<Ctx>).call(fCtx, val, i);
-            }
-            else {
-                let k = 0;
-                for (; k < dimSize; k++) {
-                    value[k] = storageArr[dimIndices[k]][rawIdx];
-                }
-                value[k] = i;
-                keep = (cb as FilterCb<Ctx>).apply(fCtx, value);
-            }
-            if (keep) {
-                newIndices[offset++] = rawIdx;
-            }
-        }
-
-        // Set indices after filtered.
-        if (offset < count) {
-            this._indices = newIndices;
-        }
-        this._count = offset;
-        // Reset data extent
-        this._extent = {};
-
-        this.getRawIndex = this._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
-
-        return this;
-    }
-
-    /**
-     * Select data in range. (For optimization of filter)
-     * (Manually inline code, support 5 million data filtering in data zoom.)
-     */
-    selectRange(range: {[dimName: string]: [number, number]}): List {
-        'use strict';
-
-        const len = this._count;
-
-        if (!len) {
-            return;
-        }
-
-        const dimensions = [];
-        for (const dim in range) {
-            if (range.hasOwnProperty(dim)) {
-                dimensions.push(dim);
-            }
-        }
-
-        if (__DEV__) {
-            validateDimensions(this, dimensions);
-        }
-
-        const dimSize = dimensions.length;
-        if (!dimSize) {
-            return;
-        }
-
-        const originalCount = this.count();
-        const Ctor = getIndicesCtor(this);
-        const newIndices = new Ctor(originalCount);
-
-        let offset = 0;
-        const dim0 = dimensions[0];
-        const dimIndices = map(dimensions, (dimName) => {
-            return this._dimensionInfos[dimName].index;
-        });
-
-        const min = range[dim0][0];
-        const max = range[dim0][1];
-        const storageArr = this._storageArr;
-
-        let quickFinished = false;
-        if (!this._indices) {
-            // Extreme optimization for common case. About 2x faster in chrome.
-            let idx = 0;
-            if (dimSize === 1) {
-                const dimStorage = storageArr[dimIndices[0]];
-                for (let i = 0; i < len; i++) {
-                    const val = dimStorage[i];
-                    // NaN will not be filtered. Consider the case, in line chart, empty
-                    // value indicates the line should be broken. But for the case like
-                    // scatter plot, a data item with empty value will not be rendered,
-                    // but the axis extent may be effected if some other dim of the data
-                    // item has value. Fortunately it is not a significant negative effect.
-                    if (
-                        (val >= min && val <= max) || isNaN(val as any)
-                    ) {
-                        newIndices[offset++] = idx;
-                    }
-                    idx++;
-                }
-                quickFinished = true;
-            }
-            else if (dimSize === 2) {
-                const dimStorage = storageArr[dimIndices[0]];
-                const dimStorage2 = storageArr[dimIndices[1]];
-                const min2 = range[dimensions[1]][0];
-                const max2 = range[dimensions[1]][1];
-                for (let i = 0; i < len; i++) {
-                    const val = dimStorage[i];
-                    const val2 = dimStorage2[i];
-                    // Do not filter NaN, see comment above.
-                    if ((
-                            (val >= min && val <= max) || isNaN(val as any)
-                        )
-                        && (
-                            (val2 >= min2 && val2 <= max2) || isNaN(val2 as any)
-                        )
-                    ) {
-                        newIndices[offset++] = idx;
-                    }
-                    idx++;
-                }
-                quickFinished = true;
-            }
-        }
-        if (!quickFinished) {
-            if (dimSize === 1) {
-                for (let i = 0; i < originalCount; i++) {
-                    const rawIndex = this.getRawIndex(i);
-                    const val = storageArr[dimIndices[0]][rawIndex];
-                    // Do not filter NaN, see comment above.
-                    if (
-                        (val >= min && val <= max) || isNaN(val as any)
-                    ) {
-                        newIndices[offset++] = rawIndex;
-                    }
-                }
-            }
-            else {
-                for (let i = 0; i < originalCount; i++) {
-                    let keep = true;
-                    const rawIndex = this.getRawIndex(i);
-                    for (let k = 0; k < dimSize; k++) {
-                        const dimk = dimensions[k];
-                        const val = storageArr[dimIndices[k]][rawIndex];
-                        // Do not filter NaN, see comment above.
-                        if (val < range[dimk][0] || val > range[dimk][1]) {
-                            keep = false;
-                        }
-                    }
-                    if (keep) {
-                        newIndices[offset++] = this.getRawIndex(i);
-                    }
-                }
-            }
-        }
-
-        // Set indices after filtered.
-        if (offset < originalCount) {
-            this._indices = newIndices;
-        }
-        this._count = offset;
-        // Reset data extent
-        this._extent = {};
-
-        this.getRawIndex = this._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
-
-        return this;
-    }
-
-    /**
-     * Data mapping to a plain array
-     */
-    mapArray<Ctx, Cb extends MapArrayCb0<Ctx>>(cb: Cb, ctx?: Ctx, ctxCompat?: Ctx): ReturnType<Cb>[];
-    /* eslint-disable */
-    mapArray<Ctx, Cb extends MapArrayCb1<Ctx>>(dims: DimensionLoose, cb: Cb, ctx?: Ctx, ctxCompat?: Ctx): ReturnType<Cb>[];
-    mapArray<Ctx, Cb extends MapArrayCb1<Ctx>>(dims: [DimensionLoose], cb: Cb, ctx?: Ctx, ctxCompat?: Ctx): ReturnType<Cb>[];
-    mapArray<Ctx, Cb extends MapArrayCb2<Ctx>>(dims: [DimensionLoose, DimensionLoose], cb: Cb, ctx?: Ctx, ctxCompat?: Ctx): ReturnType<Cb>[];
-    mapArray<Ctx, Cb extends MapArrayCb<Ctx>>(dims: ItrParamDims, cb: Cb, ctx?: Ctx, ctxCompat?: Ctx): ReturnType<Cb>[];
-    /* eslint-enable */
-    mapArray<Ctx>(
-        dims: ItrParamDims | MapArrayCb<Ctx>,
-        cb: MapArrayCb<Ctx> | Ctx,
-        ctx?: Ctx,
-        ctxCompat?: Ctx
-    ): any[] {
-        'use strict';
-
-        if (typeof dims === 'function') {
-            ctxCompat = ctx;
-            ctx = cb as Ctx;
-            cb = dims;
-            dims = [];
-        }
-
-        // ctxCompat just for compat echarts3
-        ctx = (ctx || ctxCompat || this) as Ctx;
-
-        const result: any[] = [];
-        this.each(dims, function () {
-            result.push(cb && (cb as MapArrayCb<Ctx>).apply(this, arguments));
-        }, ctx);
-        return result;
-    }
-
-    /**
-     * Data mapping to a new List with given dimensions
-     */
-    map<Ctx>(dims: DimensionLoose, cb: MapCb1<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): List<HostModel>;
-    map<Ctx>(dims: [DimensionLoose], cb: MapCb1<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): List<HostModel>;
-    map<Ctx>(dims: [DimensionLoose, DimensionLoose], cb: MapCb2<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): List<HostModel>;
-    map<Ctx>(
-        dims: ItrParamDims,
-        cb: MapCb<Ctx>,
-        ctx?: Ctx,
-        ctxCompat?: Ctx
-    ): List {
-        'use strict';
-
-        // ctxCompat just for compat echarts3
-        const fCtx = (ctx || ctxCompat || this) as CtxOrList<Ctx>;
-
-        const dimNames = map(
-            normalizeDimensions(dims), this.getDimension, this
-        );
-
-        if (__DEV__) {
-            validateDimensions(this, dimNames);
-        }
-
-        const list = cloneListForMapAndSample(this, dimNames);
-        const storage = list._storage;
-
-        // Following properties are all immutable.
-        // So we can reference to the same value
-        list._indices = this._indices;
-        list.getRawIndex = list._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
-
-        const tmpRetValue = [];
-        const dimSize = dimNames.length;
-        const dataCount = this.count();
-        const values = [];
-        const rawExtent = list._rawExtent;
-
-        for (let dataIndex = 0; dataIndex < dataCount; dataIndex++) {
-            for (let dimIndex = 0; dimIndex < dimSize; dimIndex++) {
-                values[dimIndex] = this.get(dimNames[dimIndex], dataIndex);
-            }
-            values[dimSize] = dataIndex;
-
-            let retValue = cb && cb.apply(fCtx, values);
-            if (retValue != null) {
-                // a number or string (in oridinal dimension)?
-                if (typeof retValue !== 'object') {
-                    tmpRetValue[0] = retValue;
-                    retValue = tmpRetValue;
-                }
-
-                const rawIndex = this.getRawIndex(dataIndex);
-
-                for (let i = 0; i < retValue.length; i++) {
-                    const dim = dimNames[i];
-                    const val = retValue[i];
-                    const rawExtentOnDim = rawExtent[dim];
-
-                    const dimStore = storage[dim];
-                    if (dimStore) {
-                        dimStore[rawIndex] = val;
-                    }
-
-                    if (val < rawExtentOnDim[0]) {
-                        rawExtentOnDim[0] = val as number;
-                    }
-                    if (val > rawExtentOnDim[1]) {
-                        rawExtentOnDim[1] = val as number;
-                    }
-                }
-            }
-        }
-
-        return list;
-    }
-
-    /**
-     * Large data down sampling on given dimension
-     * @param sampleIndex Sample index for name and id
-     */
-    downSample(
-        dimension: DimensionName,
-        rate: number,
-        sampleValue: (frameValues: ArrayLike<ParsedValue>) => ParsedValueNumeric,
-        sampleIndex: (frameValues: ArrayLike<ParsedValue>, value: ParsedValueNumeric) => number
-    ): List<HostModel> {
-        const list = cloneListForMapAndSample(this, [dimension]);
-        const targetStorage = list._storage;
-
-        const frameValues = [];
-        let frameSize = mathFloor(1 / rate);
-
-        const dimStore = targetStorage[dimension];
-        const len = this.count();
-        const rawExtentOnDim = list._rawExtent[dimension];
-
-        const newIndices = new (getIndicesCtor(this))(len);
-
-        let offset = 0;
-        for (let i = 0; i < len; i += frameSize) {
-            // Last frame
-            if (frameSize > len - i) {
-                frameSize = len - i;
-                frameValues.length = frameSize;
-            }
-            for (let k = 0; k < frameSize; k++) {
-                const dataIdx = this.getRawIndex(i + k);
-                frameValues[k] = dimStore[dataIdx];
-            }
-            const value = sampleValue(frameValues);
-            const sampleFrameIdx = this.getRawIndex(
-                Math.min(i + sampleIndex(frameValues, value) || 0, len - 1)
-            );
-            // Only write value on the filtered data
-            dimStore[sampleFrameIdx] = value;
-
-            if (value < rawExtentOnDim[0]) {
-                rawExtentOnDim[0] = value;
-            }
-            if (value > rawExtentOnDim[1]) {
-                rawExtentOnDim[1] = value;
-            }
-
-            newIndices[offset++] = sampleFrameIdx;
-        }
-
-        list._count = offset;
-        list._indices = newIndices;
-
-        list.getRawIndex = getRawIndexWithIndices;
-
-        return list as List<HostModel>;
-    }
-
-    /**
-     * Large data down sampling using largest-triangle-three-buckets
-     * @param {string} valueDimension
-     * @param {number} targetCount
-     */
-    lttbDownSample(
-        valueDimension: DimensionName,
-        rate: number
-    ) {
-        const list = cloneListForMapAndSample(this, []);
-        const targetStorage = list._storage;
-        const dimStore = targetStorage[valueDimension];
-        const len = this.count();
-        const newIndices = new (getIndicesCtor(this))(len);
-
-        let sampledIndex = 0;
-
-        const frameSize = mathFloor(1 / rate);
-
-        let currentRawIndex = this.getRawIndex(0);
-        let maxArea;
-        let area;
-        let nextRawIndex;
-
-        // First frame use the first data.
-        newIndices[sampledIndex++] = currentRawIndex;
-        for (let i = 1; i < len - 1; i += frameSize) {
-            const nextFrameStart = Math.min(i + frameSize, len - 1);
-            const nextFrameEnd = Math.min(i + frameSize * 2, len);
-
-            const avgX = (nextFrameEnd + nextFrameStart) / 2;
-            let avgY = 0;
-
-            for (let idx = nextFrameStart; idx < nextFrameEnd; idx++) {
-                const rawIndex = this.getRawIndex(idx);
-                const y = dimStore[rawIndex] as number;
-                if (isNaN(y)) {
-                    continue;
-                }
-                avgY += y as number;
-            }
-            avgY /= (nextFrameEnd - nextFrameStart);
-
-            const frameStart = i;
-            const frameEnd = Math.min(i + frameSize, len);
-
-            const pointAX = i - 1;
-            const pointAY = dimStore[currentRawIndex] as number;
-
-            maxArea = -1;
-
-            nextRawIndex = frameStart;
-            // Find a point from current frame that construct a triangel with largest area with previous selected point
-            // And the average of next frame.
-            for (let idx = frameStart; idx < frameEnd; idx++) {
-                const rawIndex = this.getRawIndex(idx);
-                const y = dimStore[rawIndex] as number;
-                if (isNaN(y)) {
-                    continue;
-                }
-                // Calculate triangle area over three buckets
-                area = Math.abs((pointAX - avgX) * (y - pointAY)
-                    - (pointAX - idx) * (avgY - pointAY)
-                );
-                if (area > maxArea) {
-                    maxArea = area;
-                    nextRawIndex = rawIndex; // Next a is this b
-                }
-            }
-
-            newIndices[sampledIndex++] = nextRawIndex;
-
-            currentRawIndex = nextRawIndex; // This a is the next a (chosen b)
-        }
-
-        // First frame use the last data.
-        newIndices[sampledIndex++] = this.getRawIndex(len - 1);
-        list._count = sampledIndex;
-        list._indices = newIndices;
-
-        list.getRawIndex = getRawIndexWithIndices;
-        return list;
-    }
-
-
-    /**
-     * Get model of one data item.
-     */
-    // TODO: Type of data item
-    getItemModel<ItemOpts extends unknown = unknown>(idx: number): Model<ItemOpts
-        // Extract item option with value key. FIXME will cause incompatitable issue
-        // Extract<HostModel['option']['data'][number], { value?: any }>
-    > {
-        const hostModel = this.hostModel;
-        const dataItem = this.getRawDataItem(idx) as ModelOption;
-        return new Model(dataItem, hostModel, hostModel && hostModel.ecModel);
-    }
-
-    /**
-     * Create a data differ
-     */
-    diff(otherList: List): DataDiffer {
-        const thisList = this;
-
-        return new DataDiffer(
-            otherList ? otherList.getIndices() : [],
-            this.getIndices(),
-            function (idx: number) {
-                return getId(otherList, idx);
-            },
-            function (idx: number) {
-                return getId(thisList, idx);
-            }
-        );
-    }
-
-    /**
-     * Get visual property.
-     */
-    getVisual<K extends keyof Visual>(key: K): Visual[K] {
-        const visual = this._visual as Visual;
-        return visual && visual[key];
-    }
-
-    /**
-     * Set visual property
-     *
-     * @example
-     *  setVisual('color', color);
-     *  setVisual({
-     *      'color': color
-     *  });
-     */
-    setVisual<K extends keyof Visual>(key: K, val: Visual[K]): void;
-    setVisual(kvObj: Partial<Visual>): void;
-    setVisual(kvObj: string | Partial<Visual>, val?: any): void {
-        this._visual = this._visual || {};
-        if (isObject(kvObj)) {
-            zrUtil.extend(this._visual, kvObj);
-        }
-        else {
-            this._visual[kvObj as string] = val;
-        }
-    }
-
-    /**
-     * Get visual property of single data item
-     */
-    // eslint-disable-next-line
-    getItemVisual<K extends keyof Visual>(idx: number, key: K): Visual[K] {
-        const itemVisual = this._itemVisuals[idx] as Visual;
-        const val = itemVisual && itemVisual[key];
-        if (val == null) {
-            // Use global visual property
-            return this.getVisual(key);
-        }
-        return val;
-    }
-
-    /**
-     * If exists visual property of single data item
-     */
-    hasItemVisual() {
-        return this._itemVisuals.length > 0;
-    }
-
-    /**
-     * Make sure itemVisual property is unique
-     */
-    // TODO: use key to save visual to reduce memory.
-    ensureUniqueItemVisual<K extends keyof Visual>(idx: number, key: K): Visual[K] {
-        const itemVisuals = this._itemVisuals;
-        let itemVisual = itemVisuals[idx] as Visual;
-        if (!itemVisual) {
-            itemVisual = itemVisuals[idx] = {} as Visual;
-        }
-        let val = itemVisual[key];
-        if (val == null) {
-            val = this.getVisual(key);
-
-            // TODO Performance?
-            if (zrUtil.isArray(val)) {
-                val = val.slice() as unknown as Visual[K];
-            }
-            else if (isObject(val)) {
-                val = zrUtil.extend({}, val);
-            }
-
-            itemVisual[key] = val;
-        }
-        return val;
-    }
-    /**
-     * Set visual property of single data item
-     *
-     * @param {number} idx
-     * @param {string|Object} key
-     * @param {*} [value]
-     *
-     * @example
-     *  setItemVisual(0, 'color', color);
-     *  setItemVisual(0, {
-     *      'color': color
-     *  });
-     */
-    // eslint-disable-next-line
-    setItemVisual<K extends keyof Visual>(idx: number, key: K, value: Visual[K]): void;
-    setItemVisual(idx: number, kvObject: Partial<Visual>): void;
-    // eslint-disable-next-line
-    setItemVisual<K extends keyof Visual>(idx: number, key: K | Partial<Visual>, value?: Visual[K]): void {
-        const itemVisual = this._itemVisuals[idx] || {};
-        this._itemVisuals[idx] = itemVisual;
-
-        if (isObject(key)) {
-            zrUtil.extend(itemVisual, key);
-        }
-        else {
-            itemVisual[key as string] = value;
-        }
-    }
-
-    /**
-     * Clear itemVisuals and list visual.
-     */
-    clearAllVisual(): void {
-        this._visual = {};
-        this._itemVisuals = [];
-    }
-
-    /**
-     * Set layout property.
-     */
-    setLayout(key: string, val: any): void;
-    setLayout(kvObj: Dictionary<any>): void;
-    setLayout(key: string | Dictionary<any>, val?: any): void {
-        if (isObject(key)) {
-            for (const name in key) {
-                if (key.hasOwnProperty(name)) {
-                    this.setLayout(name, key[name]);
-                }
-            }
-            return;
-        }
-        this._layout[key] = val;
-    }
-
-    /**
-     * Get layout property.
-     */
-    getLayout(key: string): any {
-        return this._layout[key];
-    }
-
-    /**
-     * Get layout of single data item
-     */
-    getItemLayout(idx: number): any {
-        return this._itemLayouts[idx];
-    }
-
-    /**
-     * Set layout of single data item
-     */
-    setItemLayout<M = false>(
-        idx: number,
-        layout: (M extends true ? Dictionary<any> : any),
-        merge?: M
-    ): void {
-        this._itemLayouts[idx] = merge
-            ? zrUtil.extend(this._itemLayouts[idx] || {}, layout)
-            : layout;
-    }
-
-    /**
-     * Clear all layout of single data item
-     */
-    clearItemLayouts(): void {
-        this._itemLayouts.length = 0;
-    }
-
-    /**
-     * Set graphic element relative to data. It can be set as null
-     */
-    setItemGraphicEl(idx: number, el: Element): void {
-        const hostModel = this.hostModel;
-
-        if (el) {
-            const ecData = getECData(el);
-            // Add data index and series index for indexing the data by element
-            // Useful in tooltip
-            ecData.dataIndex = idx;
-            ecData.dataType = this.dataType;
-            ecData.seriesIndex = hostModel && (hostModel as any).seriesIndex;
-
-            // TODO: not store dataIndex on children.
-            if (el.type === 'group') {
-                el.traverse(setItemDataAndSeriesIndex, el);
-            }
-        }
-
-        this._graphicEls[idx] = el;
-    }
-
-    getItemGraphicEl(idx: number): Element {
-        return this._graphicEls[idx];
-    }
-
-    eachItemGraphicEl<Ctx = unknown>(
-        cb: (this: Ctx, el: Element, idx: number) => void,
-        context?: Ctx
-    ): void {
-        zrUtil.each(this._graphicEls, function (el, idx) {
-            if (el) {
-                cb && cb.call(context, el, idx);
-            }
-        });
-    }
-
-    /**
-     * Shallow clone a new list except visual and layout properties, and graph elements.
-     * New list only change the indices.
-     */
-    cloneShallow(list?: List<HostModel>): List<HostModel> {
-        if (!list) {
-            const dimensionInfoList = map(this.dimensions, this.getDimensionInfo, this);
-            list = new List(dimensionInfoList, this.hostModel);
-        }
-
-        // FIXME
-        list._storage = this._storage;
-        list._storageArr = this._storageArr;
-
-        transferProperties(list, this);
-
-        // Clone will not change the data extent and indices
-        if (this._indices) {
-            const Ctor = this._indices.constructor as DataArrayLikeConstructor;
-            if (Ctor === Array) {
-                const thisCount = this._indices.length;
-                list._indices = new Ctor(thisCount);
-                for (let i = 0; i < thisCount; i++) {
-                    list._indices[i] = this._indices[i];
-                }
-            }
-            else {
-                list._indices = new (Ctor as DataTypedArrayConstructor)(this._indices);
-            }
-        }
-        else {
-            list._indices = null;
-        }
-        list.getRawIndex = list._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
-
-        return list;
-    }
-
-    /**
-     * Wrap some method to add more feature
-     */
-    wrapMethod(
-        methodName: FunctionPropertyNames<List>,
-        injectFunction: (...args: any) => any
-    ): void {
-        const originalMethod = this[methodName];
-        if (typeof originalMethod !== 'function') {
-            return;
-        }
-        this.__wrappedMethods = this.__wrappedMethods || [];
-        this.__wrappedMethods.push(methodName);
-        this[methodName] = function () {
-            const res = (originalMethod as any).apply(this, arguments);
-            return injectFunction.apply(this, [res].concat(zrUtil.slice(arguments)));
-        };
-    }
-
-
-    // ----------------------------------------------------------
-    // A work around for internal method visiting private member.
-    // ----------------------------------------------------------
-    private static internalField = (function () {
-
-        defaultDimValueGetters = {
-
-            arrayRows: getDimValueSimply,
-
-            objectRows: function (
-                this: List, dataItem: Dictionary<any>, dimName: string, dataIndex: number, dimIndex: number
-            ): ParsedValue {
-                return parseDataValue(dataItem[dimName], this._dimensionInfos[dimName]);
-            },
-
-            keyedColumns: getDimValueSimply,
-
-            original: function (
-                this: List, dataItem: any, dimName: string, dataIndex: number, dimIndex: number
-            ): ParsedValue {
-                // Performance sensitive, do not use modelUtil.getDataItemValue.
-                // If dataItem is an plain object with no value field, the let `value`
-                // will be assigned with the object, but it will be tread correctly
-                // in the `convertValue`.
-                const value = dataItem && (dataItem.value == null ? dataItem : dataItem.value);
-
-                // If any dataItem is like { value: 10 }
-                if (!this._rawData.pure && isDataItemOption(dataItem)) {
-                    this.hasItemOption = true;
-                }
-                return parseDataValue(
-                    (value instanceof Array)
-                        ? value[dimIndex]
-                        // If value is a single number or something else not array.
-                        : value,
-                    this._dimensionInfos[dimName]
-                );
-            },
-
-            typedArray: function (
-                this: List, dataItem: any, dimName: string, dataIndex: number, dimIndex: number
-            ): ParsedValue {
-                return dataItem[dimIndex];
-            }
-
-        };
-
-        function getDimValueSimply(
-            this: List, dataItem: any, dimName: string, dataIndex: number, dimIndex: number
-        ): ParsedValue {
-            return parseDataValue(dataItem[dimIndex], this._dimensionInfos[dimName]);
-        }
-
-        prepareInvertedIndex = function (list: List): void {
-            const invertedIndicesMap = list._invertedIndicesMap;
-            zrUtil.each(invertedIndicesMap, function (invertedIndices, dim) {
-                const dimInfo = list._dimensionInfos[dim];
-
-                // Currently, only dimensions that has ordinalMeta can create inverted indices.
-                const ordinalMeta = dimInfo.ordinalMeta;
-                if (ordinalMeta) {
-                    invertedIndices = invertedIndicesMap[dim] = new CtorInt32Array(
-                        ordinalMeta.categories.length
-                    );
-                    // The default value of TypedArray is 0. To avoid miss
-                    // mapping to 0, we should set it as INDEX_NOT_FOUND.
-                    for (let i = 0; i < invertedIndices.length; i++) {
-                        invertedIndices[i] = INDEX_NOT_FOUND;
-                    }
-                    for (let i = 0; i < list._count; i++) {
-                        // Only support the case that all values are distinct.
-                        invertedIndices[list.get(dim, i) as number] = i;
-                    }
-                }
-            });
-        };
-
-        getIdNameFromStore = function (
-            list: List, dimIdx: number, ordinalMeta: OrdinalMeta, rawIndex: number
-        ): string {
-            let val;
-            const chunk = list._storageArr[dimIdx];
-            if (chunk) {
-                val = chunk[rawIndex];
-                if (ordinalMeta && ordinalMeta.categories.length) {
-                    val = ordinalMeta.categories[val as OrdinalNumber];
-                }
-            }
-            return convertOptionIdName(val, null);
-        };
-
-        getIndicesCtor = function (list: List): DataArrayLikeConstructor {
-            // The possible max value in this._indicies is always this._rawCount despite of filtering.
-            return list._rawCount > 65535 ? CtorUint32Array : CtorUint16Array;
-        };
-
-        prepareStorage = function (
-            storage: DataStorage,
-            dimInfo: DataDimensionInfo,
-            end: number,
-            append?: boolean
-        ): void {
-            const DataCtor = dataCtors[dimInfo.type];
-            const dim = dimInfo.name;
-
-            if (append) {
-                const oldStore = storage[dim];
-                const oldLen = oldStore && oldStore.length;
-                if (!(oldLen === end)) {
-                    const newStore = new DataCtor(end);
-                    // The cost of the copy is probably inconsiderable
-                    // within the initial chunkSize.
-                    for (let j = 0; j < oldLen; j++) {
-                        newStore[j] = oldStore[j];
-                    }
-                    storage[dim] = newStore;
-                }
-            }
-            else {
-                storage[dim] = new DataCtor(end);
-            }
-        };
-
-        getRawIndexWithoutIndices = function (this: List, idx: number): number {
-            return idx;
-        };
-
-        getRawIndexWithIndices = function (this: List, idx: number): number {
-            if (idx < this._count && idx >= 0) {
-                return this._indices[idx];
-            }
-            return -1;
-        };
-
-        /**
-         * @see the comment of `List['getId']`.
-         */
-        getId = function (list: List, rawIndex: number): string {
-            let id = list._idList[rawIndex];
-            if (id == null && list._idDimIdx != null) {
-                id = getIdNameFromStore(list, list._idDimIdx, list._idOrdinalMeta, rawIndex);
-            }
-            if (id == null) {
-                id = ID_PREFIX + rawIndex;
-            }
-            return id;
-        };
-
-        normalizeDimensions = function (
-            dimensions: ItrParamDims
-        ): Array<DimensionLoose> {
-            if (!zrUtil.isArray(dimensions)) {
-                dimensions = dimensions != null ? [dimensions] : [];
-            }
-            return dimensions;
-        };
-
-        validateDimensions = function (list: List, dims: DimensionName[]): void {
-            for (let i = 0; i < dims.length; i++) {
-                // stroage may be empty when no data, so use
-                // dimensionInfos to check.
-                if (!list._dimensionInfos[dims[i]]) {
-                    console.error('Unkown dimension ' + dims[i]);
-                }
-            }
-        };
-
-        // Data in excludeDimensions is copied, otherwise transfered.
-        cloneListForMapAndSample = function (
-            original: List, excludeDimensions: DimensionName[]
-        ): List {
-            const allDimensions = original.dimensions;
-            const list = new List(
-                map(allDimensions, original.getDimensionInfo, original),
-                original.hostModel
-            );
-            // FIXME If needs stackedOn, value may already been stacked
-            transferProperties(list, original);
-
-            const storage = list._storage = {} as DataStorage;
-            const originalStorage = original._storage;
-            const storageArr: DataValueChunk[] = list._storageArr = [];
-
-            // Init storage
-            for (let i = 0; i < allDimensions.length; i++) {
-                const dim = allDimensions[i];
-                if (originalStorage[dim]) {
-                    // Notice that we do not reset invertedIndicesMap here, becuase
-                    // there is no scenario of mapping or sampling ordinal dimension.
-                    if (zrUtil.indexOf(excludeDimensions, dim) >= 0) {
-                        storage[dim] = cloneChunk(originalStorage[dim]);
-                        list._rawExtent[dim] = getInitialExtent();
-                        list._extent[dim] = null;
-                    }
-                    else {
-                        // Direct reference for other dimensions
-                        storage[dim] = originalStorage[dim];
-                    }
-                    storageArr.push(storage[dim]);
-                }
-            }
-            return list;
-        };
-
-        function cloneChunk(originalChunk: DataValueChunk): DataValueChunk {
-            const Ctor = originalChunk.constructor;
-            // Only shallow clone is enough when Array.
-            return Ctor === Array
-                ? (originalChunk as Array<ParsedValue>).slice()
-                : new (Ctor as DataTypedArrayConstructor)(originalChunk as DataTypedArray);
-        }
-
-        getInitialExtent = function (): [number, number] {
-            return [Infinity, -Infinity];
-        };
-
-        setItemDataAndSeriesIndex = function (this: Element, child: Element): void {
-            const childECData = getECData(child);
-            const thisECData = getECData(this);
-            childECData.seriesIndex = thisECData.seriesIndex;
-            childECData.dataIndex = thisECData.dataIndex;
-            childECData.dataType = thisECData.dataType;
-        };
-
-        transferProperties = function (target: List, source: List): void {
-            zrUtil.each(
-                TRANSFERABLE_PROPERTIES.concat(source.__wrappedMethods || []),
-                function (propName) {
-                    if (source.hasOwnProperty(propName)) {
-                        (target as any)[propName] = (source as any)[propName];
-                    }
-                }
-            );
-
-            target.__wrappedMethods = source.__wrappedMethods;
-
-            zrUtil.each(CLONE_PROPERTIES, function (propName) {
-                (target as any)[propName] = zrUtil.clone((source as any)[propName]);
-            });
-
-            target._calculationInfo = zrUtil.extend({}, source._calculationInfo);
-        };
-
-        makeIdFromName = function (list: List, idx: number): void {
-            const nameList = list._nameList;
-            const idList = list._idList;
-            const nameDimIdx = list._nameDimIdx;
-            const idDimIdx = list._idDimIdx;
-
-            let name = nameList[idx];
-            let id = idList[idx];
-
-            if (name == null && nameDimIdx != null) {
-                nameList[idx] = name = getIdNameFromStore(list, nameDimIdx, list._nameOrdinalMeta, idx);
-            }
-            if (id == null && idDimIdx != null) {
-                idList[idx] = id = getIdNameFromStore(list, idDimIdx, list._idOrdinalMeta, idx);
-            }
-            if (id == null && name != null) {
-                const nameRepeatCount = list._nameRepeatCount;
-                const nmCnt = nameRepeatCount[name] = (nameRepeatCount[name] || 0) + 1;
-                id = name;
-                if (nmCnt > 1) {
-                    id += '__ec__' + nmCnt;
-                }
-                idList[idx] = id;
-            }
-        };
-
-    })();
-
-}
-
-interface List {
-    getLinkedData(dataType?: SeriesDataType): List;
-    getLinkedDataAll(): { data: List, type?: SeriesDataType }[];
-}
-
-export default List;
diff --git a/src/data/SeriesData.ts b/src/data/SeriesData.ts
new file mode 100644
index 0000000..553a225
--- /dev/null
+++ b/src/data/SeriesData.ts
@@ -0,0 +1,1299 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+/* global Float64Array, Int32Array, Uint32Array, Uint16Array */
+
+/**
+ * List for data storage
+ */
+
+import * as zrUtil from 'zrender/src/core/util';
+import {PathStyleProps} from 'zrender/src/graphic/Path';
+import Model from '../model/Model';
+import DataDiffer from './DataDiffer';
+import {DefaultDataProvider} from './helper/dataProvider';
+import {summarizeDimensions, DimensionSummary} from './helper/dimensionHelper';
+import DataDimensionInfo from './DataDimensionInfo';
+import {ArrayLike, Dictionary, FunctionPropertyNames} from 'zrender/src/core/types';
+import Element from 'zrender/src/Element';
+import {
+    DimensionIndex, DimensionName, DimensionLoose, OptionDataItem,
+    ParsedValue, ParsedValueNumeric, DimensionUserOuput,
+    ModelOption, SeriesDataType, OptionSourceData, SOURCE_FORMAT_TYPED_ARRAY, SOURCE_FORMAT_ORIGINAL,
+    DecalObject,
+    OrdinalNumber
+} from '../util/types';
+import {convertOptionIdName, isDataItemOption} from '../util/model';
+import { getECData } from '../util/innerStore';
+import type Graph from './Graph';
+import type Tree from './Tree';
+import type { VisualMeta } from '../component/visualMap/VisualMapModel';
+import {isSourceInstance, Source} from './Source';
+import { LineStyleProps } from '../model/mixin/lineStyle';
+import DataStorage, { DimValueGetter } from './DataStorage';
+
+const isObject = zrUtil.isObject;
+const map = zrUtil.map;
+
+const CtorInt32Array = typeof Int32Array === 'undefined' ? Array : Int32Array;
+
+// Use prefix to avoid index to be the same as otherIdList[idx],
+// which will cause weird udpate animation.
+const ID_PREFIX = 'e\0\0';
+
+const INDEX_NOT_FOUND = -1;
+
+type NameRepeatCount = {[name: string]: number};
+type ItrParamDims = DimensionLoose | Array<DimensionLoose>;
+// If Ctx not specified, use List as Ctx
+type CtxOrList<Ctx> = unknown extends Ctx ? SeriesData : Ctx;
+type EachCb0<Ctx> = (this: CtxOrList<Ctx>, idx: number) => void;
+type EachCb1<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, idx: number) => void;
+type EachCb2<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, y: ParsedValue, idx: number) => void;
+type EachCb<Ctx> = (this: CtxOrList<Ctx>, ...args: any) => void;
+type FilterCb0<Ctx> = (this: CtxOrList<Ctx>, idx: number) => boolean;
+type FilterCb1<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, idx: number) => boolean;
+type FilterCb2<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, y: ParsedValue, idx: number) => boolean;
+type FilterCb<Ctx> = (this: CtxOrList<Ctx>, ...args: any) => boolean;
+type MapArrayCb0<Ctx> = (this: CtxOrList<Ctx>, idx: number) => any;
+type MapArrayCb1<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, idx: number) => any;
+type MapArrayCb2<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, y: ParsedValue, idx: number) => any;
+type MapArrayCb<Ctx> = (this: CtxOrList<Ctx>, ...args: any) => any;
+type MapCb1<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, idx: number) => ParsedValue | ParsedValue[];
+type MapCb2<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, y: ParsedValue, idx: number) =>
+    ParsedValue | ParsedValue[];
+type MapCb<Ctx> = (this: CtxOrList<Ctx>, ...args: any) => ParsedValue | ParsedValue[];
+
+const TRANSFERABLE_PROPERTIES = [
+    'hasItemOption', '_nameList', '_idList', '_invertedIndicesMap',
+    '_rawData', '_dimValueGetter', '_store',
+    '_nameDimIdx', '_idDimIdx', '_nameRepeatCount'
+];
+const CLONE_PROPERTIES = [
+    '_approximateExtent'
+];
+
+export interface DefaultDataVisual {
+    style: PathStyleProps
+    // Draw type determined which prop should be set with encoded color.
+    // It's only available on the global visual. Use getVisual('drawType') to access it.
+    // It will be set in visual/style.ts module in the first priority.
+    drawType: 'fill' | 'stroke'
+
+    symbol?: string
+    symbolSize?: number | number[]
+    symbolRotate?: number
+    symbolKeepAspect?: boolean
+    symbolOffset?: string | number | (string | number)[]
+
+    liftZ?: number
+    // For legend.
+    legendIcon?: string
+    legendLineStyle?: LineStyleProps
+
+    // visualMap will inject visualMeta data
+    visualMeta?: VisualMeta[]
+
+    // If color is encoded from palette
+    colorFromPalette?: boolean
+
+    decal?: DecalObject
+}
+
+export interface DataCalculationInfo<SERIES_MODEL> {
+    stackedDimension: string;
+    stackedByDimension: string;
+    isStackedByIndex: boolean;
+    stackedOverDimension: string;
+    stackResultDimension: string;
+    stackedOnSeries?: SERIES_MODEL;
+}
+
+// -----------------------------
+// Internal method declarations:
+// -----------------------------
+let prepareInvertedIndex: (list: SeriesData) => void;
+let getId: (list: SeriesData, rawIndex: number) => string;
+let getIdNameFromStore: (list: SeriesData, dimIdx: number, dataIdx: number) => string;
+let normalizeDimensions: (dimensions: ItrParamDims) => Array<DimensionLoose>;
+let validateDimensions: (list: SeriesData, dims: DimensionIndex[]) => void;
+let setItemDataAndSeriesIndex: (this: Element, child: Element) => void;
+let transferProperties: (target: SeriesData, source: SeriesData) => void;
+let cloneListForMapAndSample: (original: SeriesData) => SeriesData;
+
+class SeriesData<
+    HostModel extends Model = Model,
+    Visual extends DefaultDataVisual = DefaultDataVisual
+> {
+
+    readonly type = 'list';
+
+    readonly dimensions: string[];
+
+    // Infomation of each data dimension, like data type.
+    private _dimensionInfos: {[dimName: string]: DataDimensionInfo};
+
+    readonly hostModel: HostModel;
+
+    /**
+     * @readonly
+     */
+    dataType: SeriesDataType;
+
+    /**
+     * @readonly
+     * Host graph if List is used to store graph nodes / edges.
+     */
+    graph?: Graph;
+
+    /**
+     * @readonly
+     * Host tree if List is used to store tree ndoes.
+     */
+    tree?: Tree;
+
+    private _store: DataStorage;
+
+    private _nameList: string[] = [];
+    private _idList: string[] = [];
+
+    // Models of data option is stored sparse for optimizing memory cost
+    // Never used yet (not used yet).
+    // private _optionModels: Model[] = [];
+
+    // Global visual properties after visual coding
+    private _visual: Dictionary<any> = {};
+
+    // Globel layout properties.
+    private _layout: Dictionary<any> = {};
+
+    // Item visual properties after visual coding
+    private _itemVisuals: Dictionary<any>[] = [];
+
+    // Item layout properties after layout
+    private _itemLayouts: any[] = [];
+
+    // Graphic elemnents
+    private _graphicEls: Element[] = [];
+
+    // key: dim, value: extent
+    private _approximateExtent: {[dimName: string]: [number, number]} = {};
+
+    private _dimensionsSummary: DimensionSummary;
+
+    private _invertedIndicesMap: {[dimName: string]: ArrayLike<number>};
+
+    private _calculationInfo: DataCalculationInfo<HostModel> = {} as DataCalculationInfo<HostModel>;
+
+    // User output info of this data.
+    // DO NOT use it in other places!
+    // When preparing user params for user callbacks, we have
+    // to clone these inner data structures to prevent users
+    // from modifying them to effect built-in logic. And for
+    // performance consideration we make this `userOutput` to
+    // avoid clone them too many times.
+    readonly userOutput: DimensionUserOuput;
+
+    // Having detected that there is data item is non primitive type
+    // (in type `OptionDataItemObject`).
+    // Like `data: [ { value: xx, itemStyle: {...} }, ...]`
+    // At present it only happen in `SOURCE_FORMAT_ORIGINAL`.
+    hasItemOption: boolean = false;
+
+    // id or name is used on dynamic data, mapping old and new items.
+    // When generating id from name, avoid repeat.
+    private _nameRepeatCount: NameRepeatCount;
+    private _nameDimIdx: number;
+    private _idDimIdx: number;
+
+    private __wrappedMethods: string[];
+
+    // Methods that create a new list based on this list should be listed here.
+    // Notice that those method should `RETURN` the new list.
+    TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'lttbDownSample', 'map'] as const;
+    // Methods that change indices of this list should be listed here.
+    CHANGABLE_METHODS = ['filterSelf', 'selectRange'] as const;
+    DOWNSAMPLE_METHODS = ['downSample', 'lttbDownSample'] as const;
+
+    /**
+     * @param dimensions
+     *        For example, ['someDimName', {name: 'someDimName', type: 'someDimType'}, ...].
+     *        Dimensions should be concrete names like x, y, z, lng, lat, angle, radius
+     */
+    constructor(dimensions: Array<string | object | DataDimensionInfo>, hostModel: HostModel) {
+        dimensions = dimensions || ['x', 'y'];
+
+        const dimensionInfos: Dictionary<DataDimensionInfo> = {};
+        const dimensionNames = [];
+        const invertedIndicesMap: Dictionary<number[]> = {};
+
+        for (let i = 0; i < dimensions.length; i++) {
+            // Use the original dimensions[i], where other flag props may exists.
+            const dimInfoInput = dimensions[i];
+
+            const dimensionInfo: DataDimensionInfo =
+                zrUtil.isString(dimInfoInput)
+                ? new DataDimensionInfo({name: dimInfoInput})
+                : !(dimInfoInput instanceof DataDimensionInfo)
+                ? new DataDimensionInfo(dimInfoInput)
+                : dimInfoInput;
+
+            const dimensionName = dimensionInfo.name;
+            dimensionInfo.type = dimensionInfo.type || 'float';
+            if (!dimensionInfo.coordDim) {
+                dimensionInfo.coordDim = dimensionName;
+                dimensionInfo.coordDimIndex = 0;
+            }
+
+            const otherDims = dimensionInfo.otherDims = dimensionInfo.otherDims || {};
+            dimensionNames.push(dimensionName);
+            dimensionInfos[dimensionName] = dimensionInfo;
+
+            dimensionInfo.index = i;
+
+            if (dimensionInfo.createInvertedIndices) {
+                invertedIndicesMap[dimensionName] = [];
+            }
+            if (otherDims.itemName === 0) {
+                this._nameDimIdx = i;
+            }
+            if (otherDims.itemId === 0) {
+                this._idDimIdx = i;
+            }
+        }
+
+        this.dimensions = dimensionNames;
+        this._dimensionInfos = dimensionInfos;
+        this.hostModel = hostModel;
+
+        // Cache summary info for fast visit. See "dimensionHelper".
+        this._dimensionsSummary = summarizeDimensions(this);
+
+        this._invertedIndicesMap = invertedIndicesMap;
+
+        this.userOutput = this._dimensionsSummary.userOutput;
+    }
+
+    /**
+     * The meanings of the input parameter `dim`:
+     *
+     * + If dim is a number (e.g., `1`), it means the index of the dimension.
+     *   For example, `getDimension(0)` will return 'x' or 'lng' or 'radius'.
+     * + If dim is a number-like string (e.g., `"1"`):
+     *     + If there is the same concrete dim name defined in `this.dimensions`, it means that concrete name.
+     *     + If not, it will be converted to a number, which means the index of the dimension.
+     *        (why? because of the backward compatbility. We have been tolerating number-like string in
+     *        dimension setting, although now it seems that it is not a good idea.)
+     *     For example, `visualMap[i].dimension: "1"` is the same meaning as `visualMap[i].dimension: 1`,
+     *     if no dimension name is defined as `"1"`.
+     * + If dim is a not-number-like string, it means the concrete dim name.
+     *   For example, it can be be default name `"x"`, `"y"`, `"z"`, `"lng"`, `"lat"`, `"angle"`, `"radius"`,
+     *   or customized in `dimensions` property of option like `"age"`.
+     *
+     * Get dimension name
+     * @param dim See above.
+     * @return Concrete dim name.
+     */
+    getDimension(dim: DimensionLoose): DimensionName {
+        if (typeof dim === 'number'
+            // If being a number-like string but not being defined a dimension name.
+            || (!isNaN(dim as any) && !this._dimensionInfos.hasOwnProperty(dim))
+        ) {
+            dim = this.dimensions[dim as DimensionIndex];
+        }
+        return dim as DimensionName;
+    }
+
+    getDimensionIndex(dim: DimensionLoose): DimensionIndex {
+        if (typeof dim === 'number') {
+            return dim;
+        }
+        const idx = this._store.getDimensionIndex(dim);
+        if (idx == null) {
+            // If being a number-like string but not being defined a dimension name.
+            return +dim;
+        }
+        return idx;
+    }
+
+    /**
+     * Get type and calculation info of particular dimension
+     * @param dim
+     *        Dimension can be concrete names like x, y, z, lng, lat, angle, radius
+     *        Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'
+     */
+    getDimensionInfo(dim: DimensionLoose): DataDimensionInfo {
+        // Do not clone, because there may be categories in dimInfo.
+        return this._dimensionInfos[this.getDimension(dim)];
+    }
+
+    /**
+     * concrete dimension name list on coord.
+     */
+    getDimensionsOnCoord(): DimensionName[] {
+        return this._dimensionsSummary.dataDimsOnCoord.slice();
+    }
+
+    /**
+     * @param coordDim
+     * @param idx A coordDim may map to more than one data dim.
+     *        If not specified, return the first dim not extra.
+     * @return concrete data dim. If not found, return null/undefined
+     */
+    mapDimension(coordDim: DimensionName): DimensionName;
+    mapDimension(coordDim: DimensionName, idx: number): DimensionName;
+    mapDimension(coordDim: DimensionName, idx?: number): DimensionName {
+        const dimensionsSummary = this._dimensionsSummary;
+
+        if (idx == null) {
+            return dimensionsSummary.encodeFirstDimNotExtra[coordDim] as any;
+        }
+
+        const dims = dimensionsSummary.encode[coordDim];
+        return dims ? dims[idx as number] as any : null;
+    }
+
+    mapDimensionsAll(coordDim: DimensionName): DimensionName[] {
+        const dimensionsSummary = this._dimensionsSummary;
+        const dims = dimensionsSummary.encode[coordDim];
+        return (dims || []).slice();
+    }
+
+    getStore() {
+        return this._store;
+    }
+
+    /**
+     * Initialize from data
+     * @param data source or data or data storage.
+     * @param nameList The name of a datum is used on data diff and
+     *        default label/tooltip.
+     *        A name can be specified in encode.itemName,
+     *        or dataItem.name (only for series option data),
+     *        or provided in nameList from outside.
+     */
+    initData(
+        data: Source | OptionSourceData | DataStorage,
+        nameList?: string[],
+        dimValueGetter?: DimValueGetter
+    ): void {
+        const isRaw = isSourceInstance(data) || zrUtil.isArrayLike(data);
+        let store: DataStorage;
+        if (isRaw) {
+            const provider = new DefaultDataProvider(data as Source | OptionSourceData, this.dimensions.length);
+            const dimensionInfos = map(this.dimensions, dimName => this._dimensionInfos[dimName]);
+            store = new DataStorage();
+            store.initData(provider, dimensionInfos, dimValueGetter);
+        }
+        else {
+            store = data as DataStorage;
+        }
+
+        this._store = store;
+
+        // Reset
+        this._nameList = (nameList || []).slice();
+        this._idList = [];
+        this._nameRepeatCount = {};
+
+        this._doInit(0, store.count());
+    }
+
+    /**
+     * Caution: Can be only called on raw data (before `this._indices` created).
+     */
+    appendData(data: ArrayLike<any>): void {
+        const range = this._store.appendData(data);
+        this._doInit(range[0], range[1]);
+    }
+
+    private _doInit(start: number, end: number): void {
+        if (start >= end) {
+            return;
+        }
+
+        const store = this._store;
+        const provider = store.getProvider();
+
+        const nameList = this._nameList;
+        const idList = this._idList;
+        const sourceFormat = provider.getSource().sourceFormat;
+        const isFormatOriginal = sourceFormat === SOURCE_FORMAT_ORIGINAL;
+
+        const dontMakeIdFromName = this._idDimIdx != null
+            || sourceFormat === SOURCE_FORMAT_TYPED_ARRAY // Consider performance.
+            || !!provider.fillStorage;
+
+        // Each data item is value
+        // [1, 2]
+        // 2
+        // Bar chart, line chart which uses category axis
+        // only gives the 'y' value. 'x' value is the indices of category
+        // Use a tempValue to normalize the value to be a (x, y) value
+        // If dataItem is {name: ...} or {id: ...}, it has highest priority.
+        // This kind of ids and names are always stored `_nameList` and `_idList`.
+        if (isFormatOriginal && !provider.pure) {
+            const sharedDataItem = [] as OptionDataItem;
+            for (let idx = start; idx < end; idx++) {
+                // NOTICE: Try not to write things into dataItem
+                const dataItem = provider.getItem(idx, sharedDataItem);
+                if (!this.hasItemOption && isDataItemOption(dataItem)) {
+                    this.hasItemOption = true;
+                }
+                if (dataItem) {
+                    const itemName = (dataItem as any).name;
+                    if (nameList[idx] == null && itemName != null) {
+                        nameList[idx] = convertOptionIdName(itemName, null);
+                    }
+                    const itemId = (dataItem as any).id;
+                    if (idList[idx] == null && itemId != null) {
+                        idList[idx] = convertOptionIdName(itemId, null);
+                    }
+                }
+            }
+        }
+
+        if (!dontMakeIdFromName) {
+            const nameDimIdx = this._nameDimIdx;
+            const idDimIdx = this._idDimIdx;
+
+            for (let idx = start; idx < end; idx++) {
+                let name = nameList[idx];
+                let id = idList[idx];
+
+                if (name == null && nameDimIdx != null) {
+                    nameList[idx] = name = getIdNameFromStore(this, nameDimIdx, idx);
+                }
+                if (id == null && idDimIdx != null) {
+                    idList[idx] = id = getIdNameFromStore(this, idDimIdx, idx);
+                }
+                if (id == null && name != null) {
+                    const nameRepeatCount = this._nameRepeatCount;
+                    const nmCnt = nameRepeatCount[name] = (nameRepeatCount[name] || 0) + 1;
+                    id = name;
+                    if (nmCnt > 1) {
+                        id += '__ec__' + nmCnt;
+                    }
+                    idList[idx] = id;
+                }
+            }
+        }
+
+        prepareInvertedIndex(this);
+    }
+    /**
+     * PENDING: In fact currently this function is only used to short-circuit
+     * the calling of `scale.unionExtentFromData` when data have been filtered by modules
+     * like "dataZoom". `scale.unionExtentFromData` is used to calculate data extent for series on
+     * an axis, but if a "axis related data filter module" is used, the extent of the axis have
+     * been fixed and no need to calling `scale.unionExtentFromData` actually.
+     * But if we add "custom data filter" in future, which is not "axis related", this method may
+     * be still needed.
+     *
+     * Optimize for the scenario that data is filtered by a given extent.
+     * Consider that if data amount is more than hundreds of thousand,
+     * extent calculation will cost more than 10ms and the cache will
+     * be erased because of the filtering.
+     */
+    getApproximateExtent(dim: DimensionLoose): [number, number] {
+        return this._approximateExtent[dim] || this._store.getDataExtent(this.getDimensionIndex(dim));
+    }
+
+    /**
+     * Calculate extent on a filtered data might be time consuming.
+     * Approximate extent is only used for: calculte extent of filtered data outside.
+     */
+    setApproximateExtent(extent: [number, number], dim: DimensionLoose): void {
+        dim = this.getDimension(dim);
+        this._approximateExtent[dim] = extent.slice() as [number, number];
+    }
+
+    getCalculationInfo<CALC_INFO_KEY extends keyof DataCalculationInfo<HostModel>>(
+        key: CALC_INFO_KEY
+    ): DataCalculationInfo<HostModel>[CALC_INFO_KEY] {
+        return this._calculationInfo[key];
+    }
+
+    /**
+     * @param key or k-v object
+     */
+    setCalculationInfo(
+        key: DataCalculationInfo<HostModel>
+    ): void;
+    setCalculationInfo<CALC_INFO_KEY extends keyof DataCalculationInfo<HostModel>>(
+        key: CALC_INFO_KEY,
+        value: DataCalculationInfo<HostModel>[CALC_INFO_KEY]
+    ): void;
+    setCalculationInfo(
+        key: (keyof DataCalculationInfo<HostModel>) | DataCalculationInfo<HostModel>,
+        value?: DataCalculationInfo<HostModel>[keyof DataCalculationInfo<HostModel>]
+    ): void {
+        isObject(key)
+            ? zrUtil.extend(this._calculationInfo, key as object)
+            : ((this._calculationInfo as any)[key] = value);
+    }
+
+    /**
+     * @return Never be null/undefined. `number` will be converted to string. Becuase:
+     * In most cases, name is used in display, where returning a string is more convenient.
+     * In other cases, name is used in query (see `indexOfName`), where we can keep the
+     * rule that name `2` equals to name `'2'`.
+     */
+    getName(idx: number): string {
+        const rawIndex = this.getRawIndex(idx);
+        let name = this._nameList[rawIndex];
+        if (name == null && this._nameDimIdx != null) {
+            name = getIdNameFromStore(this, this._nameDimIdx, rawIndex);
+        }
+        if (name == null) {
+            name = '';
+        }
+        return name;
+    }
+
+    /**
+     * @return Never null/undefined. `number` will be converted to string. Becuase:
+     * In all cases having encountered at present, id is used in making diff comparison, which
+     * are usually based on hash map. We can keep the rule that the internal id are always string
+     * (treat `2` is the same as `'2'`) to make the related logic simple.
+     */
+    getId(idx: number): string {
+        return getId(this, this.getRawIndex(idx));
+    }
+
+    count() {
+        return this._store.count();
+    }
+    /**
+     * Get value. Return NaN if idx is out of range.
+     * @param dim Dim must be concrete name.
+     */
+    get(dim: DimensionName, idx: number): ParsedValue {
+        const store = this._store;
+        return store.get(store.getDimensionIndex(dim), idx);
+    }
+
+    getByRawIndex(dim: DimensionName, rawIdx: number): ParsedValue {
+        const store = this._store;
+        return store.getByRawIndex(store.getDimensionIndex(dim), rawIdx);
+    }
+
+    getIndices() {
+        return this._store.getIndices();
+    }
+
+    getDataExtent(dim: DimensionLoose): [number, number] {
+        return this._store.getDataExtent(this.getDimensionIndex(dim));
+    }
+
+    getSum(dim: DimensionLoose): number {
+        return this._store.getSum(this.getDimensionIndex(dim));
+    }
+
+    getMedian(dim: DimensionLoose): number {
+        return this._store.getMedian(this.getDimensionIndex(dim));
+    }
+    /**
+     * Get value for multi dimensions.
+     * @param dimensions If ignored, using all dimensions.
+     */
+    getValues(idx: number): ParsedValue[];
+    getValues(dimensions: readonly DimensionName[], idx: number): ParsedValue[];
+    getValues(dimensions: readonly DimensionName[] | number, idx?: number): ParsedValue[] {
+        const values = [];
+        if (!zrUtil.isArray(dimensions)) {
+            // stack = idx;
+            idx = dimensions as number;
+            dimensions = this.dimensions;
+        }
+
+        for (let i = 0, len = dimensions.length; i < len; i++) {
+            values.push(this.get(dimensions[i], idx));
+        }
+
+        return values;
+    }
+
+    /**
+     * If value is NaN. Inlcuding '-'
+     * Only check the coord dimensions.
+     */
+    hasValue(idx: number): boolean {
+        const dataDimsOnCoord = this._dimensionsSummary.dataDimsOnCoord;
+        for (let i = 0, len = dataDimsOnCoord.length; i < len; i++) {
+            // Ordinal type originally can be string or number.
+            // But when an ordinal type is used on coord, it can
+            // not be string but only number. So we can also use isNaN.
+            if (isNaN(this.get(dataDimsOnCoord[i], idx) as any)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Retreive the index with given name
+     */
+    indexOfName(name: string): number {
+        for (let i = 0, len = this._store.count(); i < len; i++) {
+            if (this.getName(i) === name) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    getRawIndex(idx: number) {
+        return this._store.getRawIndex(idx);
+    }
+
+    indexOfRawIndex(rawIndex: number) {
+        return this._store.indexOfRawIndex(rawIndex);
+    }
+
+    /**
+     * Only support the dimension which inverted index created.
+     * Do not support other cases until required.
+     * @param dim concrete dim
+     * @param value ordinal index
+     * @return rawIndex
+     */
+    rawIndexOf(dim: DimensionName, value: OrdinalNumber): number {
+        const invertedIndices = dim && this._invertedIndicesMap[dim];
+        if (__DEV__) {
+            if (!invertedIndices) {
+                throw new Error('Do not supported yet');
+            }
+        }
+        const rawIndex = invertedIndices[value];
+        if (rawIndex == null || isNaN(rawIndex)) {
+            return INDEX_NOT_FOUND;
+        }
+        return rawIndex;
+    }
+
+    /**
+     * Retreive the index of nearest value
+     * @param dim
+     * @param value
+     * @param [maxDistance=Infinity]
+     * @return If and only if multiple indices has
+     *         the same value, they are put to the result.
+     */
+    indicesOfNearest(dim: DimensionLoose, value: number, maxDistance?: number): number[] {
+        return this._store.indicesOfNearest(
+            this.getDimensionIndex(dim),
+            value, maxDistance
+        );
+    }
+    /**
+     * Data iteration
+     * @param ctx default this
+     * @example
+     *  list.each('x', function (x, idx) {});
+     *  list.each(['x', 'y'], function (x, y, idx) {});
+     *  list.each(function (idx) {})
+     */
+    each<Ctx>(cb: EachCb0<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): void;
+    each<Ctx>(dims: DimensionLoose, cb: EachCb1<Ctx>, ctx?: Ctx): void;
+    each<Ctx>(dims: [DimensionLoose], cb: EachCb1<Ctx>, ctx?: Ctx): void;
+    each<Ctx>(dims: [DimensionLoose, DimensionLoose], cb: EachCb2<Ctx>, ctx?: Ctx): void;
+    each<Ctx>(dims: ItrParamDims, cb: EachCb<Ctx>, ctx?: Ctx): void;
+    each<Ctx>(
+        dims: ItrParamDims | EachCb<Ctx>,
+        cb: EachCb<Ctx> | Ctx,
+        ctx?: Ctx
+    ): void {
+        'use strict';
+
+        if (typeof dims === 'function') {
+            ctx = cb as Ctx;
+            cb = dims;
+            dims = [];
+        }
+
+        // ctxCompat just for compat echarts3
+        const fCtx = (ctx || this) as CtxOrList<Ctx>;
+
+        const dimIndices = map(normalizeDimensions(dims), this.getDimensionIndex, this);
+
+        if (__DEV__) {
+            validateDimensions(this, dimIndices);
+        }
+
+        this._store.each(dimIndices, (fCtx
+            ? zrUtil.bind(cb as any, fCtx as any)
+            : cb) as any
+        );
+    }
+    /**
+     * Data filter
+     */
+    filterSelf<Ctx>(cb: FilterCb0<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): this;
+    filterSelf<Ctx>(dims: DimensionLoose, cb: FilterCb1<Ctx>, ctx?: Ctx): this;
+    filterSelf<Ctx>(dims: [DimensionLoose], cb: FilterCb1<Ctx>, ctx?: Ctx): this;
+    filterSelf<Ctx>(dims: [DimensionLoose, DimensionLoose], cb: FilterCb2<Ctx>, ctx?: Ctx): this;
+    filterSelf<Ctx>(dims: ItrParamDims, cb: FilterCb<Ctx>, ctx?: Ctx): this;
+    filterSelf<Ctx>(
+        dims: ItrParamDims | FilterCb<Ctx>,
+        cb: FilterCb<Ctx> | Ctx,
+        ctx?: Ctx
+    ): SeriesData {
+        'use strict';
+
+        if (typeof dims === 'function') {
+            ctx = cb as Ctx;
+            cb = dims;
+            dims = [];
+        }
+
+        // ctxCompat just for compat echarts3
+        const fCtx = (ctx || this) as CtxOrList<Ctx>;
+
+        const dimIndices = map(normalizeDimensions(dims), this.getDimensionIndex, this);
+
+        if (__DEV__) {
+            validateDimensions(this, dimIndices);
+        }
+
+        this._store.filterSelf(dimIndices, (fCtx
+            ? zrUtil.bind(cb as any, fCtx as any)
+            : cb) as any
+        );
+
+        return this;
+    }
+
+    /**
+     * Select data in range. (For optimization of filter)
+     * (Manually inline code, support 5 million data filtering in data zoom.)
+     */
+    selectRange(range: Record<string, [number, number]>): SeriesData {
+        'use strict';
+
+        const innerRange: Record<number, [number, number]> = {};
+        const dims = zrUtil.keys(range);
+        const dimIndices: number[] = [];
+        zrUtil.each(dims, (dim) => {
+            const dimIdx = this.getDimensionIndex(dim);
+            innerRange[dimIdx] = range[dim];
+            dimIndices.push(dimIdx);
+        });
+
+        if (__DEV__) {
+            validateDimensions(this, dimIndices);
+        }
+
+        this._store.selectRange(innerRange);
+        return this;
+    }
+
+    /**
+     * Data mapping to a plain array
+     */
+    mapArray<Ctx, Cb extends MapArrayCb0<Ctx>>(cb: Cb, ctx?: Ctx, ctxCompat?: Ctx): ReturnType<Cb>[];
+    /* eslint-disable */
+    mapArray<Ctx, Cb extends MapArrayCb1<Ctx>>(dims: DimensionLoose, cb: Cb, ctx?: Ctx, ctxCompat?: Ctx): ReturnType<Cb>[];
+    mapArray<Ctx, Cb extends MapArrayCb1<Ctx>>(dims: [DimensionLoose], cb: Cb, ctx?: Ctx, ctxCompat?: Ctx): ReturnType<Cb>[];
+    mapArray<Ctx, Cb extends MapArrayCb2<Ctx>>(dims: [DimensionLoose, DimensionLoose], cb: Cb, ctx?: Ctx, ctxCompat?: Ctx): ReturnType<Cb>[];
+    mapArray<Ctx, Cb extends MapArrayCb<Ctx>>(dims: ItrParamDims, cb: Cb, ctx?: Ctx, ctxCompat?: Ctx): ReturnType<Cb>[];
+    /* eslint-enable */
+    mapArray<Ctx>(
+        dims: ItrParamDims | MapArrayCb<Ctx>,
+        cb: MapArrayCb<Ctx> | Ctx,
+        ctx?: Ctx
+    ): unknown[] {
+        'use strict';
+
+        if (typeof dims === 'function') {
+            ctx = cb as Ctx;
+            cb = dims;
+            dims = [];
+        }
+
+        // ctxCompat just for compat echarts3
+        ctx = (ctx || this) as Ctx;
+
+        const result: unknown[] = [];
+        this.each(dims, function () {
+            result.push(cb && (cb as MapArrayCb<Ctx>).apply(this, arguments));
+        }, ctx);
+        return result;
+    }
+
+    /**
+     * Data mapping to a new List with given dimensions
+     */
+    map<Ctx>(dims: DimensionLoose, cb: MapCb1<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): SeriesData<HostModel>;
+    map<Ctx>(dims: [DimensionLoose], cb: MapCb1<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): SeriesData<HostModel>;
+    /* eslint-disable-next-line */
+    map<Ctx>(dims: [DimensionLoose, DimensionLoose], cb: MapCb2<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): SeriesData<HostModel>;
+    map<Ctx>(
+        dims: ItrParamDims,
+        cb: MapCb<Ctx>,
+        ctx?: Ctx,
+        ctxCompat?: Ctx
+    ): SeriesData {
+        'use strict';
+
+        // ctxCompat just for compat echarts3
+        const fCtx = (ctx || ctxCompat || this) as CtxOrList<Ctx>;
+
+        const dimIndices = map(
+            normalizeDimensions(dims), this.getDimensionIndex, this
+        );
+
+        if (__DEV__) {
+            validateDimensions(this, dimIndices);
+        }
+
+        const list = cloneListForMapAndSample(this);
+        list._store = this._store.map(dimIndices, (fCtx
+            ? zrUtil.bind(cb as any, fCtx as any)
+            : cb) as any
+        );
+        return list;
+    }
+
+    /**
+     * Large data down sampling on given dimension
+     * @param sampleIndex Sample index for name and id
+     */
+    downSample(
+        dimension: DimensionLoose,
+        rate: number,
+        sampleValue: (frameValues: ArrayLike<ParsedValue>) => ParsedValueNumeric,
+        sampleIndex: (frameValues: ArrayLike<ParsedValue>, value: ParsedValueNumeric) => number
+    ): SeriesData<HostModel> {
+        const list = cloneListForMapAndSample(this);
+        list._store = this._store.downSample(
+            this.getDimensionIndex(dimension),
+            rate,
+            sampleValue,
+            sampleIndex
+        );
+        return list as SeriesData<HostModel>;
+    }
+
+    /**
+     * Large data down sampling using largest-triangle-three-buckets
+     * @param {string} valueDimension
+     * @param {number} targetCount
+     */
+    lttbDownSample(
+        valueDimension: DimensionLoose,
+        rate: number
+    ) {
+        const list = cloneListForMapAndSample(this);
+        list._store = this._store.lttbDownSample(
+            this.getDimensionIndex(valueDimension),
+            rate
+        );
+        return list;
+    }
+
+    getRawDataItem(idx: number) {
+        return this._store.getRawDataItem(idx);
+    }
+    /**
+     * Get model of one data item.
+     */
+    // TODO: Type of data item
+    getItemModel<ItemOpts extends unknown = unknown>(idx: number): Model<ItemOpts
+        // Extract item option with value key. FIXME will cause incompatitable issue
+        // Extract<HostModel['option']['data'][number], { value?: any }>
+    > {
+        const hostModel = this.hostModel;
+        const dataItem = this.getRawDataItem(idx) as ModelOption;
+        return new Model(dataItem, hostModel, hostModel && hostModel.ecModel);
+    }
+
+    /**
+     * Create a data differ
+     */
+    diff(otherList: SeriesData): DataDiffer {
+        const thisList = this;
+
+        return new DataDiffer(
+            otherList ? otherList.getStore().getIndices() : [],
+            this.getStore().getIndices(),
+            function (idx: number) {
+                return getId(otherList, idx);
+            },
+            function (idx: number) {
+                return getId(thisList, idx);
+            }
+        );
+    }
+
+    /**
+     * Get visual property.
+     */
+    getVisual<K extends keyof Visual>(key: K): Visual[K] {
+        const visual = this._visual as Visual;
+        return visual && visual[key];
+    }
+
+    /**
+     * Set visual property
+     *
+     * @example
+     *  setVisual('color', color);
+     *  setVisual({
+     *      'color': color
+     *  });
+     */
+    setVisual<K extends keyof Visual>(key: K, val: Visual[K]): void;
+    setVisual(kvObj: Partial<Visual>): void;
+    setVisual(kvObj: string | Partial<Visual>, val?: any): void {
+        this._visual = this._visual || {};
+        if (isObject(kvObj)) {
+            zrUtil.extend(this._visual, kvObj);
+        }
+        else {
+            this._visual[kvObj as string] = val;
+        }
+    }
+
+    /**
+     * Get visual property of single data item
+     */
+    // eslint-disable-next-line
+    getItemVisual<K extends keyof Visual>(idx: number, key: K): Visual[K] {
+        const itemVisual = this._itemVisuals[idx] as Visual;
+        const val = itemVisual && itemVisual[key];
+        if (val == null) {
+            // Use global visual property
+            return this.getVisual(key);
+        }
+        return val;
+    }
+
+    /**
+     * If exists visual property of single data item
+     */
+    hasItemVisual() {
+        return this._itemVisuals.length > 0;
+    }
+
+    /**
+     * Make sure itemVisual property is unique
+     */
+    // TODO: use key to save visual to reduce memory.
+    ensureUniqueItemVisual<K extends keyof Visual>(idx: number, key: K): Visual[K] {
+        const itemVisuals = this._itemVisuals;
+        let itemVisual = itemVisuals[idx] as Visual;
+        if (!itemVisual) {
+            itemVisual = itemVisuals[idx] = {} as Visual;
+        }
+        let val = itemVisual[key];
+        if (val == null) {
+            val = this.getVisual(key);
+
+            // TODO Performance?
+            if (zrUtil.isArray(val)) {
+                val = val.slice() as unknown as Visual[K];
+            }
+            else if (isObject(val)) {
+                val = zrUtil.extend({}, val);
+            }
+
+            itemVisual[key] = val;
+        }
+        return val;
+    }
+    /**
+     * Set visual property of single data item
+     *
+     * @param {number} idx
+     * @param {string|Object} key
+     * @param {*} [value]
+     *
+     * @example
+     *  setItemVisual(0, 'color', color);
+     *  setItemVisual(0, {
+     *      'color': color
+     *  });
+     */
+    // eslint-disable-next-line
+    setItemVisual<K extends keyof Visual>(idx: number, key: K, value: Visual[K]): void;
+    setItemVisual(idx: number, kvObject: Partial<Visual>): void;
+    // eslint-disable-next-line
+    setItemVisual<K extends keyof Visual>(idx: number, key: K | Partial<Visual>, value?: Visual[K]): void {
+        const itemVisual = this._itemVisuals[idx] || {};
+        this._itemVisuals[idx] = itemVisual;
+
+        if (isObject(key)) {
+            zrUtil.extend(itemVisual, key);
+        }
+        else {
+            itemVisual[key as string] = value;
+        }
+    }
+
+    /**
+     * Clear itemVisuals and list visual.
+     */
+    clearAllVisual(): void {
+        this._visual = {};
+        this._itemVisuals = [];
+    }
+
+    /**
+     * Set layout property.
+     */
+    setLayout(key: string, val: any): void;
+    setLayout(kvObj: Dictionary<any>): void;
+    setLayout(key: string | Dictionary<any>, val?: any): void {
+        if (isObject(key)) {
+            for (const name in key) {
+                if (key.hasOwnProperty(name)) {
+                    this.setLayout(name, key[name]);
+                }
+            }
+            return;
+        }
+        this._layout[key] = val;
+    }
+
+    /**
+     * Get layout property.
+     */
+    getLayout(key: string): any {
+        return this._layout[key];
+    }
+
+    /**
+     * Get layout of single data item
+     */
+    getItemLayout(idx: number): any {
+        return this._itemLayouts[idx];
+    }
+
+    /**
+     * Set layout of single data item
+     */
+    setItemLayout<M = false>(
+        idx: number,
+        layout: (M extends true ? Dictionary<any> : any),
+        merge?: M
+    ): void {
+        this._itemLayouts[idx] = merge
+            ? zrUtil.extend(this._itemLayouts[idx] || {}, layout)
+            : layout;
+    }
+
+    /**
+     * Clear all layout of single data item
+     */
+    clearItemLayouts(): void {
+        this._itemLayouts.length = 0;
+    }
+
+    /**
+     * Set graphic element relative to data. It can be set as null
+     */
+    setItemGraphicEl(idx: number, el: Element): void {
+        const hostModel = this.hostModel;
+
+        if (el) {
+            const ecData = getECData(el);
+            // Add data index and series index for indexing the data by element
+            // Useful in tooltip
+            ecData.dataIndex = idx;
+            ecData.dataType = this.dataType;
+            ecData.seriesIndex = hostModel && (hostModel as any).seriesIndex;
+
+            // TODO: not store dataIndex on children.
+            if (el.type === 'group') {
+                el.traverse(setItemDataAndSeriesIndex, el);
+            }
+        }
+
+        this._graphicEls[idx] = el;
+    }
+
+    getItemGraphicEl(idx: number): Element {
+        return this._graphicEls[idx];
+    }
+
+    eachItemGraphicEl<Ctx = unknown>(
+        cb: (this: Ctx, el: Element, idx: number) => void,
+        context?: Ctx
+    ): void {
+        zrUtil.each(this._graphicEls, function (el, idx) {
+            if (el) {
+                cb && cb.call(context, el, idx);
+            }
+        });
+    }
+
+    /**
+     * Shallow clone a new list except visual and layout properties, and graph elements.
+     * New list only change the indices.
+     */
+    cloneShallow(list?: SeriesData<HostModel>): SeriesData<HostModel> {
+        if (!list) {
+            const dimensionInfoList = map(this.dimensions, this.getDimensionInfo, this);
+            list = new SeriesData(dimensionInfoList, this.hostModel);
+        }
+
+        transferProperties(list, this);
+
+        return list;
+    }
+
+    /**
+     * Wrap some method to add more feature
+     */
+    wrapMethod(
+        methodName: FunctionPropertyNames<SeriesData>,
+        injectFunction: (...args: any) => any
+    ): void {
+        const originalMethod = this[methodName];
+        if (typeof originalMethod !== 'function') {
+            return;
+        }
+        this.__wrappedMethods = this.__wrappedMethods || [];
+        this.__wrappedMethods.push(methodName);
+        this[methodName] = function () {
+            const res = (originalMethod as any).apply(this, arguments);
+            return injectFunction.apply(this, [res].concat(zrUtil.slice(arguments)));
+        };
+    }
+
+
+    // ----------------------------------------------------------
+    // A work around for internal method visiting private member.
+    // ----------------------------------------------------------
+    private static internalField = (function () {
+
+        prepareInvertedIndex = function (list: SeriesData): void {
+            const invertedIndicesMap = list._invertedIndicesMap;
+            zrUtil.each(invertedIndicesMap, function (invertedIndices, dim) {
+                const dimInfo = list._dimensionInfos[dim];
+                // Currently, only dimensions that has ordinalMeta can create inverted indices.
+                const ordinalMeta = dimInfo.ordinalMeta;
+                const store = list._store;
+                const dimIdx = store.getDimensionIndex(dim);
+                if (ordinalMeta) {
+                    invertedIndices = invertedIndicesMap[dim] = new CtorInt32Array(
+                        ordinalMeta.categories.length
+                    );
+                    // The default value of TypedArray is 0. To avoid miss
+                    // mapping to 0, we should set it as INDEX_NOT_FOUND.
+                    for (let i = 0; i < invertedIndices.length; i++) {
+                        invertedIndices[i] = INDEX_NOT_FOUND;
+                    }
+                    for (let i = 0; i < store.count(); i++) {
+                        // Only support the case that all values are distinct.
+                        invertedIndices[store.get(dimIdx, i) as number] = i;
+                    }
+                }
+            });
+        };
+
+        getIdNameFromStore = function (
+            list: SeriesData, dimIdx: number, idx: number
+        ): string {
+            return convertOptionIdName(list.getStore().getCategory(dimIdx, idx), null);
+        };
+
+        /**
+         * @see the comment of `List['getId']`.
+         */
+        getId = function (list: SeriesData, rawIndex: number): string {
+            let id = list._idList[rawIndex];
+            if (id == null && list._idDimIdx != null) {
+                id = getIdNameFromStore(list, list._idDimIdx, rawIndex);
+            }
+            if (id == null) {
+                id = ID_PREFIX + rawIndex;
+            }
+            return id;
+        };
+
+        normalizeDimensions = function (
+            dimensions: ItrParamDims
+        ): Array<DimensionLoose> {
+            if (!zrUtil.isArray(dimensions)) {
+                dimensions = dimensions != null ? [dimensions] : [];
+            }
+            return dimensions;
+        };
+
+        validateDimensions = function (list: SeriesData, dims: DimensionIndex[]): void {
+            for (let i = 0; i < dims.length; i++) {
+                // stroage may be empty when no data, so use
+                // dimensionInfos to check.
+                if (!list.dimensions[dims[i]]) {
+                    console.error('Unkown dimension ' + dims[i]);
+                }
+            }
+        };
+
+        // Data in excludeDimensions is copied, otherwise transfered.
+        cloneListForMapAndSample = function (original: SeriesData): SeriesData {
+            const allDimensions = original.dimensions;
+            const list = new SeriesData(
+                map(allDimensions, original.getDimensionInfo, original),
+                original.hostModel
+            );
+            // FIXME If needs stackedOn, value may already been stacked
+            transferProperties(list, original);
+            return list;
+        };
+
+        setItemDataAndSeriesIndex = function (this: Element, child: Element): void {
+            const childECData = getECData(child);
+            const thisECData = getECData(this);
+            childECData.seriesIndex = thisECData.seriesIndex;
+            childECData.dataIndex = thisECData.dataIndex;
+            childECData.dataType = thisECData.dataType;
+        };
+
+        transferProperties = function (target: SeriesData, source: SeriesData): void {
+            zrUtil.each(
+                TRANSFERABLE_PROPERTIES.concat(source.__wrappedMethods || []),
+                function (propName) {
+                    if (source.hasOwnProperty(propName)) {
+                        (target as any)[propName] = (source as any)[propName];
+                    }
+                }
+            );
+
+            target.__wrappedMethods = source.__wrappedMethods;
+
+            zrUtil.each(CLONE_PROPERTIES, function (propName) {
+                (target as any)[propName] = zrUtil.clone((source as any)[propName]);
+            });
+
+            target._calculationInfo = zrUtil.extend({}, source._calculationInfo);
+        };
+
+    })();
+
+}
+
+interface SeriesData {
+    getLinkedData(dataType?: SeriesDataType): SeriesData;
+    getLinkedDataAll(): { data: SeriesData, type?: SeriesDataType }[];
+}
+
+export default SeriesData;
diff --git a/src/data/Tree.ts b/src/data/Tree.ts
index 684fcb8..207e451 100644
--- a/src/data/Tree.ts
+++ b/src/data/Tree.ts
@@ -23,8 +23,8 @@
 
 import * as zrUtil from 'zrender/src/core/util';
 import Model from '../model/Model';
-import linkList from './helper/linkList';
-import List from './List';
+import linkSeriesData from './helper/linkSeriesData';
+import SeriesData from './SeriesData';
 import createDimensions from './helper/createDimensions';
 import {
     DimensionLoose, ParsedValue, OptionDataValue,
@@ -307,7 +307,7 @@ class Tree<HostModel extends Model = Model, LevelOption = any> {
 
     root: TreeNode;
 
-    data: List;
+    data: SeriesData;
 
     hostModel: HostModel;
 
@@ -399,7 +399,7 @@ class Tree<HostModel extends Model = Model, LevelOption = any> {
     static createTree<T extends TreeNodeOption, HostModel extends Model, LevelOption>(
         dataRoot: T,
         hostModel: HostModel,
-        beforeLink?: (data: List) => void
+        beforeLink?: (data: SeriesData) => void
     ) {
 
         const tree = new Tree(hostModel);
@@ -436,12 +436,12 @@ class Tree<HostModel extends Model = Model, LevelOption = any> {
             dimensionsCount: dimMax
         });
 
-        const list = new List(dimensionsInfo, hostModel);
+        const list = new SeriesData(dimensionsInfo, hostModel);
         list.initData(listData);
 
         beforeLink && beforeLink(list);
 
-        linkList({
+        linkSeriesData({
             mainData: list,
             struct: tree,
             structAttr: 'tree'
diff --git a/src/data/helper/completeDimensions.ts b/src/data/helper/completeDimensions.ts
index f95122b..b0afd04 100644
--- a/src/data/helper/completeDimensions.ts
+++ b/src/data/helper/completeDimensions.ts
@@ -31,7 +31,7 @@ import {
     EncodeDefaulter, OptionEncodeValue, OptionEncode, DimensionName, DimensionIndex, DataVisualDimensions
 } from '../../util/types';
 import DataDimensionInfo from '../DataDimensionInfo';
-import List from '../List';
+import SeriesData from '../SeriesData';
 import { CoordDimensionDefinition, CoordDimensionDefinitionLoose } from './createDimensions';
 
 
@@ -76,7 +76,7 @@ import { CoordDimensionDefinition, CoordDimensionDefinitionLoose } from './creat
  */
 function completeDimensions(
     sysDims: CoordDimensionDefinitionLoose[],
-    source: Source | List | OptionSourceData,
+    source: Source | SeriesData | OptionSourceData,
     opt: {
         dimsDef?: DimensionDefinitionLoose[];
         encodeDef?: HashMap<OptionEncodeValue, DimensionName> | OptionEncode;
diff --git a/src/data/helper/createDimensions.ts b/src/data/helper/createDimensions.ts
index 102104c..82a625a 100644
--- a/src/data/helper/createDimensions.ts
+++ b/src/data/helper/createDimensions.ts
@@ -26,7 +26,7 @@ import {
     DimensionDefinitionLoose, OptionEncode, OptionEncodeValue,
     EncodeDefaulter, OptionSourceData, DimensionName, DimensionDefinition, DataVisualDimensions, DimensionIndex
 } from '../../util/types';
-import List from '../List';
+import SeriesData from '../SeriesData';
 import DataDimensionInfo from '../DataDimensionInfo';
 import { HashMap } from 'zrender/src/core/util';
 import OrdinalMeta from '../OrdinalMeta';
@@ -60,7 +60,7 @@ export type CreateDimensionsParams = {
  */
 export default function createDimensions(
     // TODO: TYPE completeDimensions type
-    source: Source | List | OptionSourceData,
+    source: Source | SeriesData | OptionSourceData,
     opt?: CreateDimensionsParams
 ): DataDimensionInfo[] {
     opt = opt || {};
diff --git a/src/data/helper/dataProvider.ts b/src/data/helper/dataProvider.ts
index 94a60fb..5dc74c0 100644
--- a/src/data/helper/dataProvider.ts
+++ b/src/data/helper/dataProvider.ts
@@ -36,7 +36,7 @@ import {
     DimensionName, DimensionIndex, OptionSourceData,
     DimensionIndexLoose, OptionDataItem, OptionDataValue, SourceFormat, SeriesLayoutBy, ParsedValue
 } from '../../util/types';
-import List from '../List';
+import SeriesData from '../SeriesData';
 
 export interface DataProvider {
     /**
@@ -469,7 +469,7 @@ function getMethodMapKey(sourceFormat: SourceFormat, seriesLayoutBy: SeriesLayou
 // value may be 0.91000000001, which have brings trouble to display.
 // TODO: consider how to treat null/undefined/NaN when display?
 export function retrieveRawValue(
-    data: List, dataIndex: number, dim?: DimensionName | DimensionIndexLoose
+    data: SeriesData, dataIndex: number, dim?: DimensionName | DimensionIndexLoose
     // If dimIndex is null/undefined, return OptionDataItem.
     // Otherwise, return OptionDataValue.
 ): OptionDataValue | OptionDataItem {
@@ -484,7 +484,7 @@ export function retrieveRawValue(
         return;
     }
 
-    const sourceFormat = data.getProvider().getSource().sourceFormat;
+    const sourceFormat = data.getStore().getProvider().getSource().sourceFormat;
     let dimName;
     let dimIndex;
 
@@ -510,12 +510,12 @@ export function retrieveRawValue(
  * @param dataIndex
  * @param attr like 'selected'
  */
-export function retrieveRawAttr(data: List, dataIndex: number, attr: string): any {
+export function retrieveRawAttr(data: SeriesData, dataIndex: number, attr: string): any {
     if (!data) {
         return;
     }
 
-    const sourceFormat = data.getProvider().getSource().sourceFormat;
+    const sourceFormat = data.getStore().getProvider().getSource().sourceFormat;
 
     if (sourceFormat !== SOURCE_FORMAT_ORIGINAL
         && sourceFormat !== SOURCE_FORMAT_OBJECT_ROWS
diff --git a/src/data/helper/dataStackHelper.ts b/src/data/helper/dataStackHelper.ts
index cef669c..0343014 100644
--- a/src/data/helper/dataStackHelper.ts
+++ b/src/data/helper/dataStackHelper.ts
@@ -20,7 +20,7 @@
 import {each, isString} from 'zrender/src/core/util';
 import DataDimensionInfo from '../DataDimensionInfo';
 import SeriesModel from '../../model/Series';
-import List, { DataCalculationInfo } from '../List';
+import SeriesData, { DataCalculationInfo } from '../SeriesData';
 import type { SeriesOption, SeriesStackOptionMixin, DimensionName } from '../../util/types';
 
 
@@ -30,7 +30,7 @@ import type { SeriesOption, SeriesStackOptionMixin, DimensionName } from '../../
  * we just support that stacked by index.
  *
  * @param seriesModel
- * @param dimensionInfoList The same as the input of <module:echarts/data/List>.
+ * @param dimensionInfoList The same as the input of <module:echarts/data/SeriesData>.
  *        The input dimensionInfoList will be modified.
  * @param opt
  * @param opt.stackedCoordDimension Specify a coord dimension if needed.
@@ -154,7 +154,7 @@ export function enableDataStack(
     };
 }
 
-export function isDimensionStacked(data: List, stackedDim: string /*, stackedByDim*/): boolean {
+export function isDimensionStacked(data: SeriesData, stackedDim: string /*, stackedByDim*/): boolean {
     // Each single series only maps to one pair of axis. So we do not need to
     // check stackByDim, whatever stacked by a dimension or stacked by index.
     return !!stackedDim && stackedDim === data.getCalculationInfo('stackedDimension');
@@ -165,7 +165,7 @@ export function isDimensionStacked(data: List, stackedDim: string /*, stackedByD
         // );
 }
 
-export function getStackedDimension(data: List, targetDim: string): DimensionName {
+export function getStackedDimension(data: SeriesData, targetDim: string): DimensionName {
     return isDimensionStacked(data, targetDim)
         ? data.getCalculationInfo('stackResultDimension')
         : targetDim;
diff --git a/src/data/helper/dimensionHelper.ts b/src/data/helper/dimensionHelper.ts
index b38062d..425271f 100644
--- a/src/data/helper/dimensionHelper.ts
+++ b/src/data/helper/dimensionHelper.ts
@@ -19,10 +19,11 @@
 
 
 import {each, createHashMap, assert} from 'zrender/src/core/util';
-import List, { ListDimensionType } from '../List';
+import SeriesData from '../SeriesData';
 import {
     DimensionName, VISUAL_DIMENSIONS, DimensionType, DimensionUserOuput, DimensionUserOuputEncode, DimensionIndex
 } from '../../util/types';
+import { DataStoreDimensionType } from '../DataStorage';
 
 export type DimensionSummaryEncode = {
     defaultedLabel: DimensionName[],
@@ -40,7 +41,7 @@ export type DimensionSummary = {
     encodeFirstDimNotExtra: {[coordDim: string]: DimensionName},
 };
 
-export function summarizeDimensions(data: List): DimensionSummary {
+export function summarizeDimensions(data: SeriesData): DimensionSummary {
     const summary: DimensionSummary = {} as DimensionSummary;
     const encode = summary.encode = {} as DimensionSummaryEncode;
     const notExtraCoordDimMap = createHashMap<1, DimensionName>();
@@ -140,7 +141,7 @@ function getOrCreateEncodeArr(
 }
 
 // FIXME:TS should be type `AxisType`
-export function getDimensionTypeByAxis(axisType: string): ListDimensionType {
+export function getDimensionTypeByAxis(axisType: string): DataStoreDimensionType {
     return axisType === 'category'
         ? 'ordinal'
         : axisType === 'time'
diff --git a/src/data/helper/linkList.ts b/src/data/helper/linkSeriesData.ts
similarity index 75%
rename from src/data/helper/linkList.ts
rename to src/data/helper/linkSeriesData.ts
index a7cecc4..2b767cf 100644
--- a/src/data/helper/linkList.ts
+++ b/src/data/helper/linkSeriesData.ts
@@ -23,37 +23,37 @@
  */
 
 import { curry, each, assert, extend, map, keys } from 'zrender/src/core/util';
-import List from '../List';
+import SeriesData from '../SeriesData';
 import { makeInner } from '../../util/model';
 import { SeriesDataType } from '../../util/types';
 
 // That is: { dataType: data },
 // like: { node: nodeList, edge: edgeList }.
 // Should contain mainData.
-type Datas = { [key in SeriesDataType]?: List };
+type Datas = { [key in SeriesDataType]?: SeriesData };
 type StructReferDataAttr = 'data' | 'edgeData';
 type StructAttr = 'tree' | 'graph';
 
 const inner = makeInner<{
     datas: Datas;
-    mainData: List;
-}, List>();
+    mainData: SeriesData;
+}, SeriesData>();
 
 
 // Caution:
-// In most case, either list or its shallow clones (see list.cloneShallow)
+// In most case, either seriesData or its shallow clones (see seriesData.cloneShallow)
 // is active in echarts process. So considering heap memory consumption,
-// we do not clone tree or graph, but share them among list and its shallow clones.
-// But in some rare case, we have to keep old list (like do animation in chart). So
-// please take care that both the old list and the new list share the same tree/graph.
+// we do not clone tree or graph, but share them among seriesData and its shallow clones.
+// But in some rare case, we have to keep old seriesData (like do animation in chart). So
+// please take care that both the old seriesData and the new seriesData share the same tree/graph.
 
-type LinkListOpt = {
-    mainData: List;
+type LinkSeriesDataOpt = {
+    mainData: SeriesData;
     // For example, instance of Graph or Tree.
     struct: {
         update: () => void;
     } & {
-        [key in StructReferDataAttr]?: List
+        [key in StructReferDataAttr]?: SeriesData
     };
     // Will designate: `mainData[structAttr] = struct;`
     structAttr: StructAttr;
@@ -63,7 +63,7 @@ type LinkListOpt = {
     datasAttr?: { [key in SeriesDataType]?: StructReferDataAttr };
 };
 
-function linkList(opt: LinkListOpt): void {
+function linkSeriesData(opt: LinkSeriesDataOpt): void {
     const mainData = opt.mainData;
     let datas = opt.datas;
 
@@ -76,7 +76,7 @@ function linkList(opt: LinkListOpt): void {
     linkAll(mainData, datas, opt);
 
     // Porxy data original methods.
-    each(datas, function (data: List) {
+    each(datas, function (data: SeriesData) {
         each(mainData.TRANSFERABLE_METHODS, function (methodName) {
             data.wrapMethod(methodName, curry(transferInjection, opt));
         });
@@ -95,7 +95,7 @@ function linkList(opt: LinkListOpt): void {
     assert(datas[mainData.dataType] === mainData);
 }
 
-function transferInjection(this: List, opt: LinkListOpt, res: List): unknown {
+function transferInjection(this: SeriesData, opt: LinkSeriesDataOpt, res: SeriesData): unknown {
     if (isMainData(this)) {
         // Transfer datas to new main data.
         const datas = extend({}, inner(this).datas);
@@ -109,17 +109,17 @@ function transferInjection(this: List, opt: LinkListOpt, res: List): unknown {
     return res;
 }
 
-function changeInjection(opt: LinkListOpt, res: unknown): unknown {
+function changeInjection(opt: LinkSeriesDataOpt, res: unknown): unknown {
     opt.struct && opt.struct.update();
     return res;
 }
 
-function cloneShallowInjection(opt: LinkListOpt, res: List): List {
+function cloneShallowInjection(opt: LinkSeriesDataOpt, res: SeriesData): SeriesData {
     // cloneShallow, which brings about some fragilities, may be inappropriate
     // to be exposed as an API. So for implementation simplicity we can make
     // the restriction that cloneShallow of not-mainData should not be invoked
     // outside, but only be invoked here.
-    each(inner(res).datas, function (data: List, dataType) {
+    each(inner(res).datas, function (data: SeriesData, dataType) {
         data !== res && linkSingle(data.cloneShallow(), dataType, res, opt);
     });
     return res;
@@ -131,7 +131,7 @@ function cloneShallowInjection(opt: LinkListOpt, res: List): List {
  * @public
  * @param [dataType] If not specified, return mainData.
  */
-function getLinkedData(this: List, dataType?: SeriesDataType): List {
+function getLinkedData(this: SeriesData, dataType?: SeriesDataType): SeriesData {
     const mainData = inner(this).mainData;
     return (dataType == null || mainData == null)
         ? mainData
@@ -141,8 +141,8 @@ function getLinkedData(this: List, dataType?: SeriesDataType): List {
 /**
  * Get list of all linked data
  */
-function getLinkedDataAll(this: List): {
-    data: List,
+function getLinkedDataAll(this: SeriesData): {
+    data: SeriesData,
     type?: SeriesDataType
 }[] {
     const mainData = inner(this).mainData;
@@ -156,18 +156,18 @@ function getLinkedDataAll(this: List): {
         });
 }
 
-function isMainData(data: List): boolean {
+function isMainData(data: SeriesData): boolean {
     return inner(data).mainData === data;
 }
 
-function linkAll(mainData: List, datas: Datas, opt: LinkListOpt): void {
+function linkAll(mainData: SeriesData, datas: Datas, opt: LinkSeriesDataOpt): void {
     inner(mainData).datas = {};
-    each(datas, function (data: List, dataType) {
+    each(datas, function (data: SeriesData, dataType) {
         linkSingle(data, dataType, mainData, opt);
     });
 }
 
-function linkSingle(data: List, dataType: SeriesDataType, mainData: List, opt: LinkListOpt): void {
+function linkSingle(data: SeriesData, dataType: SeriesDataType, mainData: SeriesData, opt: LinkSeriesDataOpt): void {
     inner(mainData).datas[dataType] = data;
     inner(data).mainData = mainData;
 
@@ -183,4 +183,4 @@ function linkSingle(data: List, dataType: SeriesDataType, mainData: List, opt: L
     data.getLinkedDataAll = getLinkedDataAll;
 }
 
-export default linkList;
+export default linkSeriesData;
diff --git a/src/export/api.ts b/src/export/api.ts
index 1d84814..9279b3c 100644
--- a/src/export/api.ts
+++ b/src/export/api.ts
@@ -53,7 +53,7 @@ export * as util from './api/util';
 export {default as env} from 'zrender/src/core/env';
 
 //////////////// Export for Exension Usage ////////////////
-export {default as List} from '../data/List';
+export {default as SeriesData} from '../data/SeriesData';
 export {default as Model} from '../model/Model';
 export {default as Axis} from '../coord/Axis';
 
diff --git a/src/export/api/helper.ts b/src/export/api/helper.ts
index 07d6cef..614d210 100644
--- a/src/export/api/helper.ts
+++ b/src/export/api/helper.ts
@@ -22,7 +22,7 @@
  */
 
 import * as zrUtil from 'zrender/src/core/util';
-import createListFromArray from '../../chart/helper/createListFromArray';
+import createSeriesDataFromArray from '../../chart/helper/createSeriesDataFromArray';
 // import createGraphFromNodeEdge from './chart/helper/createGraphFromNodeEdge';
 import * as axisHelper from '../../coord/axisHelper';
 import {AxisModelCommonMixin} from '../../coord/axisModelCommonMixin';
@@ -43,7 +43,7 @@ import { DisplayState, TextCommonOption } from '../../util/types';
  * Create a muti dimension List structure from seriesModel.
  */
 export function createList(seriesModel: SeriesModel) {
-    return createListFromArray(seriesModel.getSource(), seriesModel);
+    return createSeriesDataFromArray(seriesModel.getSource(), seriesModel);
 }
 
 // export function createGraph(seriesModel) {
diff --git a/src/label/labelStyle.ts b/src/label/labelStyle.ts
index def6b19..606aa94 100644
--- a/src/label/labelStyle.ts
+++ b/src/label/labelStyle.ts
@@ -38,7 +38,7 @@ import { isFunction, retrieve2, extend, keys, trim } from 'zrender/src/core/util
 import { SPECIAL_STATES, DISPLAY_STATES } from '../util/states';
 import { deprecateReplaceLog } from '../util/log';
 import { makeInner, interpolateRawValues } from '../util/model';
-import List from '../data/List';
+import SeriesData from '../data/SeriesData';
 import { initProps, updateProps } from '../util/graphic';
 import { getECData } from '../util/innerStore';
 
@@ -688,7 +688,7 @@ export function setLabelValueAnimation(
 export function animateLabelValue(
     textEl: ZRText,
     dataIndex: number,
-    data: List,
+    data: SeriesData,
     animatableModel: Model<AnimationOptionMixin>,
     labelFetcher: SetLabelStyleOpt<number>['labelFetcher']
 ) {
diff --git a/src/layout/barGrid.ts b/src/layout/barGrid.ts
index edf6400..b389d7a 100644
--- a/src/layout/barGrid.ts
+++ b/src/layout/barGrid.ts
@@ -472,7 +472,7 @@ export function layout(seriesType: string, ecModel: GlobalModel) {
 
         const valueDim = data.mapDimension(valueAxis.dim);
         const baseDim = data.mapDimension(baseAxis.dim);
-        const stacked = isDimensionStacked(data, valueDim /*, baseDim*/);
+        const stacked = isDimensionStacked(data, valueDim);
         const isValueAxisH = valueAxis.isHorizontal();
 
         const valueAxisStart = getValueAxisStart(baseAxis, valueAxis, stacked);
diff --git a/src/layout/points.ts b/src/layout/points.ts
index 8946852..2d138be 100644
--- a/src/layout/points.ts
+++ b/src/layout/points.ts
@@ -69,18 +69,19 @@ export default function pointsLayout(seriesType: string, forceStoreInTypedArray?
 
                     const tmpIn: ParsedValueNumeric[] = [];
                     const tmpOut: number[] = [];
+                    const store = data.getStore();
 
                     for (let i = params.start, offset = 0; i < params.end; i++) {
                         let point;
 
                         if (dimLen === 1) {
-                            const x = data.getByDimIdx(dimIdx0, i) as ParsedValueNumeric;
+                            const x = store.get(dimIdx0, i) as ParsedValueNumeric;
                             // NOTE: Make sure the second parameter is null to use default strategy.
                             point = coordSys.dataToPoint(x, null, tmpOut);
                         }
                         else {
-                            tmpIn[0] = data.getByDimIdx(dimIdx0, i) as ParsedValueNumeric;
-                            tmpIn[1] = data.getByDimIdx(dimIdx1, i) as ParsedValueNumeric;
+                            tmpIn[0] = store.get(dimIdx0, i) as ParsedValueNumeric;
+                            tmpIn[1] = store.get(dimIdx1, i) as ParsedValueNumeric;
                             // Let coordinate system to handle the NaN data.
                             point = coordSys.dataToPoint(tmpIn, null, tmpOut);
                         }
diff --git a/src/model/Series.ts b/src/model/Series.ts
index 9ee54c8..a47d819 100644
--- a/src/model/Series.ts
+++ b/src/model/Series.ts
@@ -40,7 +40,7 @@ import { CoordinateSystem } from '../coord/CoordinateSystem';
 import { ExtendableConstructor, mountExtend, Constructor } from '../util/clazz';
 import { PipelineContext, SeriesTaskContext, GeneralTask, OverallTask, SeriesTask } from '../core/Scheduler';
 import LegendVisualProvider from '../visual/LegendVisualProvider';
-import List from '../data/List';
+import SeriesData from '../data/SeriesData';
 import Axis from '../coord/Axis';
 import type { BrushCommonSelectorsForSeries, BrushSelectableArea } from '../component/brush/selector';
 import makeStyleMapper from './mixin/makeStyleMapper';
@@ -52,12 +52,12 @@ import {Group} from '../util/graphic';
 import {LegendIconParams} from '../component/legend/LegendModel';
 
 const inner = modelUtil.makeInner<{
-    data: List
-    dataBeforeProcessed: List
+    data: SeriesData
+    dataBeforeProcessed: SeriesData
     sourceManager: SourceManager
 }, SeriesModel>();
 
-function getSelectionKey(data: List, dataIndex: number): string {
+function getSelectionKey(data: SeriesData, dataIndex: number): string {
     return data.getName(dataIndex) || data.getId(dataIndex);
 }
 
@@ -105,7 +105,7 @@ interface SeriesModel {
      */
     brushSelector(
         dataIndex: number,
-        data: List,
+        data: SeriesData,
         selectors: BrushCommonSelectorsForSeries,
         area: BrushSelectableArea
     ): boolean;
@@ -220,7 +220,7 @@ class SeriesModel<Opt extends SeriesOption = SeriesOption> extends ComponentMode
         // dataBeforeProcessed by cloneShallow), cloneShallow will
         // cause data.graph.data !== data when using
         // module:echarts/data/Graph or module:echarts/data/Tree.
-        // See module:echarts/data/helper/linkList
+        // See module:echarts/data/helper/linkSeriesData
 
         // Theoretically, it is unreasonable to call `seriesModel.getData()` in the model
         // init or merge stage, because the data can be restored. So we do not `restoreData`
@@ -314,7 +314,7 @@ class SeriesModel<Opt extends SeriesOption = SeriesOption> extends ComponentMode
      * Init a data structure from data related option in series
      * Must be overriden.
      */
-    getInitialData(option: Opt, ecModel: GlobalModel): List {
+    getInitialData(option: Opt, ecModel: GlobalModel): SeriesData {
         return;
     }
 
@@ -335,23 +335,23 @@ class SeriesModel<Opt extends SeriesOption = SeriesOption> extends ComponentMode
      * data in the stream procedure. So we fetch data from upstream
      * each time `task.perform` called.
      */
-    getData(dataType?: SeriesDataType): List<this> {
+    getData(dataType?: SeriesDataType): SeriesData<this> {
         const task = getCurrentTask(this);
         if (task) {
             const data = task.context.data;
-            return (dataType == null ? data : data.getLinkedData(dataType)) as List<this>;
+            return (dataType == null ? data : data.getLinkedData(dataType)) as SeriesData<this>;
         }
         else {
             // When series is not alive (that may happen when click toolbox
             // restore or setOption with not merge mode), series data may
             // be still need to judge animation or something when graphic
             // elements want to know whether fade out.
-            return inner(this).data as List<this>;
+            return inner(this).data as SeriesData<this>;
         }
     }
 
     getAllData(): ({
-        data: List,
+        data: SeriesData,
         type?: SeriesDataType
     })[] {
         const mainData = this.getData();
@@ -360,7 +360,7 @@ class SeriesModel<Opt extends SeriesOption = SeriesOption> extends ComponentMode
             : [{ data: mainData }];
     }
 
-    setData(data: List): void {
+    setData(data: SeriesData): void {
         const task = getCurrentTask(this);
         if (task) {
             const context = task.context;
@@ -392,7 +392,7 @@ class SeriesModel<Opt extends SeriesOption = SeriesOption> extends ComponentMode
     /**
      * Get data before processed
      */
-    getRawData(): List {
+    getRawData(): SeriesData {
         return inner(this).dataBeforeProcessed;
     }
 
@@ -541,7 +541,6 @@ class SeriesModel<Opt extends SeriesOption = SeriesOption> extends ComponentMode
             return true;
         }
 
-        // NOTE: don't support define universalTransition in global option yet.
         const universalTransitionOpt = this.option.universalTransition;
         // Quick reject
         if (!universalTransitionOpt) {
@@ -556,7 +555,7 @@ class SeriesModel<Opt extends SeriesOption = SeriesOption> extends ComponentMode
         return universalTransitionOpt && universalTransitionOpt.enabled;
     }
 
-    private _innerSelect(data: List, innerDataIndices: number[]) {
+    private _innerSelect(data: SeriesData, innerDataIndices: number[]) {
         const selectedMode = this.option.selectedMode;
         const len = innerDataIndices.length;
         if (!selectedMode || !len) {
@@ -585,7 +584,7 @@ class SeriesModel<Opt extends SeriesOption = SeriesOption> extends ComponentMode
         }
     }
 
-    private _initSelectedMapFromData(data: List) {
+    private _initSelectedMapFromData(data: SeriesData) {
         // Ignore select info in data if selectedMap exists.
         // NOTE It's only for legacy usage. edge data is not supported.
         if (this.option.selectedMap) {
@@ -676,13 +675,13 @@ function dataTaskProgress(param: StageHandlerProgressParams, context: SeriesTask
 }
 
 // TODO refactor
-function wrapData(data: List, seriesModel: SeriesModel): void {
+function wrapData(data: SeriesData, seriesModel: SeriesModel): void {
     zrUtil.each([...data.CHANGABLE_METHODS, ...data.DOWNSAMPLE_METHODS], function (methodName) {
         data.wrapMethod(methodName as any, zrUtil.curry(onDataChange, seriesModel));
     });
 }
 
-function onDataChange(this: List, seriesModel: SeriesModel, newList: List): List {
+function onDataChange(this: SeriesData, seriesModel: SeriesModel, newList: SeriesData): SeriesData {
     const task = getCurrentTask(seriesModel);
     if (task) {
         // Consider case: filter, selectRange
diff --git a/src/processor/dataStack.ts b/src/processor/dataStack.ts
index e9559cb..88cdc68 100644
--- a/src/processor/dataStack.ts
+++ b/src/processor/dataStack.ts
@@ -21,7 +21,7 @@ import {createHashMap, each} from 'zrender/src/core/util';
 import GlobalModel from '../model/Global';
 import SeriesModel from '../model/Series';
 import { SeriesOption, SeriesStackOptionMixin, DimensionName } from '../util/types';
-import List from '../data/List';
+import SeriesData from '../data/SeriesData';
 import { addSafe } from '../util/number';
 
 interface StackInfo {
@@ -30,7 +30,7 @@ interface StackInfo {
     stackedByDimension: DimensionName
     stackResultDimension: DimensionName
     stackedOverDimension: DimensionName
-    data: List
+    data: SeriesData
     seriesModel: SeriesModel<SeriesOption & SeriesStackOptionMixin>
 }
 // (1) [Caution]: the logic is correct based on the premises:
diff --git a/src/scale/Log.ts b/src/scale/Log.ts
index 11a1c84..f8c3258 100644
--- a/src/scale/Log.ts
+++ b/src/scale/Log.ts
@@ -24,7 +24,7 @@ import * as scaleHelper from './helper';
 
 // Use some method of IntervalScale
 import IntervalScale from './Interval';
-import List from '../data/List';
+import SeriesData from '../data/SeriesData';
 import { DimensionName, ScaleTick } from '../util/types';
 
 const scaleProto = Scale.prototype;
@@ -118,7 +118,7 @@ class LogScale extends Scale {
         scaleProto.unionExtent.call(this, extent);
     }
 
-    unionExtentFromData(data: List, dim: DimensionName): void {
+    unionExtentFromData(data: SeriesData, dim: DimensionName): void {
         // TODO
         // filter value that <= 0
         this.unionExtent(data.getApproximateExtent(dim));
diff --git a/src/scale/Ordinal.ts b/src/scale/Ordinal.ts
index 76f515a..7f23c2d 100644
--- a/src/scale/Ordinal.ts
+++ b/src/scale/Ordinal.ts
@@ -26,7 +26,7 @@
 
 import Scale from './Scale';
 import OrdinalMeta from '../data/OrdinalMeta';
-import List from '../data/List';
+import SeriesData from '../data/SeriesData';
 import * as scaleHelper from './helper';
 import {
     OrdinalRawValue,
@@ -263,7 +263,7 @@ class OrdinalScale extends Scale<OrdinalScaleSetting> {
         return this._extent[1] - this._extent[0] + 1;
     }
 
-    unionExtentFromData(data: List, dim: DimensionLoose) {
+    unionExtentFromData(data: SeriesData, dim: DimensionLoose) {
         this.unionExtent(data.getApproximateExtent(dim));
     }
 
diff --git a/src/scale/Scale.ts b/src/scale/Scale.ts
index 701013c..afae3f4 100644
--- a/src/scale/Scale.ts
+++ b/src/scale/Scale.ts
@@ -20,7 +20,7 @@
 
 import * as clazzUtil from '../util/clazz';
 import { Dictionary } from 'zrender/src/core/types';
-import List from '../data/List';
+import SeriesData from '../data/SeriesData';
 import {
     DimensionName,
     ScaleDataValue,
@@ -91,7 +91,7 @@ abstract class Scale<SETTING extends Dictionary<unknown> = Dictionary<unknown>>
     /**
      * Set extent from data
      */
-    unionExtentFromData(data: List, dim: DimensionName | DimensionLoose): void {
+    unionExtentFromData(data: SeriesData, dim: DimensionName | DimensionLoose): void {
         this.unionExtent(data.getApproximateExtent(dim));
     }
 
diff --git a/src/util/model.ts b/src/util/model.ts
index b640ec6..a16377e 100644
--- a/src/util/model.ts
+++ b/src/util/model.ts
@@ -32,7 +32,7 @@ import {
 import env from 'zrender/src/core/env';
 import GlobalModel from '../model/Global';
 import ComponentModel, {ComponentModelConstructor} from '../model/Component';
-import List from '../data/List';
+import SeriesData from '../data/SeriesData';
 import {
     ComponentOption,
     ComponentMainType,
@@ -686,7 +686,7 @@ export function compressBatches(
  *                         each of which can be Array or primary type.
  * @return dataIndex If not found, return undefined/null.
  */
-export function queryDataIndex(data: List, payload: Payload & {
+export function queryDataIndex(data: SeriesData, payload: Payload & {
     dataIndexInside?: number | number[]
     dataIndex?: number | number[]
     name?: string | string[]
@@ -1032,7 +1032,7 @@ export function groupData<T, R extends string | number>(
  *                     Other cases do not supported.
  */
 export function interpolateRawValues(
-    data: List,
+    data: SeriesData,
     precision: number | 'auto',
     sourceValue: InterpolatableValue,
     targetValue: InterpolatableValue,
diff --git a/src/util/states.ts b/src/util/states.ts
index aa78811..ac5e01c 100644
--- a/src/util/states.ts
+++ b/src/util/states.ts
@@ -41,7 +41,7 @@ import {
 import { extend, indexOf, isArrayLike, isObject, keys, isArray, each } from 'zrender/src/core/util';
 import { getECData } from './innerStore';
 import * as colorTool from 'zrender/src/tool/color';
-import List from '../data/List';
+import SeriesData from '../data/SeriesData';
 import SeriesModel from '../model/Series';
 import { CoordinateSystemMaster, CoordinateSystem } from '../coord/CoordinateSystem';
 import { queryDataIndex, makeInner } from './model';
@@ -422,7 +422,7 @@ export function blurSeries(
     const ecModel = api.getModel();
     blurScope = blurScope || 'coordinateSystem';
 
-    function leaveBlurOfIndices(data: List, dataIndices: ArrayLike<number>) {
+    function leaveBlurOfIndices(data: SeriesData, dataIndices: ArrayLike<number>) {
         for (let i = 0; i < dataIndices.length; i++) {
             const itemEl = data.getItemGraphicEl(dataIndices[i]);
             itemEl && leaveBlur(itemEl);
diff --git a/src/util/types.ts b/src/util/types.ts
index 225d6cb..3e4dd22 100644
--- a/src/util/types.ts
+++ b/src/util/types.ts
@@ -33,7 +33,7 @@ import ExtensionAPI from '../core/ExtensionAPI';
 import SeriesModel from '../model/Series';
 import { createHashMap, HashMap } from 'zrender/src/core/util';
 import { TaskPlanCallbackReturn, TaskProgressParams } from '../core/task';
-import List, {ListDimensionType} from '../data/List';
+import SeriesData from '../data/SeriesData';
 import { Dictionary, ElementEventName, ImageLike, TextAlign, TextVerticalAlign } from 'zrender/src/core/types';
 import { PatternObject } from 'zrender/src/graphic/Pattern';
 import { TooltipMarker } from './format';
@@ -47,6 +47,7 @@ import { ImageStyleProps } from 'zrender/src/graphic/Image';
 import ZRText, { TextStyleProps } from 'zrender/src/graphic/Text';
 import { Source } from '../data/Source';
 import Model from '../model/Model';
+import { DataStoreDimensionType } from '../data/DataStorage';
 
 
 
@@ -128,7 +129,7 @@ export interface ECElement extends Element {
 }
 
 export interface DataHost {
-    getData(dataType?: SeriesDataType): List;
+    getData(dataType?: SeriesDataType): SeriesData;
 }
 
 export interface DataModel extends Model<unknown>, DataHost, DataFormatMixin {}
@@ -300,8 +301,8 @@ export interface StageHandlerInternal extends StageHandler {
 
 export type StageHandlerProgressParams = TaskProgressParams;
 export interface StageHandlerProgressExecutor {
-    dataEach?: (data: List, idx: number) => void;
-    progress?: (params: StageHandlerProgressParams, data: List) => void;
+    dataEach?: (data: SeriesData, idx: number) => void;
+    progress?: (params: StageHandlerProgressParams, data: SeriesData) => void;
 }
 export type StageHandlerPlanReturn = TaskPlanCallbackReturn;
 export interface StageHandlerPlan {
@@ -355,11 +356,11 @@ export type OrdinalSortInfo = {
 /**
  * `OptionDataValue` is the primitive value in `series.data` or `dataset.source`.
  * `OptionDataValue` are parsed (see `src/data/helper/dataValueHelper.parseDataValue`)
- * into `ParsedValue` and stored into `data/List` storage.
+ * into `ParsedValue` and stored into `data/SeriesData` storage.
  * Note:
  * (1) The term "parse" does not mean `src/scale/Scale['parse']`.
  * (2) If a category dimension is not mapped to any axis, its raw value will NOT be
- * parsed to `OrdinalNumber` but keep the original `OrdinalRawValue` in `src/data/List` storage.
+ * parsed to `OrdinalNumber` but keep the original `OrdinalRawValue` in `src/data/SeriesData` storage.
  */
 export type ParsedValue = ParsedValueNumeric | OrdinalRawValue;
 export type ParsedValueNumeric = number | OrdinalNumber;
@@ -414,7 +415,7 @@ export type DimensionIndex = number;
 export type DimensionIndexLoose = DimensionIndex | string;
 export type DimensionName = string;
 export type DimensionLoose = DimensionName | DimensionIndexLoose;
-export type DimensionType = ListDimensionType;
+export type DimensionType = DataStoreDimensionType;
 
 export const VISUAL_DIMENSIONS = createHashMap<number, keyof DataVisualDimensions>([
     'tooltip', 'label', 'itemName', 'itemId', 'itemGroupId', 'seriesName'
@@ -436,7 +437,7 @@ export interface DataVisualDimensions {
 }
 
 export type DimensionDefinition = {
-    type?: ListDimensionType,
+    type?: DataStoreDimensionType,
     name?: DimensionName,
     displayName?: string
 };
diff --git a/src/view/Chart.ts b/src/view/Chart.ts
index eb9db4a..8f32838 100644
--- a/src/view/Chart.ts
+++ b/src/view/Chart.ts
@@ -34,7 +34,7 @@ import {
     StageHandlerPlanReturn, DisplayState, StageHandlerProgressParams, ECElementEvent
 } from '../util/types';
 import { SeriesTaskContext, SeriesTask } from '../core/Scheduler';
-import List from '../data/List';
+import SeriesData from '../data/SeriesData';
 
 const inner = modelUtil.makeInner<{
     updateMethod: keyof ChartView
@@ -206,7 +206,7 @@ function elSetState(el: Element, state: DisplayState, highlightDigit: number) {
     }
 }
 
-function toggleHighlight(data: List, payload: Payload, state: DisplayState) {
+function toggleHighlight(data: SeriesData, payload: Payload, state: DisplayState) {
     const dataIndex = modelUtil.queryDataIndex(data, payload);
 
     const highlightDigit = (payload && payload.highlightKey != null)
diff --git a/src/visual/LegendVisualProvider.ts b/src/visual/LegendVisualProvider.ts
index c62297e..c4ff79a 100644
--- a/src/visual/LegendVisualProvider.ts
+++ b/src/visual/LegendVisualProvider.ts
@@ -1,4 +1,4 @@
-import List from '../data/List';
+import SeriesData from '../data/SeriesData';
 
 /*
 * Licensed to the Apache Software Foundation (ASF) under one
@@ -26,14 +26,14 @@ import List from '../data/List';
  */
 class LegendVisualProvider {
 
-    private _getDataWithEncodedVisual: () => List;
-    private _getRawData: () => List;
+    private _getDataWithEncodedVisual: () => SeriesData;
+    private _getRawData: () => SeriesData;
 
     constructor(
         // Function to get data after filtered. It stores all the encoding info
-        getDataWithEncodedVisual: () => List,
+        getDataWithEncodedVisual: () => SeriesData,
         // Function to get raw data before filtered.
-        getRawData: () => List
+        getRawData: () => SeriesData
     ) {
         this._getDataWithEncodedVisual = getDataWithEncodedVisual;
         this._getRawData = getRawData;
diff --git a/src/visual/commonVisualTypes.ts b/src/visual/commonVisualTypes.ts
index e279a4c..7b81214 100644
--- a/src/visual/commonVisualTypes.ts
+++ b/src/visual/commonVisualTypes.ts
@@ -17,7 +17,7 @@
 * under the License.
 */
 
-import { DefaultDataVisual } from '../data/List';
+import { DefaultDataVisual } from '../data/SeriesData';
 
 export interface LineDataVisual extends DefaultDataVisual {
     fromSymbol: string
diff --git a/src/visual/helper.ts b/src/visual/helper.ts
index e419a9a..5c60f6f 100644
--- a/src/visual/helper.ts
+++ b/src/visual/helper.ts
@@ -24,10 +24,10 @@
  * In the List module storage:
  *  'style', 'symbol', 'symbolSize'...
  */
-import List from '../data/List';
+import SeriesData from '../data/SeriesData';
 
 
-export function getItemVisualFromData(data: List, dataIndex: number, key: string) {
+export function getItemVisualFromData(data: SeriesData, dataIndex: number, key: string) {
     switch (key) {
         case 'color':
             const style = data.getItemVisual(dataIndex, 'style');
@@ -45,7 +45,7 @@ export function getItemVisualFromData(data: List, dataIndex: number, key: string
     }
 }
 
-export function getVisualFromData(data: List, key: string) {
+export function getVisualFromData(data: SeriesData, key: string) {
     switch (key) {
         case 'color':
             const style = data.getVisual('style');
@@ -63,7 +63,7 @@ export function getVisualFromData(data: List, key: string) {
     }
 }
 
-export function setItemVisualFromData(data: List, dataIndex: number, key: string, value: any) {
+export function setItemVisualFromData(data: SeriesData, dataIndex: number, key: string, value: any) {
     switch (key) {
         case 'color':
             // Make sure not sharing style object.
diff --git a/src/visual/symbol.ts b/src/visual/symbol.ts
index 1e17689..019d536 100644
--- a/src/visual/symbol.ts
+++ b/src/visual/symbol.ts
@@ -28,7 +28,7 @@ import {
     SymbolRotateCallback,
     SymbolOffsetCallback
 } from '../util/types';
-import List from '../data/List';
+import SeriesData from '../data/SeriesData';
 import SeriesModel from '../model/Series';
 import GlobalModel from '../model/Global';
 
@@ -91,7 +91,7 @@ const seriesSymbolTask: StageHandler = {
             return;
         }
 
-        function dataEach(data: List, idx: number) {
+        function dataEach(data: SeriesData, idx: number) {
             const rawValue = seriesModel.getRawValue(idx);
             const params = seriesModel.getDataParams(idx);
             hasSymbolTypeCallback && data.setItemVisual(
@@ -133,7 +133,7 @@ const dataSymbolTask: StageHandler = {
 
         const data = seriesModel.getData();
 
-        function dataEach(data: List, idx: number) {
+        function dataEach(data: SeriesData, idx: number) {
             const itemModel = data.getItemModel<SymbolOptionMixin>(idx);
             const itemSymbolType = itemModel.getShallow('symbol', true);
             const itemSymbolSize = itemModel.getShallow('symbolSize', true);
diff --git a/src/visual/visualSolution.ts b/src/visual/visualSolution.ts
index accd72d..2f014a8 100644
--- a/src/visual/visualSolution.ts
+++ b/src/visual/visualSolution.ts
@@ -30,7 +30,7 @@ import {
     DimensionLoose,
     StageHandlerProgressExecutor
 } from '../util/types';
-import List from '../data/List';
+import SeriesData from '../data/SeriesData';
 import { getItemVisualFromData, setItemVisualFromData } from './helper';
 
 const each = zrUtil.each;
@@ -135,7 +135,7 @@ export function replaceVisualOption<T extends string>(
 export function applyVisual<VisualState extends string, Scope>(
     stateList: readonly VisualState[],
     visualMappings: VisualMappingCollection<VisualState>,
-    data: List,
+    data: SeriesData,
     getValueState: (this: Scope, valueOrIndex: ParsedValue | number) => VisualState,
     scope?: Scope,
     dimension?: DimensionLoose
diff --git a/test/ut/spec/data/List.test.ts b/test/ut/spec/data/SeriesData.test.ts
similarity index 92%
rename from test/ut/spec/data/List.test.ts
rename to test/ut/spec/data/SeriesData.test.ts
index c136afc..6442cf3 100644
--- a/test/ut/spec/data/List.test.ts
+++ b/test/ut/spec/data/SeriesData.test.ts
@@ -20,7 +20,7 @@
 
 /* global Float32Array */
 
-import List from '../../../../src/data/List';
+import SeriesData from '../../../../src/data/SeriesData';
 import Model from '../../../../src/model/Model';
 import { createSourceFromSeriesDataOption, Source, createSource } from '../../../../src/data/Source';
 import { OptionDataItemObject, OptionDataValue, SOURCE_FORMAT_ARRAY_ROWS } from '../../../../src/util/types';
@@ -37,7 +37,7 @@ describe('List', function () {
     describe('Data Manipulation', function () {
 
         it('initData 1d', function () {
-            const list = new List(['x', 'y'], new Model());
+            const list = new SeriesData(['x', 'y'], new Model());
             list.initData([10, 20, 30]);
             expect(list.get('x', 0)).toEqual(10);
             expect(list.get('x', 1)).toEqual(20);
@@ -46,21 +46,21 @@ describe('List', function () {
         });
 
         it('initData 2d', function () {
-            const list = new List(['x', 'y'], new Model());
+            const list = new SeriesData(['x', 'y'], new Model());
             list.initData([[10, 15], [20, 25], [30, 35]]);
             expect(list.get('x', 1)).toEqual(20);
             expect(list.get('y', 1)).toEqual(25);
         });
 
         it('initData 2d yx', function () {
-            const list = new List(['y', 'x'], new Model());
+            const list = new SeriesData(['y', 'x'], new Model());
             list.initData([[10, 15], [20, 25], [30, 35]]);
             expect(list.get('x', 1)).toEqual(25);
             expect(list.get('y', 1)).toEqual(20);
         });
 
         it('Data with option 1d', function () {
-            const list = new List(['x', 'y'], new Model());
+            const list = new SeriesData(['x', 'y'], new Model());
             list.initData([
                 1,
                 {
@@ -73,17 +73,17 @@ describe('List', function () {
         });
 
         it('Empty data', function () {
-            const list = new List(['x', 'y'], new Model());
+            const list = new SeriesData(['x', 'y'], new Model());
             list.initData([1, '-']);
             expect(list.get('y', 1)).toBeNaN();
         });
 
         it('getRawValue', function () {
-            const list1 = new List(['x', 'y'], new Model());
+            const list1 = new SeriesData(['x', 'y'], new Model());
             // here construct a new list2 because if we only use one list
             // to call initData() twice, list._chunkCount will be accumulated
             // to 1 instead of 0.
-            const list2 = new List(['x', 'y'], new Model());
+            const list2 = new SeriesData(['x', 'y'], new Model());
 
             list1.initData([1, 2, 3]);
             expect(list1.getItemModel(1).option).toEqual(2);
@@ -93,22 +93,22 @@ describe('List', function () {
         });
 
         it('indexOfRawIndex', function () {
-            const list = new List(['x'], new Model());
+            const list = new SeriesData(['x'], new Model());
             list.initData([]);
             expect(list.indexOfRawIndex(1)).toEqual(-1);
 
-            const list1 = new List(['x'], new Model());
+            const list1 = new SeriesData(['x'], new Model());
             list1.initData([0]);
             expect(list1.indexOfRawIndex(0)).toEqual(0);
             expect(list1.indexOfRawIndex(1)).toEqual(-1);
 
-            const list2 = new List(['x'], new Model());
+            const list2 = new SeriesData(['x'], new Model());
             list2.initData([0, 1, 2, 3]);
             expect(list2.indexOfRawIndex(1)).toEqual(1);
             expect(list2.indexOfRawIndex(2)).toEqual(2);
             expect(list2.indexOfRawIndex(5)).toEqual(-1);
 
-            const list3 = new List(['x'], new Model());
+            const list3 = new SeriesData(['x'], new Model());
             list3.initData([0, 1, 2, 3, 4]);
             expect(list3.indexOfRawIndex(2)).toEqual(2);
             expect(list3.indexOfRawIndex(3)).toEqual(3);
@@ -121,14 +121,14 @@ describe('List', function () {
         });
 
         it('getDataExtent', function () {
-            const list = new List(['x', 'y'], new Model());
+            const list = new SeriesData(['x', 'y'], new Model());
             list.initData([1, 2, 3]);
             expect(list.getDataExtent('x')).toEqual([1, 3]);
             expect(list.getDataExtent('y')).toEqual([1, 3]);
         });
 
         it('Data types', function () {
-            const list = new List([{
+            const list = new SeriesData([{
                 name: 'x',
                 type: 'int'
             }, {
@@ -141,7 +141,7 @@ describe('List', function () {
         });
 
         it('map', function () {
-            const list = new List(['x', 'y'], new Model());
+            const list = new SeriesData(['x', 'y'], new Model());
             list.initData([[10, 15], [20, 25], [30, 35]]);
             expect(list.map(['x', 'y'], function (x: number, y: number) {
                 return [x + 2, y + 2];
@@ -151,7 +151,7 @@ describe('List', function () {
         });
 
         it('mapArray', function () {
-            const list = new List(['x', 'y'], new Model());
+            const list = new SeriesData(['x', 'y'], new Model());
             list.initData([[10, 15], [20, 25], [30, 35]]);
             expect(list.mapArray(['x', 'y'], function (x, y) {
                 return [x, y];
@@ -159,7 +159,7 @@ describe('List', function () {
         });
 
         it('filterSelf', function () {
-            const list = new List(['x', 'y'], new Model());
+            const list = new SeriesData(['x', 'y'], new Model());
             list.initData([[10, 15], [20, 25], [30, 35]]);
             expect(list.filterSelf(['x', 'y'], function (x, y) {
                 return x < 30 && x > 10;
@@ -169,7 +169,7 @@ describe('List', function () {
         });
 
         it('dataProvider', function () {
-            const list = new List(['x', 'y'], new Model());
+            const list = new SeriesData(['x', 'y'], new Model());
             const typedArray = new Float32Array([10, 10, 20, 20]);
             const source = createSourceFromSeriesDataOption(typedArray);
             list.initData({
@@ -193,7 +193,7 @@ describe('List', function () {
 
     describe('Data read', function () {
         it('indicesOfNearest', function () {
-            const list = new List(['value'], new Model());
+            const list = new SeriesData(['value'], new Model());
             // ---- index: 0   1   2   3   4   5   6   7
             list.initData([10, 20, 30, 35, 40, 40, 35, 50]);
 
@@ -213,7 +213,7 @@ describe('List', function () {
 
     describe('id_and_name', function () {
 
-        function makeOneByOneChecker(list: List) {
+        function makeOneByOneChecker(list: SeriesData) {
             let getIdDataIndex = 0;
             let getNameDataIndex = 0;
 
@@ -237,7 +237,7 @@ describe('List', function () {
 
         describe('only_name_declared', function () {
 
-            function doChecks(list: List) {
+            function doChecks(list: SeriesData) {
                 const oneByOne = makeOneByOneChecker(list);
 
                 oneByOne.idEqualsTo('a');
@@ -260,7 +260,7 @@ describe('List', function () {
             }
 
             it('sourceFormatOriginal', function () {
-                const list = new List(['x', 'y'], new Model());
+                const list = new SeriesData(['x', 'y'], new Model());
                 list.initData([
                     { value: 10, name: 'a' },
                     { value: 20, name: 'b' },
@@ -276,7 +276,7 @@ describe('List', function () {
             });
 
             it('sourceFormatArrayRows', function () {
-                const list = new List(
+                const list = new SeriesData(
                     [
                         'x',
                         { name: 'q', type: 'ordinal', otherDims: { itemName: 0 } }
@@ -314,7 +314,7 @@ describe('List', function () {
         describe('id_name_declared_sourceFormat_original', function () {
 
             it('sourceFormatOriginal', function () {
-                const list = new List(['x'], new Model());
+                const list = new SeriesData(['x'], new Model());
                 const oneByOne = makeOneByOneChecker(list);
 
                 list.initData([
@@ -407,7 +407,7 @@ describe('List', function () {
 
         describe('id_name_declared_sourceFormat_arrayRows', function () {
 
-            function makeChecker(list: List) {
+            function makeChecker(list: SeriesData) {
                 const oneByOne = makeOneByOneChecker(list);
                 return {
                     checkAfterInitData() {
@@ -491,7 +491,7 @@ describe('List', function () {
             });
 
             function testArrayRowsInSource(dimensionsInfo: DataDimensionInfo[]): void {
-                const list = new List(dimensionsInfo, new Model());
+                const list = new SeriesData(dimensionsInfo, new Model());
                 const checker = makeChecker(list);
 
                 const source = createSource(

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org