- 
                Notifications
    You must be signed in to change notification settings 
- Fork 1
Refactor AttemptRepository to use LinkedHashMap for unique AttemptItems #911
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|  | @@ -31,7 +31,8 @@ class AttemptRepository(val context: Context) { | |||||
| lateinit var attempt : Attempt | ||||||
| private val isOfflineExam: Boolean get() = exam?.isOfflineExam ?: false | ||||||
| var page = 1 | ||||||
| val attemptItems = mutableListOf<AttemptItem>() | ||||||
| private val attemptItemMap = linkedMapOf<Int, AttemptItem>() | ||||||
| val attemptItems: List<AttemptItem> get() = attemptItemMap.values.toList() | ||||||
| 
      Comment on lines
    
      +34
     to 
      +35
    
   There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Consider thread safety implications of the LinkedHashMap. The LinkedHashMap implementation effectively prevents duplicates and maintains insertion order, but it's not thread-safe. Given that this repository uses coroutines and may be accessed from multiple threads, consider using  -private val attemptItemMap = linkedMapOf<Int, AttemptItem>()
+private val attemptItemMap = Collections.synchronizedMap(linkedMapOf<Int, AttemptItem>())Additionally, the  🤖 Prompt for AI Agents | ||||||
| private var _totalQuestions = 0 | ||||||
| val totalQuestions get() = _totalQuestions | ||||||
|  | ||||||
|  | @@ -75,7 +76,7 @@ class AttemptRepository(val context: Context) { | |||||
| override fun onSuccess(result: TestpressApiResponse<AttemptItem>) { | ||||||
| if (fetchSinglePageOnly) { | ||||||
| _totalQuestions = result.count | ||||||
| attemptItems.addAll(result.results) | ||||||
| addToAttemptItems(result.results) | ||||||
| _attemptItemsResource.postValue(Resource.success(attemptItems)) | ||||||
| if (result.hasMore()) { | ||||||
| page++ | ||||||
|  | @@ -84,11 +85,11 @@ class AttemptRepository(val context: Context) { | |||||
| } | ||||||
| if (result.hasMore()) { | ||||||
| _totalQuestions = result.count | ||||||
| attemptItems.addAll(result.results) | ||||||
| addToAttemptItems(result.results) | ||||||
| page++ | ||||||
| fetchAttemptItems(questionsUrlFrag, fetchSinglePageOnly) | ||||||
| } else { | ||||||
| attemptItems.addAll(result.results) | ||||||
| addToAttemptItems(result.results) | ||||||
| _attemptItemsResource.postValue(Resource.success(attemptItems)) | ||||||
| } | ||||||
| } | ||||||
|  | @@ -101,6 +102,13 @@ class AttemptRepository(val context: Context) { | |||||
| } | ||||||
| } | ||||||
|  | ||||||
| // Temporary fix: In rare cases, fetchAttemptItems() was repeatedly invoked due to unknown causes, | ||||||
| // leading to duplicate entries in the list. To mitigate this, we're using a LinkedHashMap to ensure | ||||||
| // uniqueness by item ID while preserving insertion order. | ||||||
| private fun addToAttemptItems(items: List<AttemptItem>) { | ||||||
| items.forEach { item -> attemptItemMap[item.id] = item } | ||||||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's a potential  You should handle the case where  
        Suggested change
       
 | ||||||
| } | ||||||
|  | ||||||
| private fun createOfflineAttemptItemItem() { | ||||||
| CoroutineScope(Dispatchers.IO).launch { | ||||||
| if (isAttemptItemsAlreadyCreated()) { | ||||||
|  | @@ -369,7 +377,7 @@ class AttemptRepository(val context: Context) { | |||||
| } | ||||||
|  | ||||||
| fun clearAttemptItems() { | ||||||
| attemptItems.clear() | ||||||
| attemptItemMap.clear() | ||||||
| } | ||||||
|  | ||||||
| fun resetPageCount() { | ||||||
|  | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This getter creates a new list via
toList()every timeattemptItemsis accessed. This can lead to performance issues if the property is accessed frequently, as each access has a time complexity of O(N) to create the new list, where N is the number of items.Throughout
AttemptRepository, this property is accessed multiple times in different methods, which will result in repeated list creation.To optimize this, you could cache the list and only regenerate it when
attemptItemMapis modified. Here's a conceptual example:This would require changes in
addToAttemptItemsandclearAttemptItemsbut would make access toattemptItemsan O(1) operation.