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 类),将有助于构建更健壮、更易维护的应用程序。

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

831

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

737

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

733

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

397

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

398

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

446

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

430

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16925

2023.08.03

php与html混编教程大全
php与html混编教程大全

本专题整合了php和html混编相关教程,阅读专题下面的文章了解更多详细内容。

3

2026.01.13

热门下载

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

精品课程

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

共23课时 | 2.5万人学习

C# 教程
C# 教程

共94课时 | 6.6万人学习

Java 教程
Java 教程

共578课时 | 45.5万人学习

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

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