Scala学习记录,case class,迭代器
case class
case class创建的对象的属性是不可改的
创建对象,可以不用写new
自动重写:toString, equals, hashCode, copy
自动重写方法:toString,equals,hashCode,copy
小习一下
1.case class 的定义语法是什么
基本形式:case class
关键字后接类名,类名通常采用大写字母开头的驼峰命名法。然后是一对圆括号,括号内是类的参数列表,参数的格式为参数名:参数类型
,多个参数之间用逗号分隔: case class ClassName(parameter1: Type1, parameter2: Type2, /*...*/)
2.case class有哪些作用
-
数据建模与封装
-
方便的数据组合:case class 可以有效地将多个相关的数据项组合成一个逻辑单元。
-
传递数据的容器:它可以作为一种数据容器,在函数之间或者不同的模块之间传递复杂的数据。
-
-
支持模式匹配
-
默认的不可变性:case class 的实例默认是不可变的。所有的属性都是以
val
(值)的形式定义的,即:其属性不能被修改。 -
函数式编程友好:case class 提供了一种简单的方式来创建和使用不可变数据,有助于编写更加纯粹的函数式代码,如对数据进行转换、映射等操作,而不用担心副作用。
-
-
不可变数据结构的实现
-
方便的复制与更新机制
-
copy
方法的使用:case class 提供了copy
方法,用于创建一个基于现有实例的新实例,并可以有选择地修改某些属性。
-
3.case class的特点有哪些
-
自动生成方法
-
equals
和hashCode
方法: -
toString
方法:
-
-
属性为
val
默认不可变性 -
解构支持,方便数据提取
-
伴生对象自动生成
-
继承和密封性(与
sealed trait
结合) -
1.层次结构控制:当
case class
与sealed trait
结合使用时,可以构建一个受控制的继承层次结构。
4.case class和普通class在那些方面有区别
-
构造函数相关差异
-
参数默认可见性
-
case class:在
case class
中,构造函数的参数默认是val
(对于不可变数据),这意味着这些参数是公开可读的,并且不能在外部重新赋值。 -
普通 class:普通
class
的构造函数参数默认是私有的,需要通过自定义的访问器方法(如getter
和setter
)来控制对参数的访问和修改。
-
-
自动生成的方法
-
case class:
case class
会自动生成apply
方法(在伴生对象中)用于方便地创建实例,还会生成unapply
方法用于模式匹配。 -
普通 class:普通
class
不会自动生成这些方法,需要开发者自己根据需求编写构造函数和其他相关方法。如果要实现类似case class
的apply
功能,需要在伴生对象(在 Scala 中)或者静态方法(在 Java 等语言中)中手动定义创建实例的方法。
-
-
构造函数的用途
-
case class:构造函数主要用于初始化不可变的数据,并且这些数据可以方便地通过模式匹配等方式进行处理。
-
普通 class:普通
class
的构造函数除了初始化数据外,还可能用于初始化可变的数据成员,并且可以在构造函数中进行复杂的初始化逻辑,如资源分配、数据库连接等操作。
-
-
-
数据可变性差异
-
默认行为
-
case class:如前面提到的,
case class
默认是不可变的,这是因为其构造函数参数是val
。这种不可变性有助于在函数式编程和并发编程中避免数据被意外修改。 -
普通 class:普通
class
的数据成员可以是可变的(通过var
定义)或者不可变的(通过val
定义),开发者可以根据具体需求来选择。例如,一个用于记录游戏状态的普通class
可能有可变的成员来反映游戏中的动态变化。
-
-
修改数据的方式
-
case class:如果要修改
case class
的数据,通常会使用copy
方法。例如,对于case class Book(title: String, author: String)
,可以通过book.copy(title = "New Title")
来创建一个新的book
实例,其标题被修改为New Title
。 -
普通 class:对于普通
class
,如果数据成员是可变的,可以直接通过访问器方法(如setter
)来修改。如果是不可变的,可能需要像case class
一样创建一个新的实例来更新数据。
-
-
-
模式匹配支持差异
-
直接支持程度
-
case class:
case class
在模式匹配中有很好的支持。可以直接在模式匹配表达式中使用case class
的构造函数参数进行解构。。 -
普通 class:普通
class
在模式匹配中没有这种直接的支持。要让普通class
参与模式匹配,通常需要在伴生对象中定义unapply
方法,这个过程相对复杂。
-
-
-
方法自动生成差异
-
equals
、hashCode
和toString
方法-
case class:
case class
会自动生成equals
、hashCode
和toString
方法。equals
方法基于构造函数参数来判断两个case class
实例是否相等,hashCode
方法也与构造函数参数相关,toString
方法会输出case class
名称和构造参数的值。 -
普通 class:普通
class
不会自动生成这些方法,需要开发者手动重写这些方法来满足特定的需求。如果不重写equals
和hashCode
方法,默认的行为可能不符合预期,尤其是在比较复杂的对象结构中。
-
-
5.case class课后练习
1.使用case class创建图书信息类Book:包含四个属性:ID,书名,作者,价格,数量。
2.创建一个名为BookList的可变List,它只能用来保存Book的实例。
3.初始化三本不同的书(注意id不同就是不同的书),加入到Booklist中。
4.添加一本已经存在的书。此时应该去修改Booklist中对应书目的数量,而不是去添加一条新数据。
5.根据图书名称查询是否在列表中(通过遍历列表对比名称)
6.删除指定书名的书
7.删除指定ID的书
8.对于图书按价格从高到低排序。
9.遍历图书列表,并打印每本书的详细信息
10.展示全部的总的金额
package hh
import scala.collection.mutable.ListBuffer
case class Book(id: String, name: String, author: String, price: Double, quantity: Int)
object 图书馆case {
def main(args: Array[String]): Unit = {
// 创建可变列表 BookList
val bookList = ListBuffer[Book]()
// 初始化三本不同的书并加入列表
val book1 = Book("1", "西游记", "吴承恩", 20, 5)
val book2 = Book("2", "红楼梦", "曹雪芹", 15, 3)
val book3 = Book("3", "三国演义", "罗贯中", 25, 2)
bookList += book1
bookList += book2
bookList += book3
// 添加一本已经存在的书(修改数量)
val existingBook = Book("2", "红楼梦", "曹雪芹", 15, 1)
addOrUpdateBook(existingBook, bookList)
// 根据图书名称查询是否在列表中
val bookNameToSearch = "三国演义"
println(s"Is book '$bookNameToSearch' in the list? ${isBookInListByName(bookNameToSearch, bookList)}")
// 删除指定书名的书
val bookNameToDelete = "三国演义"
deleteBookByName(bookNameToDelete, bookList)
// 删除指定 ID 的书
val bookIdToDelete = "3"
deleteBookById(bookIdToDelete, bookList)
// 对图书按价格从高到低排序
val sortedBooks = bookList.sortBy(-_.price)
// 遍历图书列表并打印每本书的详细信息
println("Books in the list:")
sortedBooks.foreach(book => println(s"ID: ${book.id}, Name: ${book.name}, Author: ${book.author}, Price: ${book.price}, Quantity: ${book.quantity}"))
// 展示全部的总的金额
val totalAmount = sortedBooks.map(book => book.price * book.quantity).sum
println(s"Total amount: $totalAmount")
}
def addOrUpdateBook(book: Book, bookList: ListBuffer[Book]): Unit = {
val existingBookIndex = bookList.indexWhere(_.id == book.id)
if (existingBookIndex!= -1) {
bookList(existingBookIndex) = book.copy(quantity = bookList(existingBookIndex).quantity + book.quantity)
} else {
bookList += book
}
}
def isBookInListByName(name: String, bookList: ListBuffer[Book]): Boolean = {
bookList.exists(_.name == name)
}
def deleteBookByName(name: String, bookList: ListBuffer[Book]): Unit = {
val maybeBook = bookList.find(_.name == name)
maybeBook.foreach(book => bookList -= book)
}
def deleteBookById(id: String, bookList: ListBuffer[Book]): Unit = {
val maybeBook = bookList.find(_.id == id)
maybeBook.foreach(book => bookList -= book)
}
}
打印结果:
迭代器定义:迭代器不是一种集合,它是一种用于访问集合的方法。
迭代器需要通过集合对应的迭代器调用迭代器的方法来访问。
支持函数式编程风格,便于链式操作。
迭代器的使用:
object Test_迭代 {
def main(args: Array[String]): Unit = {
val list1 =List(1,2,3,4)
//1. foreach 实现
list1.foreach(println)
//2.迭代器
//2.1构建迭代器
val it = list1.iterator
//2.2依次取元素
while (it.hasNext){
println(it.next())
}
val set1 =Set("apple","banana")
val it2 =set1.iterator //构建迭代器
while (it2.hasNext){
println(it2.next())
}
it2.map(a => s"I like $a").foreach(println)
}
}
object Test_迭代 {
def main(args: Array[String]): Unit = {
val list1 =List(1,2,3,4)
//duplicate:复制迭代器
//得到一组,两个迭代器,它们有相同的数据,但是相互独立
val it = List(3,4,5,6,7).iterator
val (it1,it2) = it.duplicate
println(it1,it2)
//用第一个迭代器 来计算平均值
val dataList = it1.toList//把迭代器的数据放到列表中
val avg = dataList.sum / dataList.size
println("平均值:"+avg)
//用第二个迭代器来选出大于平均值的元素
it2.filter(x => x > avg).foreach(println)
}
}
take:从当前位置开始,取几个元素,得到新的迭代器
drop:从当前位置开始,跳过指定数量的元素,得到新的迭代器
//take:从当前位置开始,取几个元素,得到新的迭代器
val it = List(1,2,3,4,5,6,7).iterator
//drop:从当前位置开始,跳过指定数量的元素,得到新的迭代器
val it2 = it.drop(3)
//只输出3个
val it1 = it2.take(3)
while (it1.hasNext){
println(it1.next())
}
toList的作用:把迭代器中剩余的元素保存到一个List中
//toList的作用:把迭代器中剩余的元素保存到一个List中
val it = List(1,2,3,4,5,6,7).iterator
it.next()//移动一次
it.next()//移动一次
while (it.hasNext){
println(it.next())
}
val li = it.toList
println(li)
zip方法(拉链):把两个迭代器合成一下,得到新的迭代器,长度以短的为准
val nums = List(1,2,3).iterator
val words = List("one","two","three").iterator
//zip:把两个迭代器合成一下,得到新的迭代器,长度以短的为准
val it = nums.zip(words)
while (it.hasNext){
val p = it.next()//元组
println(s"${p._1} ===== ${p._2}")
}
小习一下:
1.以下关于送代器next方法说法正确的是( C )
A.它总是返回送代器中的第一个元素。
B.它返回送代器中的下一个元素,并将送代器位置向前移动一位,如果没有会返回None。
C.它返回送代器中的下一个元素,并将送代器位置向前移动一位,如果没有会抛出NoSuchElementException.
D.它可以返回送代器中的任意一个元素。
- 分析:在 Scala 中,迭代器的
next
方法返回迭代器中的下一个元素,并将迭代器位置向前移动一位。如果没有下一个元素,会抛出NoSuchElementException
。选项 A 错误,它不是总是返回第一个元素;选项 B 错误,没有下一个元素时不会返回None
,而是抛出异常;选项 D 错误,它是按顺序返回下一个元素,不是任意一个元素。
2.duplicate方法创建的迭代器副本与原迭代器( C )
A.共享元素序列,修改其中一个会影响另一个。
B.不共享元素序列,修改其中一个不会影响另一个。
C.共享元素序列,但修改其中一个不会影响另一个。
D.没有任何关系
分析:duplicate
方法创建的迭代器副本与原迭代器共享元素序列。对其中一个迭代器的操作会影响另一个,因为它们本质上是在同一个元素序列上进行遍历。
3.对于zip方法,当两个选代器台长度不同时( B )
A.会自动补齐较短的迭代器,使其长度与较长的迭代器相同。
B.会在较短的迭代器耗尽时停上zip操作。
C.会抛出异常。
D.会重复使用较短的迭代器元素,直到与较长的送代器长度相同。
- 分析:对于
zip
方法,当两个迭代器长度不同时,会在较短的迭代器耗尽时停止zip
操作。选项 A 错误,不会自动补齐;选项 C 错误,一般不会抛出异常;选项 D 错误,不会重复使用较短的迭代器元素来补齐
4.toList方法对迭代器的作用是( B )
A.将迭代器中的所有元素转换为一个列表,包括已经遍历过的元素。
B.将迭代器中的剩余元素转换为一个列表。
C.将列表转换为迭代器。
D.将选代器转换为一个不可变的集合。
分析:toList
方法是将迭代器中的剩余元素转换为一个列表。选项 A 错误,不是包括已经遍历过的元素;选项 C 错误,它不是将列表转换为迭代器;选项 D 错误,它只是转换为列表,不是转换为不可变的集合这种说法不准确。
5.使用drop方法创建的子送代器( B )
A.包含原迭代器的所有元素。
B.从原迭代器的开头开始,跳过指定数量的元素后包含剩余的元素。
C.包含原选代器中指定数量的元素,从开头开始。
D.是原迭代器的一个副本,不跳过任何元素
分析:使用drop
方法创建的子迭代器是从原迭代器的开头开始,跳过指定数量的元素后包含剩余的元素。选项 A 错误,不是包含所有元素;选项 C 错误,不是从开头包含指定数量的元素;选项 D 错误,会跳过指定数量的元素,不是不跳过任何元素。
6.迭代器课后练习:
1.创建学生信息数据结构和初始数据。创建一个表示学生信息的类,case student 包含姓名、年龄和 成绩列表 [ 数学,英语,语文 ] 等属性。然后创建几个学生对象,并将它们存储在一个列表中。
2.创建选代器并进行基础遍历。为学生列表创建迭代器,使用选代器的hasNext 和next方法遍历学生信息,打印每个学生的姓名。
3.使用 duplicate 方法和筛选操作。利用 duplicate 方法创建迭代器到本。在原送代器上根据年龄条件筛选年龄>20岁的同学,在副本迭代器上根据语文成绩条件筛选>80分的同学。
4.使用zip 方法关联信息。创建一个新的列表用于存储每个学生的平均成绩(三门课的平均绩),再创建该列表的选代器。通过zip 方法将学生选代器和平均成绩选代器组合,然后打印每个学生的姓名和平均成绩。
5.打印前三名的同学信息。
6.打印从第4名开始的后3位同学的信息。
7.重新创建学生列表的迭代器,用于统计所有学生的所有成绩的总和,并打印结果。
8.再次使用该迭代器,筛选出成绩列表中有至少一个特定分数以上的学生信息,并打印他们的姓名和年龄
package Scala_CSDN.十一月
// 1. 创建表示学生信息的类并初始化学生对象列表
case class Student(name: String, age: Int, scores: List[Double])
object 迭代器1 {
def main(args: Array[String]): Unit = {
// 创建学生对象并存储在列表中
val studentList = List(
Student("小张", 18, List(88.0, 85.0, 90.0)),
Student("小卢", 18, List(90.0, 97.0, 85.0)),
Student("小王", 19, List(98.0, 80.0, 95.0)),
Student("小朱", 21, List(90.0, 85.0, 90.0)),
Student("小沈", 19, List(88.0, 90.0, 90.0))
)
// 2. 创建迭代器并进行基础遍历
val studentIterator = studentList.iterator
while (studentIterator.hasNext) {
val student = studentIterator.next()
println(s"学生姓名: ${student.name}")
}
// 3. 使用duplicate方法和筛选操作
val (originalIterator, duplicateIterator) = studentList.iterator.duplicate
val filteredByAge = originalIterator.filter(_.age > 20)
val filteredByChineseScore = duplicateIterator.filter(student => student.scores(2) > 80)
println("年龄大于20岁的同学:")
filteredByAge.foreach(student => println(s"姓名: ${student.name}, 年龄: ${student.age}"))
println("语文成绩大于80分的同学:")
filteredByChineseScore.foreach(student => println(s"姓名: ${student.name}, 语文成绩: ${student.scores(2)}"))
// 4. 使用zip方法关联信息
val averageScoresList = studentList.map(student => student.scores.sum / student.scores.size)
val averageScoresIterator = averageScoresList.iterator
val zippedIterator = studentList.iterator.zip(averageScoresIterator)
println("学生姓名和平均成绩:")
zippedIterator.foreach { case (student, averageScore) =>
println(s"姓名: ${student.name}, 平均成绩: ${averageScore}")
}
// 5. 打印前三名的同学信息
val topThreeStudents = studentList.sortBy(-_.scores.sum).take(3)
println("前三名同学信息:")
topThreeStudents.foreach(student => println(s"姓名: ${student.name}, 年龄: ${student.age}, 成绩总和: ${student.scores.sum}"))
// 6. 打印从第4名开始的后3位同学的信息
val fourthAndNextThreeStudents = studentList.sortBy(_.scores.sum).drop(3).take(3)
println("从第4名开始的后3位同学信息:")
fourthAndNextThreeStudents.foreach(student => println(s"姓名: ${student.name}, 年龄: ${student.age}, 成绩总和: ${student.scores.sum}"))
// 7. 重新创建学生列表的迭代器,用于统计所有学生的所有成绩的总和,并打印结果
val allScoresIterator = studentList.iterator
var totalScore = 0.0
while (allScoresIterator.hasNext) {
val student = allScoresIterator.next()
totalScore += student.scores.sum
}
println(s"所有学生的所有成绩总和: ${totalScore}")
// 8. 再次使用该迭代器,筛选出成绩列表中有至少一个特定分数以上的学生信息,并打印他们的姓名和年龄
val specificScore = 90.0
val filteredStudents = studentList.iterator.filter(student => student.scores.exists(_ > specificScore))
println(s"成绩中有至少一个${specificScore}分以上的学生信息:")
filteredStudents.foreach(student => println(s"姓名: ${student.name}, 年龄: ${student.age}"))
}
}
运行结果为: