Collections
- Kotlin course
- Intermediate
- Lesson 114/116
- 6 min
Write data filtering with List, Map and Flow.
Library: Collection management
This lesson defines the Library class that holds books in a collection. Inside it private val books = mutableListOf<Book>() stores the books; the add method appends, the listAll method returns a copy of the books as a list. So the books are managed from a single place.
class Library {
private val books = mutableListOf<Book>()
fun add(b: Book) { books.add(b) }
fun listAll(): List<Book> = books.toList()
}mutableListOf creates a mutable list; add appends to the end. toList() returns an immutable copy of the list; this prevents external code from corrupting it. Following encapsulation, books is kept private; access goes through methods.
Encapsulation and access
The books field is kept private; it cannot be accessed directly from outside. This is the encapsulation principle: the internal representation is hidden, access is limited to methods. Since listAll returns a copy, external code cannot corrupt the original list.
val lib = Library()
lib.add(Book("Crime and Punishment", "Dostoyevsky", 1866, Fiction))
for (b in lib.listAll()) {
println(b.format())
}Here a book is added and then the list is iterated with listAll. External code cannot reach lib.books (private); it can only use add and listAll. This preserves Library's internal consistency. Later, methods such as remove or findByAuthor can be added.
Practice Define the Library class and use it
The Book and Category definitions are ready. Define class Library. Inside it add private val books = mutableListOf<Book>(). The fun add(b: Book) method does books.add(b). Add fun listAll(): List<Book> = books.toList(). In main create val lib = Library(); add two books; loop with for (b in lib.listAll()) and println each book's format output.
Starter code
data class Book(val title: String, val author: String, val year: Int, val category: Category) {
fun format(): String = "$title - $author ($year) [${category.label()}]"
}
sealed class Category {
abstract fun label(): String
}
object Fiction : Category() {
override fun label() = "Fiction"
}
object NonFiction : Category() {
override fun label() = "Non-fiction"
}
// Library class: hold a mutable list of Book (private val books)
// add(b: Book): append to the list
// listAll(): return List<Book>
// main: add two books, print each format output with listAll
fun main() {
// write here
}Sample solution
class Library {
private val books = mutableListOf<Book>()
fun add(b: Book) { books.add(b) }
fun listAll(): List<Book> = books.toList()
}
fun main() {
val lib = Library()
lib.add(Book("Crime and Punishment", "Dostoyevsky", 1866, Fiction))
lib.add(Book("Sapiens", "Harari", 2011, NonFiction))
for (b in lib.listAll()) {
println(b.format())
}
}Expected output
Crime and Punishment - Dostoyevsky (1866) [Fiction] Sapiens - Harari (2011) [Non-fiction]
Questions and answers
Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.
1 Match the Library parts
| Term | Meaning |
|---|---|
mutableListOf<Book> |
Mutable list |
fun add(b) |
Add method |
fun listAll() |
List return |
private val books |
Hidden field |
2 Create a mutable list
private val books = ____<Book>()
private val books = mutableListOf<Book>()
Explanation: mutableListOf<Book>() creates a mutable list.
3 Why is Library.books made private?
- To use less memory
- To prevent external code from mutating the list directly Correct answer
- To make it faster
- To avoid compiler warnings
Explanation: private prevents external access and provides encapsulation.