基于LinkedList高性能android列表适配器

news2024/9/21 10:37:52

1、前言

        我们大部分的时候都是使用ArrayList作为Android适配器Adapter(无论是ListView或者RecyclerView)下的数据容器。为什么使用ArrayList呢?因为他的内部是由数组实现的,所以访问数组元素速度最快,但是如果数据变化(增加、删除、顺序变更)速度较慢,而且需要开辟连续大空间。

        对应的LinkedList双向链表效果跟ArrayList相反,更新更快;查询速度更慢一些。不要求内存数据是连续的。

2、背景

        目前在维护小说书架需求。这个场景下 其实 展示的书籍场景不是很频繁,但是用户点击书籍之后,会重新排序 然后进行展示出来,用户加入书架、删除书架书籍的情况还是比较多的。想做滑动速度优化。所以我感觉是不是可以优化一下LinkedList的访问速度,从而达到一种完美的方案呢?

3、解决方案

幸亏我们应用的场景是一个一块屏幕内的列表,每次获取数据的时候 也不是都从0开始获取。

我们知道一个列表(无论是RecyclerView或者ListView),获取数据的都是一个方法

@Override
    public View getView(int position, View convertView, ViewGroup parent) {
        T holder;
        if (convertView == null) {
            int viewType = getItemViewType(position);
            int layoutId = getLayoutId(viewType);

            convertView = LayoutInflater.from(context.getContext()).inflate(layoutId, parent, false);
            holder = getViewHolder(convertView);
            convertView.setTag(holder);
        } else {
            holder = (T) convertView.getTag();
        }
        holder.setViewHolderData(position, getItem(position));
        return convertView;
    }

 每一次都是从getView中获取指定的position,而这个position都是有规律的:

如果向上滑动,那么这个position值从0开始 到展示的最下面的点位。

如果向下滑动,那么这个position值从下底部开始,往上数到上点位

为了快速的找到我们的数据,我们利用 上点位的数据和下点位的数据。快读定位我们下一个展示的数据值

比如我们现在的下点位是10,我想向上滑动一个item,那么这个位置就是11,我们可以从下点位的node值为10,快读找到他的next的node值11,而不用从0开始数到这个位置。

向下滑动也是一样的,比如目前上点位是5,再向下滑动一个item,向拿到4的位置数据,可以从5这个node找到前驱pre值。

每次滑动的时候更新这两个位置的node 就可以快读在链表LinkedList中找到对应的值了。几乎可以达到O(0),而不是O(n)的目的。

4、实施方案

4.1、适配器获取数据改造-getItem()

@Override
    public LinkedBean getItem(int position) {
        if (isOptim) {
            if (realList != null) {
                // 如果有notify刷新的话 那么重新去构建数据
                if (isChange) {
                    lastPositon = -1;
                }
                if (listView != null) {
                    // 获取当前最上面的那个view 是位置号,
                    int currentFirstPositon = listView.getFirstVisiblePosition();
                    // 但是listview有一个问题 getFirstVisiblePosition方法获取的是展示的第一个完整的位置;
                    // 如果出现一部分 就出现了位置不准问题 因此增加一个兼容
                    if (position < currentFirstPositon) {
                        currentFirstPositon = position;
                    }
                    // 如果是刷新notifydata 或者首次进入 那么需要重头开始查询位置
                    if (lastPositon == currentFirstPositon || lastPositon == -1) {
                        isChange = false;
                        lastPositon = currentFirstPositon;
                        if (isChange || lastNode == null) {
                            lastNode = realList.getNode(position);
                            return lastNode.item;
                        } else {
                            // 如果没有滑动 那么通过headBeanNode这个基准 就不变
                            // lastFirstPositon和headBeanNode是一堆映射 ;通过diff去链表上查询
                            int diff = position - lastPositon;
                            MyLinkedList.Node<LinkedBean> item = realList.getNode(lastNode, diff);
                            if (item != null) {
                                return item.item;
                            }
                        }
                    } else {
                        // 如果如果滑动了 那么需要更新基准的lastFirstPositon和headBeanNode基准
                        int diff1 = currentFirstPositon - lastPositon;
                        MyLinkedList.Node<LinkedBean> diffItem = realList.getNode(lastNode, diff1);
                        lastNode = diffItem;
                        lastPositon = currentFirstPositon;
                        // 基准更新完了 重复上面的相同的方法 通过diff去查找
                        int diff2 = position - lastPositon;
                        MyLinkedList.Node<LinkedBean> item = realList.getNode(lastNode, diff2);
                        if (item != null) {
                            return item.item;
                        }
                    }
                }
                LinkedBean bean = super.getItem(position);
                return bean;
            } else {
                LinkedBean bean = super.getItem(position);
                return bean;
            }
        } else {
            LinkedBean bean = super.getItem(position);
            return bean;
        }
    }

我们的方案就是记住上次展示的position和对应的Node数据;当用户滑动的时候 获取新的postion的时候,我们通过链表去找当前的node向前或者向后diff个节点值,就是我们想要的node数据了。

4.2、改造LinkedList

public Node<E> getNode(int index){
        checkElementIndex(index);
        return node(index);
    }

    public Node<E> getNode(Node<E> node,int index){
//        checkElementIndex(index);
        return node(node,index);
    }
 /**
     * Returns the (non-null) Node at the specified element index.
     */
    Node<E> node(Node<E> node,int diff) {
        // assert isElementIndex(index);
        if (diff > 0) {
            Node<E> x = node;
            for (int i = 0; i < diff; i++) {
                if (x != null) {
                    x = x.next;
                }
            }
            return x;
        } else {
            diff = -diff;
            Node<E> x = node;
            for (int i = 0; i < diff; i++) {
                if (x != null) {
                    x = x.prev;
                }
            }
            return x;
        }
    }

由于LinkedList里面没有我们想要的这种方法getNode的方法;

原版的LinkedList只能拿到Node里面的值,但是我们想获取这个Node,所以只能自己重写LinkedList。拷贝一份,然后添加这两个方法就可以了

5、性能对比

5.1、获取数据

每次获取数据的时间进行对比:我们使用的是累计时间处于请求次数,求取的平均时间。

5.1.1、优化前的时间:

滑动到了第3323条数据的时候情况;达到了0.02195毫秒;随着数据的增大,每次获取的时间会越来越长!

 5.1.2、优化后的时间:

每一次的时间差不多都在0.0055毫秒左右

5.1.3、如果使用ArrayList的话

耗费的时间 0.0037毫秒,数据阅读,时间趋近于0 ,这里是滑动到10515条数据

5.2、修改时间

LinkedList:删除了25400次,平均时间0.00429毫秒左右 

ArrayList:删除了25400次,平均耗时0.22728346号码

差距真的很明显啊!

删除的速度LinkedList是ArrayList的52.9倍!

读取的速度LinkedList是ArrayList的0.672倍! 但是这个意义不大 每次读取的速度差不多为0.0055毫秒 已经很短了!

附上源码地址:

有分的点点下面的

https://download.csdn.net/download/huazai30000/87541606

没分的去github吧

GitHub - xuhuawei131/LinkedAdapter: LinkedList数据结构的适配器

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

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

相关文章

JNI内两种方式从C/C++中传递一维、二维、三维数组数据至Java层详细梳理

目录 0 前言 1 准备工作介绍 2 一维数组 2.1 return形式 2.2 参数形式 3 二维数组 3.1 return形式 3.2 参数形式 4 三维数组 4.1 return形式 4.2 参数形式 5 测试代码 6 结果说明 0 前言 就如之前我写过的一篇文章【JNI内形参从C代码中获取返回值并返回到Java层使…

Elsevier上传LaTeX 修改稿踩坑

背景 千辛万苦修改完论文&#xff0c;结果发现要求上传可编辑文件&#xff0c;tex上传真的太难了&#xff0c;一堆坑&#xff0c;尤其是编译错误&#xff0c;要等系统创建pdf后才能找到。中间还打了北京的客服电话&#xff0c;结果他们那边并不懂相关的东西。说latex是第三方公…

C语言-基础了解-22-C文件读写

C文件读写 一、打开文件 可以使用 fopen( ) 函数来创建一个新的文件或者打开一个已有的文件&#xff0c;这个调用会初始化类型 FILE 的一个对象&#xff0c;类型 FILE 包含了所有用来控制流的必要的信息。下面是这个函数调用的原型&#xff1a; FILE *fopen( const char *fil…

JS中undefined和null的区别

● JavaScript 真是一个特殊的语言, 其他语言都只有一个表示 “无” 的值, 比如 Java 语言用的是 null, C 语言用的是 NULL, Python 语言用的是 None, Ruby 语言用的是 nil. 只有 JS 里面表示 “空” 的有两个, 一个是 undefined, 一个是 null ● 很多小朋友在刚开始学习的时候…

【项目设计】高并发内存池(六)[细节优化+测试]

&#x1f387;C学习历程&#xff1a;入门 博客主页&#xff1a;一起去看日落吗持续分享博主的C学习历程博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a; 也许你现在做的事情&#xff0c;暂时看不到成果&#xff0c;但不要忘记&…

[ 网络 ] 应用层协议 —— HTTP协议

目录 1.HTTP协议 1.1URL urlencode和urldecode 2. HTTP协议格式 HTTP请求 HTTP响应 3.告知服务器意图的HTTP方法 GET&#xff1a;获取资源 POST&#xff1a;传输实体主体 GET和POST的区别 使用Cookie的状态管理 4.返回结果的HTTP状态码 状态码告知从服务器端返回的…

十连胜!实在智能×浙江省十家农商行,数字科技赋能普惠金融

近日&#xff0c;中共中央、国务院印发了《数字中国建设整体布局规划》&#xff08;以下简称《规划》&#xff09;。《规划》指出&#xff0c;建设数字中国是数字时代推进中国式现代化的重要引擎&#xff0c;是构筑国家竞争新优势的有力支撑。全面提升数字中国建设的整体性、系…

C语言-基础了解-23-C预处理器

C预处理器 一、C预处理器 C 预处理器不是编译器的组成部分&#xff0c;但是它是编译过程中一个单独的步骤。简言之&#xff0c;C 预处理器只不过是一个文本替换工具而已&#xff0c;它们会指示编译器在实际编译之前完成所需的预处理。我们将把 C 预处理器&#xff08;C Prepr…

spring-security2

参考b站up主&#xff1a;传送门 前沿 &#xff1a; 回顾上章&#xff1a;最顶层就是一个过滤器 SpringSecurity流程&#xff1a;FilterChainProxy->根据请求决定一个 SecurityFilterChain (挨个调用其matches方法 匹配请求能否处理)-> 执行SecurityFilterChain中的一系列…

Java简单认识泛型——图文详解

写在开头:想必大家和博主一样&#xff0c;在以往学习JavaSE的语法中&#xff0c;遇到了一个陌生的词——泛型&#xff0c;博主当时很好奇&#xff0c;什么是泛型呢&#xff1f;即使是学完了JavaSE&#xff0c;这个问题都没有解决&#xff0c;只能在百度查阅了解关于泛型的一些皮…

Maven依赖的基本概念

目录 1.依赖的基本配置 2.依赖范围 3.传递性依赖 1.依赖的基本配置 根元素project下的dependencies可以包含多个 dependence元素&#xff0c;以声明多个依赖。每个依赖都应该包含以下元素&#xff1a; 1. groupId, artifactId, version : 依赖的基本坐标&#xff0c; 对于任…

【C和C++】输出100内能够被13整除的数,取模判断方法

目录 前言基础概念重温整除例子小知识点收尾前言 在软件行业已经有快十年,技术虽然一般般,但是足够应付和解决编程入门的相关问题! 都说十年磨一剑,积累到一定经验,是时候发挥自己的价值,给予入门的同行些许的帮助! 为什么要写收费专栏,其实原因很简单,时间就是金钱(…

聚观早报 |拼多多跨境电商业务正式登陆澳洲;中国加快6G网络研发

今日要闻&#xff1a;拼多多跨境电商业务正式登陆澳洲&#xff1b;全球自动驾驶公司排名特斯拉垫底&#xff1b;中国将加快 6G 网络研发&#xff1b;B站再次“崩”上热搜&#xff01;已闪电修复&#xff1b;微软将必应AI聊天每次对话上限增加至8条拼多多跨境电商业务正式登陆澳…

大神教你在 Linux 中查看你的时区

在这篇短文中&#xff0c;我们将向你简单介绍几种 Linux 下查看系统时区的简单方法。在 Linux 机器中&#xff0c;尤其是生产服务器上的时间管理技能&#xff0c;是在系统管理中一个极其重要的方面。Linux 包含多种可用的时间管理工具&#xff0c;比如 date 或 timedatectlcomm…

自动化测试——执行javaScript脚本

文章目录一、点击元素(对应的click())二、input标签对应的值&#xff08;对应的send_keys()&#xff09;修改时间控件的属性值&#xff1a;三、元素的文本属性四、js脚本滚动操作一、点击元素(对应的click()) 使用场景&#xff1a;当使用显性等待不能解决问题时 代码中实现点击…

[神经网络]DETR目标检测网络

一、概述 相较于传统目标检测&#xff0c;DETR是一种纯端到端的网络。它不再需要NMS(非极大值抑制&#xff0c;用于去除多余的预测框)和生成anchor。 DETR提出了一个新的目标函数&#xff08;二分图匹配&#xff09;&#xff0c;这个函数可以强制网络输出一个独一无二的预测值&…

【Unity大气渲染】Unity Shader中实现大气散射(半成品)

写在前面 这是之前在做天空盒的时候同步写的分析博客&#xff0c;结果后面写到一半就忘了继续了&#xff0c;这里先贴出当时写的半成品&#xff0c;有小伙伴问我怎么做的&#xff0c;这里只能尽力把之前的半成品先放出来了&#xff08;写得很乱&#xff0c;勿怪orz&#xff09…

用C语言实现一个任意类型的队列

下面是一个简单的无类型队列的实现&#xff1a; #include <stdio.h> #include <stdlib.h>typedef struct Node {void *data;struct Node *next; } Node;typedef struct Queue {Node *front;Node *rear;int size; } Queue;void enqueue(Queue *queue, void *data);…

[蓝桥杯] 枚举、模拟和排列问题

文章目录 一、连号区间数 1、1 题目描述 1、2 题解关键思路与解答 二、递增三元组 2、1 题目描述 2、2 题解关键思路与解答 三、错误票据 3、1 题目描述 3、2 题解关键思路与解答 四、回文日期 4、1 题目描述 4、2 题解关键思路与解答 五、归并排序 标题&#xff1a;蓝桥杯——…

windows和linux出现timewait过多的解决方法

一、timewait出现在客户端还是服务端以及什么情况下出现 我是做性能测试的。在压测过程中遇到了timewait过多的情况&#xff0c;下面来看一下timewait产生的原因及解决办法&#xff0c;我自己在服务器起了一个很简单的springboot应用来验证自己的猜想及解决办法。 说到产生原…