0

0

Java 应用中管理近期使用列表元素的索引

碧海醫心

碧海醫心

发布时间:2025-10-05 16:53:01

|

187人浏览过

|

来源于php中文网

原创

Java 应用中管理近期使用列表元素的索引

本教程旨在指导开发者如何在Java应用程序中高效地实现“近期使用”功能,例如食谱应用中的“近期食谱”列表。文章将详细介绍如何使用固定大小的列表来存储近期访问的元素索引,并采用滑动窗口机制来维护列表的更新,确保始终显示最新的N个项目。此外,还将探讨数据持久化、性能优化及其他注意事项,以构建健壮的近期使用功能。

引言:管理近期使用食谱

在许多应用程序中,显示用户近期查看或操作过的项目是一项常见且有用的功能,例如电商应用的“最近浏览”、文档编辑器的“最近打开文件”等。对于食谱应用而言,实现一个“近期食谱”功能可以显著提升用户体验,允许用户快速回溯他们感兴趣的食谱。本教程将以一个食谱应用为例,演示如何高效地管理并获取最近使用的食谱索引。

假设我们有一个食谱数据库 ReceiptsBase,其中包含多个食谱,每个食谱由一个 ArrayList 表示,存储了图片资源ID、名称资源ID等信息。我们的目标是创建一个固定大小的“近期食谱”列表,例如显示最近使用的三个食谱。

核心概念:固定大小的近期列表

要实现“近期使用”功能,最核心的思路是维护一个固定大小的数据结构(如数组或 ArrayList),当用户使用(查看)某个食谱时,将其添加到这个数据结构中。当数据结构已满时,需要采用一种“滑动窗口”机制:移除最旧的元素,并添加新的元素,从而始终保持列表中存储的是最新的N个项目。

在这个场景中,由于我们的食谱存储在 ReceiptsBase 的 recipesAll 中,并且通过索引访问,因此在“近期食谱”列表中存储食谱的索引是最有效的方式。

实现策略:基于数组/列表的滑动窗口

我们将创建一个专门的类来管理近期使用的食谱索引。这个类将包含一个固定大小的 ArrayList 或数组来存储这些索引。

立即学习Java免费学习笔记(深入)”;

1. 定义 RecentRecipesManager 类

首先,我们创建一个 RecentRecipesManager 类,它将负责存储、添加和获取近期食谱的索引。

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class RecentRecipesManager {
    private static final int MAX_RECENT_RECIPES = 3; // 定义近期食谱的最大数量
    private List recentRecipeIndices; // 存储近期食谱的索引

    public RecentRecipesManager() {
        // 使用 LinkedList 在添加/删除两端元素时效率更高
        // 如果对访问中间元素有较高要求,也可以使用 ArrayList
        recentRecipeIndices = new ArrayList<>();
    }

    /**
     * 将一个食谱索引添加到近期食谱列表。
     * 如果食谱已在列表中,则将其移到最前面(表示最近使用)。
     * 如果列表已满,则移除最旧的食谱。
     *
     * @param recipeIndex 要添加的食谱索引
     */
    public void addRecipeToRecent(int recipeIndex) {
        // 1. 移除现有条目(如果已存在),确保不重复且是最新使用
        recentRecipeIndices.remove(Integer.valueOf(recipeIndex));

        // 2. 将新食谱索引添加到列表的最前面 (索引0)
        recentRecipeIndices.add(0, recipeIndex);

        // 3. 如果列表大小超过最大限制,则移除最旧的食谱 (列表末尾)
        if (recentRecipeIndices.size() > MAX_RECENT_RECIPES) {
            recentRecipeIndices.remove(recentRecipeIndices.size() - 1);
        }
    }

    /**
     * 获取当前近期食谱的索引列表。
     * 返回的列表是不可修改的,以防止外部意外修改。
     *
     * @return 近期食谱索引的列表
     */
    public List getRecentRecipeIndices() {
        return Collections.unmodifiableList(recentRecipeIndices);
    }

    /**
     * 清空近期食谱列表。
     */
    public void clearRecentRecipes() {
        recentRecipeIndices.clear();
    }
}

代码解析:

  • MAX_RECENT_RECIPES: 定义了我们希望维护的近期食谱的最大数量。
  • recentRecipeIndices: 一个 List,用于存储食谱在 ReceiptsBase.recipesAll 中的索引。
  • addRecipeToRecent(int recipeIndex) 方法是核心:
    • 它首先尝试从列表中移除 recipeIndex 的任何现有实例。这是为了确保如果用户再次查看一个已经存在于“近期”列表中的食谱,该食谱会被视为最新使用,并被移到列表的最前面,而不是在末尾重复添加。
    • 然后,它将 recipeIndex 添加到列表的索引0位置。这意味着 recentRecipeIndices.get(0) 总是返回最近使用的食谱。
    • 最后,如果列表的大小超过 MAX_RECENT_RECIPES,它会移除列表中的最后一个元素(即最旧的食谱)。
  • getRecentRecipeIndices(): 返回当前近期食谱的索引列表。使用 Collections.unmodifiableList() 是一个良好的实践,可以防止外部代码直接修改这个列表,从而维护 RecentRecipesManager 的内部状态一致性。

2. 集成到食谱应用

在 MainActivity 或其他负责显示食谱详情的 Activity 中,当用户点击一个食谱时,需要调用 RecentRecipesManager 来更新近期食谱列表。

假设 ReceiptsBase 类保持不变。

在 MainActivity 中:

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import androidx.appcompat.app.AppCompatActivity;

import java.util.List;

public class MainActivity extends AppCompatActivity {

    private RecentRecipesManager recentRecipesManager;
    private ReceiptsBase receiptsBase; // 假设 ReceiptsBase 是单例或通过某种方式获取实例

    // 假设这些是近期食谱的ImageButton
    private ImageButton recent1, recent2, recent3;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // hideSystemUI(); // 根据需要保留或移除

        // 初始化 RecentRecipesManager
        recentRecipesManager = new RecentRecipesManager();
        // 初始化 ReceiptsBase (这里仅作示例,实际应用中可能通过依赖注入或单例模式获取)
        receiptsBase = new ReceiptsBase();

        // 查找按钮
        Button button_recipes = findViewById(R.id.button2);
        button_recipes.setOnClickListener(view -> openRecipes());

        Button button_search = findViewById(R.id.button3);
        button_search.setOnClickListener(view -> openSearch());

        Button button_supriseme = findViewById(R.id.button4);
        button_supriseme.setOnClickListener(view -> openSupriseMe());

        // 查找近期食谱的 ImageButton
        recent1 = findViewById(R.id.rec1);
        recent2 = findViewById(R.id.rec2);
        recent3 = findViewById(R.id.rec3);

        // 初始化显示近期食谱
        updateRecentRecipesUI();
    }

    @Override
    protected void onResume() {
        super.onResume();
        // 每次回到主界面时,刷新近期食谱显示
        updateRecentRecipesUI();
    }

    // 当用户点击一个食谱时调用此方法,并传递食谱索引
    public void openRecipe(int recipeIndex){
        // 1. 更新近期食谱管理器
        recentRecipesManager.addRecipeToRecent(recipeIndex);

        // 2. 启动食谱详情页面
        Intent intent = new Intent(this, Example.class); // 假设 Example 是食谱详情页
        intent.putExtra("recipeIndex", recipeIndex);
        startActivity(intent);
        // finish(); // 根据应用流程决定是否关闭当前Activity
    }

    /**
     * 更新UI上近期食谱的显示。
     */
    private void updateRecentRecipesUI() {
        List recentIndices = recentRecipesManager.getRecentRecipeIndices();

        // 隐藏所有近期食谱按钮,然后根据实际数量显示
        recent1.setVisibility(View.GONE);
        recent2.setVisibility(View.GONE);
        recent3.setVisibility(View.GONE);

        // 遍历近期食谱索引,并设置对应的ImageButton
        if (recentIndices.size() > 0) {
            int index0 = recentIndices.get(0);
            // 假设食谱的第一个元素是图片资源ID
            recent1.setImageDrawable(getDrawable(receiptsBase.getReceipt(index0).get(0)));
            recent1.setOnClickListener(view -> openRecipe(index0));
            recent1.setVisibility(View.VISIBLE);
        }
        if (recentIndices.size() > 1) {
            int index1 = recentIndices.get(1);
            recent2.setImageDrawable(getDrawable(receiptsBase.getReceipt(index1).get(0)));
            recent2.setOnClickListener(view -> openRecipe(index1));
            recent2.setVisibility(View.VISIBLE);
        }
        if (recentIndices.size() > 2) {
            int index2 = recentIndices.get(2);
            recent3.setImageDrawable(getDrawable(receiptsBase.getReceipt(index2).get(0)));
            recent3.setOnClickListener(view -> openRecipe(index2));
            recent3.setVisibility(View.VISIBLE);
        }
    }

    // 其他 openXXX 方法保持不变
    public void openRecipes(){
        Intent rec = new Intent(this, recipes.class);
        startActivity(rec);
        // finish();
    }
    public void openSearch(){
        Intent sea = new Intent(this, search.class);
        startActivity(sea);
        // finish();
    }
    public void openSupriseMe(){
        Intent sup = new Intent(this, Example.class); // 假设 Example 是一个通用页面
        startActivity(sup);
        // finish();
    }
}

关键点:

  • 在 MainActivity 的 onCreate 中初始化 RecentRecipesManager。
  • openRecipe(int recipeIndex) 方法现在在启动食谱详情页之前,会调用 recentRecipesManager.addRecipeToRecent(recipeIndex) 来更新近期列表。
  • updateRecentRecipesUI() 方法负责根据 recentRecipesManager 中存储的索引来更新三个 ImageButton 的图片和点击事件。它会隐藏所有按钮,然后根据实际的近期食谱数量来显示和设置它们。
  • 在 onResume() 中调用 updateRecentRecipesUI() 确保每次用户返回主界面时,近期食谱显示都是最新的。

数据持久化考量

目前 RecentRecipesManager 仅在内存中维护近期食谱列表。这意味着一旦应用程序进程被杀死,所有近期食谱数据都会丢失。为了在应用重启后依然保留这些数据,我们需要进行数据持久化。

Yes!SUN企业网站系统 3.5 Build 20100303
Yes!SUN企业网站系统 3.5 Build 20100303

Yes!Sun基于PHP+MYSQL技术,体积小巧、应用灵活、功能强大,是一款为企业网站量身打造的WEB系统。其创新的设计理念,为企业网的开发设计及使用带来了全新的体验:支持前沿技术:动态缓存、伪静态、静态生成、友好URL、SEO设置等提升网站性能、用户体验、搜索引擎友好度的技术均为Yes!Sun所支持。易于二次开发:采用独创的平台化理念,按需定制项目中的各种元素,如:产品属性、产品相册、新闻列表

下载

在 Android 开发中,常用的持久化方式包括:

  1. SharedPreferences: 适用于存储少量键值对数据。对于存储近期食谱的索引列表非常适用。你可以将索引列表序列化成一个字符串(例如,用逗号分隔),然后存储在 SharedPreferences 中。

    • 存储: 当 recentRecipeIndices 列表发生变化时,将其转换为字符串并保存。
    • 加载: 在 RecentRecipesManager 构造函数中,从 SharedPreferences 读取字符串,解析回 List
    // 在 RecentRecipesManager 中添加持久化逻辑
    import android.content.Context;
    import android.content.SharedPreferences;
    import java.util.Arrays;
    import java.util.stream.Collectors;
    
    public class RecentRecipesManager {
        // ... (现有代码) ...
        private static final String PREFS_NAME = "RecentRecipesPrefs";
        private static final String KEY_RECENT_INDICES = "recentRecipeIndices";
        private Context context; // 需要在构造函数中传入Context
    
        public RecentRecipesManager(Context context) {
            this.context = context.getApplicationContext(); // 使用ApplicationContext防止内存泄漏
            recentRecipeIndices = new ArrayList<>();
            loadRecentRecipes(); // 加载已保存的近期食谱
        }
    
        @Override // addRecipeToRecent 方法修改
        public void addRecipeToRecent(int recipeIndex) {
            // ... (现有逻辑) ...
            saveRecentRecipes(); // 每次更新后保存
        }
    
        private void saveRecentRecipes() {
            SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
            SharedPreferences.Editor editor = prefs.edit();
            // 将List转换为逗号分隔的字符串
            String indicesString = recentRecipeIndices.stream()
                                                    .map(String::valueOf)
                                                    .collect(Collectors.joining(","));
            editor.putString(KEY_RECENT_INDICES, indicesString);
            editor.apply(); // 异步保存
        }
    
        private void loadRecentRecipes() {
            SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
            String indicesString = prefs.getString(KEY_RECENT_INDICES, "");
            if (!indicesString.isEmpty()) {
                recentRecipeIndices = Arrays.stream(indicesString.split(","))
                                            .map(Integer::parseInt)
                                            .collect(Collectors.toCollection(ArrayList::new));
            }
        }
    }

    并在 MainActivity 中相应地实例化 RecentRecipesManager: recentRecipesManager = new RecentRecipesManager(this);

  2. Room Persistence Library: 如果食谱数据本身也存储在本地数据库中,或者近期食谱列表需要更复杂的查询和管理,可以考虑使用 Room。将近期食谱的索引作为数据库中的一个单独表或字段进行管理。

优化与注意事项

  1. 数据结构选择: 尽管示例使用了 ArrayList,但对于频繁在列表两端进行添加和删除操作的场景,LinkedList 或 ArrayDeque (作为双端队列) 可能提供更好的性能,因为它们不需要移动大量元素。

    • LinkedList: add(0, element) 和 remove(size - 1) 操作的平均时间复杂度为 O(1)。
    • ArrayList: add(0, element) 操作需要移动所有后续元素,时间复杂度为 O(N);remove(size - 1) 为 O(1)。
    • 对于 MAX_RECENT_RECIPES 较小(如3)的情况,ArrayList 的性能差异不明显。但如果近期列表较大,LinkedList 会是更好的选择。
  2. 处理重复项: 示例代码中的 recentRecipeIndices.remove(Integer.valueOf(recipeIndex)) 已经处理了重复项,确保一个食谱只出现一次,并且每次使用都会将其移到列表的最前面。

  3. 线程安全: 如果 RecentRecipesManager 可能在多个线程中被访问(例如,在后台线程加载食谱详情,同时主线程更新近期列表),则需要考虑线程安全。可以使用 Collections.synchronizedList() 包装 recentRecipeIndices,或者在 addRecipeToRecent 等方法中使用 synchronized 关键字。

    // 线程安全版本
    private List recentRecipeIndices = Collections.synchronizedList(new ArrayList<>());
  4. 错误处理: 在 updateRecentRecipesUI 中,确保 receiptsBase.getReceipt(index) 返回的 ArrayList 不为空,并且 get(0) 访问的索引是有效的。在实际应用中,应该添加空指针检查和索引越界检查。

  5. 抽象 Recipe 对象: 当前 ReceiptsBase 使用 ArrayList> 来表示食谱。更好的做法是定义一个 Recipe 类,包含图片ID、名称ID等属性,这样代码的可读性和可维护性会大大提高。

    // 示例 Recipe 类
    public class Recipe {
        private int imageResId;
        private int nameResId;
        private int infoResId;
        // ... 其他属性
    
        public Recipe(int imageResId, int nameResId, int infoResId /*, ... */) {
            this.imageResId = imageResId;
            this.nameResId = nameResId;
            this.infoResId = infoResId;
        }
    
        public int getImageResId() { return imageResId; }
        public int getNameResId() { return nameResId; }
        // ... getters
    }

    然后 ReceiptsBase 可以存储 ArrayList,RecentRecipesManager 仍然存储 int 类型的索引。

总结

通过本教程,我们学习了如何在 Java 应用中实现一个“近期使用”功能。核心在于维护一个固定大小的列表,并利用滑动窗口机制(移除最旧,添加最新)来管理元素。我们还探讨了如何将此功能集成到 Android 应用的 UI 中,以及如何通过 SharedPreferences 实现数据持久化,确保用户体验的连贯性。在实际开发中,根据项目需求选择合适的数据结构、考虑线程安全和错误处理,并采用良好的面向对象设计(如抽象 Recipe 类),将有助于构建更健壮、更易维护的应用程序。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
go语言 面向对象
go语言 面向对象

本专题整合了go语言面向对象相关内容,阅读专题下面的文章了解更多详细内容。

56

2025.09.05

java面向对象
java面向对象

本专题整合了java面向对象相关内容,阅读专题下面的文章了解更多详细内容。

52

2025.11.27

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

320

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

212

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1503

2023.10.24

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

625

2023.11.24

java读取文件转成字符串的方法
java读取文件转成字符串的方法

Java8引入了新的文件I/O API,使用java.nio.file.Files类读取文件内容更加方便。对于较旧版本的Java,可以使用java.io.FileReader和java.io.BufferedReader来读取文件。在这些方法中,你需要将文件路径替换为你的实际文件路径,并且可能需要处理可能的IOException异常。想了解更多java的相关内容,可以阅读本专题下面的文章。

655

2024.03.22

php中定义字符串的方式
php中定义字符串的方式

php中定义字符串的方式:单引号;双引号;heredoc语法等等。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

610

2024.04.29

C++ 设计模式与软件架构
C++ 设计模式与软件架构

本专题深入讲解 C++ 中的常见设计模式与架构优化,包括单例模式、工厂模式、观察者模式、策略模式、命令模式等,结合实际案例展示如何在 C++ 项目中应用这些模式提升代码可维护性与扩展性。通过案例分析,帮助开发者掌握 如何运用设计模式构建高质量的软件架构,提升系统的灵活性与可扩展性。

14

2026.01.30

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 3万人学习

C# 教程
C# 教程

共94课时 | 8万人学习

Java 教程
Java 教程

共578课时 | 53.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号