第九章 集合
9、java集合(3天) | |
9.1 Java集合框架 | 1课时 |
9.2 Collection接口API | 1课时 |
9.3 Iterator迭代器接口 | 1课时 |
9.4 Collection子接口之一: List接口 | 1课时 |
9.5 Collection子接口之二:Set接口 | 1课时 |
9.6 Map接口 | 1课时 |
9.7 Collections工具类 | 1课时 |
案例
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.junit.Test;
/**
* 1.java中的容器--在内存层面,对数据进行统一的存储和管理:数组 ; java集合
* 拓展:数据的持久化:文件(.jpg;mp3等); xml; 数据库
*
* 2. 数组在内存存储方面的特点:①数组初始化以后,长度就确定了。
* ②数组声明的类型,就决定了进行元素初始化时的类型
* 弊端:数组初始化以后,长度就不可变了。
* 数组中提供的属性和方法少,不便于进行添加、删除、插入等操作
* 数组存储的数据是有序的、可以重复的。---->存储数据的特点单一
*
* String[] arr = new String[10];
* Object[]
*
* 3.集合框架
两者是并列关系
* java.util.Collection:单列数据
* |------List子接口:存储有序的、可重复的数据 ---->"动态"数组
*
* |------Set子接口:存储无序的、不可重复的数据 ----->高中讲的"集合"
*
*
* java.util.Map:双列数据。存储一对一对的数据:key-value对 ---->中学讲的"函数": y = f(x). 比如:y = x + 2;
* (x1,y1),(x2,y2),...
*
* 4. 测试Collection中的常用方法
*
* 5. 规定:如果集合Collection中存储自定义类的对象,要求自定义类要重写equals().
*
*
* 6. 集合:很常用。
* 掌握:层次一:选择合适的集合类去实现数据的保存,调用其内部的相关方法。
*
* 层次二:不同的集合类底层的数据结构为何?如何实现数据的操作的:增删改查等。
*/
public class CollectionTest {
@Test
public void test4(){
Collection coll = new ArrayList();
coll.add(new String("AA"));
coll.add("MM");
coll.add(new Person("Tom",12));
coll.add(123);
coll.add(456);
//11.equals(Object obj):比较当前对象和obj是否相等。
Collection coll1 = new ArrayList();
coll1.add(new String("AA"));
coll1.add("MM");
coll1.add(new Person("Tom",12));
coll1.add(123);
coll1.add(456);
boolean flag = coll.equals(coll1);
System.out.println(flag);
//12.hashCode():获取当前对象的哈希值
System.out.println(coll.hashCode());
//13.toArray():将集合转换为数组:Object[]
Object[] arr = coll.toArray();
for(int i = 0;i < arr.length;i++){
System.out.println(arr[i]);
}
//14.toArray(T[] arr):略
//15.iterator():涉及集合元素的遍历。 见 IteratorTest.java
}
@Test
public void test3(){
Collection coll = new ArrayList();
coll.add(new String("AA"));
coll.add("MM");
coll.add(new Person("Tom",12));
coll.add(123);
coll.add(456);
//8.remove(Object obj):从当前集合中移除obj元素。仍然需要调用到obj所在类的equals()
// System.out.println(coll);
// coll.remove(123);
// coll.remove(new Person("Tom",12));
// System.out.println(coll);
//9.removeAll(Collection coll):差集:从当前集合中移除coll集合中的元素。
// Collection coll1 = Arrays.asList(123,456);
// coll.removeAll(coll1);
// System.out.println(coll);
//10.retainAll(Collection coll):交集:获取当前集合和coll集合的共有元素。
Collection coll1 = Arrays.asList(123,456,678);
coll.retainAll(coll1);
System.out.println(coll);
}
@Test
public void test2(){
Collection coll = new ArrayList();
coll.add(new String("AA"));
coll.add("MM");
// Person p = new Person("Tom", 12);
// coll.add(p);
coll.add(new Person("Tom",12));
coll.add(123);
coll.add(456);
//6.contains(Object obj):判断当前集合中是否包含obj:调用了obj所在类的equals().
boolean flag = coll.contains(new String("AA"));
System.out.println(flag);//true
// flag = coll.contains(p);
flag = coll.contains(new Person("Tom",12));
System.out.println(flag);//p:true--->new:false-->true
//7.containsAll(Collection coll):当前集合中是否包含coll中的所有元素
Collection list = Arrays.asList(new Integer[]{123,456});//从数组---->集合的转换
flag = coll.containsAll(list);
System.out.println(flag);//
}
@Test
public void test1(){
Collection coll = new ArrayList();
//2.add(Object obj):将obj添加到当前的集合中
coll.add("AA");
coll.add("MM");
coll.add(123);//自动装箱
//1.size():返回集合中存储的数据的个数
System.out.println(coll.size());
//3.addAll(Collection coll):将coll集合中的所有元素添加到当前集合中
Collection coll1 = new ArrayList();
coll1.add(1);
coll1.add(2);
coll1.add(3);
coll.addAll(coll1);
// coll.add(coll1);
System.out.println(coll);
//5.clear():清空当前集合
coll.clear();
//4.isEmpty():判断当前集合是否为空
System.out.println(coll.isEmpty());
}
}
Collection接口继承树
Map接口继承树
9-2 Collection 接口API
Collection 接口
- Collection 接口是 List、Set 和 Queue 接口的父接口,该接口里定义的方法既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合。
- JDK不提供此接口的任何直接实现,而是提供更具体的子接口(如:Set和List)实现。
- 在 Java5 之前,Java 集合会丢失容器中所有对象的数据类型,把所有对象都当成 Object 类型处理;从 JDK 5.0 增加了泛型以后,Java 集合可以记住容器中对象的数据类型
Collection 接口方法
9-3 Iterator迭代器接口
案例
/**
* 集合的遍历:
* 方式一:使用Iterator实现
* 方式二:增强for循环(jdk 5.0 :foreach循环)
*
*/
public class IteratorTest {
//笔试题:
@Test
public void test4(){
String[] arr = new String[]{"MM","MM","MM","MM"};
// for(int i = 0;i < arr.length;i++){
// arr[i] = "GG";
// }
for(String s : arr){
s = "GG";
}
/*
* int m = 10;
* int n = m;
* n = 20;
* sysout(m);
*/
for(int i = 0;i < arr.length;i++){
System.out.println(arr[i]);
}
}
@Test
public void test3(){
Collection coll = new ArrayList();
coll.add(new String("AA"));
coll.add("MM");
coll.add(new Person("Tom",12));
coll.add(123);
coll.add(456);
//for(集合元素的类型 局部变量 : 集合引用){}
for(Object obj : coll){
System.out.println(obj);
}
System.out.println("**************************");
//增强for循环用来遍历数组
int[] arr = new int[]{1,2,3,4,5,6,7};
for(int a : arr){
System.out.println(a);
}
}
//错误的使用方式:
@Test
public void test2(){
Collection coll = new ArrayList();
coll.add(new String("AA"));
coll.add("MM");
coll.add(new Person("Tom",12));
coll.add(123);
coll.add(456);
//错误方式一:
// Iterator iterator = coll.iterator();
// while(iterator.next() != null){
// System.out.println(iterator.next());
// }
//错误方式二:
// while(coll.iterator().hasNext()){
// System.out.println(coll.iterator().next());
// }
}
@Test
public void test1(){
Collection coll = new ArrayList();
coll.add(new String("AA"));
coll.add("MM");
coll.add(new Person("Tom",12));
coll.add(123);
coll.add(456);
Iterator iterator = coll.iterator();//List list = Arrays.asList(T ... t);
//方式一:
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());
//执行报NoSuchElementException的异常
// System.out.println(iterator.next());
//方式二:
// for(int i = 0;i < coll.size();i++){
// System.out.println(iterator.next());
// }
//方式三:
//hasNext():判断是否还有下一个元素
while(iterator.hasNext()){
//next():①指针下移 ②将下移以后集合位置上的元素返回
System.out.println(iterator.next());
}
}
}
public class Person {
private String name;
private int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
// @Override
// public int hashCode() {
// final int prime = 31;
// int result = 1;
// result = prime * result + age;
// result = prime * result + ((name == null) ? 0 : name.hashCode());
// return result;
// }
@Override
public boolean equals(Object obj) {
System.out.println("Person equals...");
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
使用 Iterator 接口遍历集合元素
- Iterator对象称为迭代器(设计模式的一种),主要用于遍历 Collection 集合中的元素。
- 所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象。
- Iterator 仅用于遍历集合,Iterator 本身并不提供承装对象的能力。如果需要创建 Iterator 对象,则必须有一个被迭代的集合。
Iterator接口的方法
使用 foreach 循环遍历集合元素
练习:判断输出结果为何?
public class TestFor {
public static void main(String[] args){
String[] str = new String[5];
for(String myStr : str){
myStr = "xxxxx";
System.out.println(myStr);
}
for(int i = 0;i < str.length;i++){
System.out.println(str[i]);
}
}
}
9-4 Collection子接口之一:List 接口
List接口
- Java中数组用来存储数据的局限性。
- List集合类中元素有序、且可重复,集合中的每个元素都有其对应的顺序索引。
- List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
- JDK API中List接口的实现类常用的有:ArrayList、LinkedList和Vector。
案例
/**
* java.util.Collection:单列数据
* |------List子接口:存储有序的、可重复的数据 ---->"动态"数组
* |-----ArrayList:作为List的主要实现类;线程不安全的,效率高;底层使用数组实现
* (Collections中定义了synchronizedList(List list)将此ArrayList转化为线程安全的)
* |-----LinkedList:对于频繁的插入、删除操作,我们建议使用此类,因为效率高;底层使用双向链表实现
* |-----Vector:List的古老实现类;线程安全的,效率低;底层使用数组实现
*
*
* [面试题] ArrayList、LinkedList、Vector区别?
* 共同点:ArrayList、LinkedList、Vector都是List接口的实现类,存储的数据都是有序的、可重复的。
* 区别:如上
*
* List list = new ArrayList();//jdk 7.0 : Object[] elementData = new Object[10];
* sysout(list.size());//0
* list.add(..);//elementData[0] = ..
* ...
* ...
* 一旦要添加的元素超出了底层数组的长度,就需要扩容,默认扩容为原来的1.5倍,同时,需要将原有数组
* 中的元素复制新的数组中。
* ...
*
* 实际情况:需要存储80个数据到ArrayList中,建议:List list = new ArrayList(85);
*
*
*
* 说明:1.向List中添加自定义类的对象的话,要求此自定义类重写equals()方法!
* 2.补充:数据结构解决两个问题:
* 1.数据之间逻辑关系:一对一,一对多,多对多,...
* 2.数据的存储结构:①顺序存储:一维数组 ②链式存储
*/
public class ListTest {
/*
* 在Collection的基础上,新增的方法:
void add(int index, Object ele):在index位置插入ele元素
boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
Object get(int index):获取指定index位置的元素
int indexOf(Object obj):返回obj在集合中首次出现的位置.如果不存在,返回-1.
int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置.如果不存在,返回-1.
Object remove(int index):移除指定index位置的元素,并返回此元素
Object set(int index, Object ele):设置指定index位置的元素为ele
List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的左闭右开区间的子集合
substring(int from ,int to) / read(int from,int length)
总结:List中的常用方法:
增:add(Object obj)
删:remove(Object obj) / remove(int index)
改:set(int index, Object ele)
查:get(int index)
插:add(int index, Object ele)
遍历:iterator();增强for;for
长度:size()
*/
//遍历
@Test
public void test4(){
List list = new ArrayList();
list.add(123);
list.add(456);
list.add("AA");
list.add(new Person("Tom",12));
list.add(456);
//遍历方式一:
Iterator iterator = list.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("*************");
//遍历方式二:
for(Object o : list){
System.out.println(o);
}
System.out.println("*************");
//遍历方式三:因为List提供索引
for(int i = 0;i < list.size();i++){
System.out.println(list.get(i));
}
}
@Test
public void test3(){
List list = new ArrayList();
list.add(123);
list.add(456);
list.add("AA");
list.add(new Person("Tom",12));
list.add(456);
//修改:
list.set(1, "BB");
System.out.println(list);
//获取子集合:
List subList = list.subList(1, 3);
System.out.println(subList);
}
@Test
public void test2(){
List list = new ArrayList();
list.add(123);
list.add(456);
list.add("AA");
list.add(new Person("Tom",12));
list.add(456);
int index = list.indexOf(456);
System.out.println(index);
int lastIndex = list.lastIndexOf(456);
System.out.println(lastIndex);
Object obj = list.remove(2);
System.out.println(obj);
System.out.println(list);
System.out.println(list.get(2));
}
@Test
public void test1(){
List list = new ArrayList();
list.add(123);
list.add(456);
list.add("AA");
list.add(new Person("Tom",12));
System.out.println(list);
//插入:add(int index, Object ele)
list.add(1, "BB");
System.out.println(list);
//插入:addAll(int index, Collection eles)
List list1 = Arrays.asList(1,2,3);
list.addAll(2, list1);
// list.add(2,list1);
System.out.println(list);
//查询:get(int index)
System.out.println(list.get(1));
}
}
List实现类之一:ArrayList
-
ArrayList 是 List 接口的典型实现类、主要实现类
-
本质上,ArrayList是对象引用的一个”变长”数组
-
ArrayList 是线程不安全的,而 Vector 是线程安全的,即使为保证 List 集合线程安全,也不推荐使用Vector
-
Arrays.asList(…) 方法返回的 List 集合既不是 ArrayList 实例,也不是 Vector 实例。 Arrays.asList(…) 返回值是一个固定长度的 List 集合
【面试题】
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
updateList(list);
System.out.println(list);//[1,3]
}
private static void updateList(List<Integer> list) {
//list.remove(new Integer(2));
list.remove(2);
}
List实现类之二:LinkedList
案例
import java.util.Iterator;
import java.util.LinkedList;
public class LinkedListDemo {
//LinkedList它内部封装的是双向链表数据结构
//每个借点是一个Node对象,Node对象中封装的是你要添加的元素.
//还有一个指向上一个Node对象的应用和指向下一个Node对象的引用
/*
不同的容器有不同的数据结构,不同的数据结构操作起来性能是不一样的
连接数据结构,做插入、删除的效率比较高,但查询效率比较低(LinkedList)
数组结构,它做查询的时候效率高,因为可以通过下标直接找到元素
但插入和删除效率比较低,因为要做位移操作.(ArrayList)
*/
public static void main(String[] args) {
//boolean add(E e) 将指定的元素添加到此列表的尾部。
//void add(int index, E element) 将指定的元素插入此列表中的指定位置。
// E set(int index, E element) 用指定的元素替代此列表中指定位置上的元素。
// Iterator<E> iterator() 返回以恰当顺序在此列表的元素上进行迭代的迭代器。 (此方法在其父类AbstractSequentialList<E>中)
// E removeFirst() 移除并返回此列表的第一个元素。
// E pollFirst() 获取并移除此列表的第一个元素;如果此列表为空,则返回 null。
LinkedList<String> sList = new LinkedList<String>();
//如果用父类的引用指向子类对象List->LinkedList 那么LinkedList自有的方法不能使用
sList.add("zhansan");
sList.add("lisi");
sList.add("wangwu");
sList.add("rose");
sList.add("mary");
sList.add("jack");
sList.addFirst("aa");
sList.addLast("bb");
Iterator<String> it = sList.iterator();
//Iterator迭代器对象的方法
//boolean hasNext() 如果仍有元素可以迭代,则返回 true。
//E next() 返回迭代的下一个元素。
//void remove() 从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)。
// E get(int index) 返回此列表中指定位置上的元素。
while(it.hasNext()){
String name = it.next();
System.out.print(name+" ");
//aa zhansan lisi wangwu rose mary jack bb
}
System.out.println();
for(Iterator<String> it1 = sList.iterator();it1.hasNext();){
String name1 = it1.next();
System.out.print(name1+" ");
//aa zhansan lisi wangwu rose mary jack bb
}
System.out.println();
for (String string : sList) {
System.out.print(string+" ");
//aa zhansan lisi wangwu rose mary jack bb
}
System.out.println();
System.out.println(sList.removeFirst());//aa
System.out.println(sList.size());//7
System.out.println(sList.pollFirst());//zhansan
System.out.println(sList.size());//6
sList.clear();
System.out.println(sList.pollFirst());//null
}
}
LinkedList模拟堆栈
import java.util.Iterator;
import java.util.LinkedList;
//堆栈后进先出
public class LinkedListDemo1 {
public static void main(String[] args) {
MyStack<String> mystack = new MyStack<String>();
mystack.push("zhangsan");
mystack.push("lisi");
mystack.push("wangwu");
mystack.push("zhaoliu");
mystack.pop();
mystack.pop();
Iterator<String> it = mystack.iterator();
while(it.hasNext()){
System.out.print(it.next()+" ");//lisi zhangsan
}
}
}
class MyStack<T>{
private LinkedList<T> data = null;
public MyStack(){
data=new LinkedList<T>();
}
//压栈的方法
public void push(T obj){
data.addFirst(obj);
}
//出栈的方法
public T pop(){
return data.removeFirst();
}
public Iterator<T> iterator(){
return data.iterator();
}
}
自己实现LinkedList
https://blog.csdn.net/qq_34436819/article/details/53850925
自建ArrayList及Iterator
public interface Iterator<T> {
public boolean hasNext();//判断是否有下一个元素
public T next();//获取下一个元素的内容
}
public interface List<T>{
public void add(T obj);//给具体的容器添加元素
public T get(int index);//获取指定位置上的元素
public int size();//获取容器中的元素个数
public Iterator<T> iterator();//获取迭代器
}
public class ArrayList
public ArrayList() {
super();
obj = new Object[10];
index=0;
size=0;
}
@Override
public void add(T obj) {
this.obj[index++]=obj;//把数据存放到数组中
size++;
}
@SuppressWarnings("unchecked")//压制警告
@Override
public T get(int index) {
return (T) this.obj[index];
}
@Override
public int size() {
return size;
}
@Override
public Iterator<T> iterator() {
return new MyIterator<T>(this);
}
}
public class MyIterator
public MyIterator(List<T> list) {
super();
this.list = list;
}
//判断是否有下一个元素
@Override
public boolean hasNext() {
return index<list.size();
}
//取出下一个元素
@Override
public T next() {
return list.get(index++);
}
}
public class Test {
public static void main(String[] args) {
List<String> nameList = new ArrayList<String>();
nameList.add("zhangsan");
nameList.add("lisi");
nameList.add("wangwu");
nameList.add("aa");
nameList.add("bb");
Iterator<String> it = nameList.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
System.out.println("********");
for (int i = 0; i < nameList.size(); i++) {
System.out.println(nameList.get(i));
}
}
}
- 对于频繁的插入或删除元素的操作,建议使用LinkedList类,效率较高
- 新增方法:
- void addFirst(Object obj)
- void addLast(Object obj)
- Object getFirst()
- Object getLast()
- Object removeFirst()
- Object removeLast()
List 实现类之三:Vector
- Vector 是一个古老的集合,JDK1.0就有了。大多数操作与ArrayList相同,区别之处在于Vector是线程安全的。
- 在各种list中,最好把ArrayList作为缺省选择。当插入、删除频繁时,使用LinkedList;Vector总是比ArrayList慢,所以尽量避免使用。
- 新增方法:
- void addElement(Object obj)
- void insertElementAt(Object obj,int index)
- void setElementAt(Object obj,int index)
- void removeElement(Object obj)
- void removeAllElements()
【面试题】
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
updateList(list);
System.out.println(list);
}
private static void updateList(List<Integer> list) {
list.remove(new Integer(2));
}
答案:[1,3]
9-5 Collection子接口之二:Set 接口
Set 接口
/**
*
* java.util.Collection:单列数据
* |------List子接口:存储有序的、可重复的数据 ---->"动态"数组
*
* |------Set子接口:存储无序的、不可重复的数据 ----->高中讲的"集合"
* |-----HashSet:是Set的主要实现类。底层实现:HashSet底层使用了HashMap.
* |-----LinkedHashSet:是HashSet的子类,可以按照添加的顺序实现遍历。
* (原因:在HashSet底层存储结构的基础上,额外提供了一对指针。能够记录此Node元素的
* 上一个和下一个元素。)--->对于频繁的遍历,效率高。
*
* |-----TreeSet:可以按照添加的元素的指定属性的大小实现遍历。底层实现:红黑树
*
*/
public class SetTest {
/**
* TreeSet的定制排序:
* 1.提供Comparator接口匿名实现类的对象
* 2.重写其中的compare(Object o1,Object o2),指明排序的规则。
* 3.将此实现类的对象作为参数传递到TreeSet的构造器中
* 4.向TreeSet的对象中添加compare()方法中判断的类的对象。
*
* 总结:
* 元素是否能add成功,是否能remove,是否contains,...都依赖于compareTo()或compare().
* 与元素所在类的equals()/hashCode()无关!
*
*/
@Test
public void test4(){
//提供Comparator接口匿名实现类的对象
Comparator com = new Comparator(){
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof Customer && o2 instanceof Customer){
Customer c1 = (Customer)o1;
Customer c2 = (Customer)o2;
return c1.getAge() - c2.getAge();
}
return 0;
}
};
Set set = new TreeSet(com);
set.add(new Customer("Tom", 12));
set.add(new Customer("Jerry", 2));
set.add(new Customer("HanMeimei", 52));
set.add(new Customer("Lilei", 44));
set.add(new Customer("Yaoming", 44));
set.remove(new Customer("Yaoming", 44));
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
/**
* TreeSet的使用
* 1.向TreeSet中添加的元素必须是同一个类创建的对象。
*
* 2.TreeSet排序的方式:① 自然排序(Comparable) ②定制排序(Comparator)
* 3.自然排序:
* ①要求添加的元素所在的类要实现Comparable接口
* ②重写接口中的compareTo(Object obj)--->指明排序的规则
* 如果此方法返回值为0,则新要添加的元素添加不成功。
* ③向TreeSet中添加此实现类的对象即可。
*/
@Test
public void test3(){
Set set = new TreeSet();
//错误的!
// set.add(new String("BB"));
// set.add("AA");
// set.add(123);
// set.add(new Person("Tom", 12));
//方式一:
// set.add(new String("BB"));
// set.add("AA");
// set.add("OO");
// set.add("KK");
// set.add("JJ");
//方式二:
// set.add(45);
// set.add(-45);
// set.add(5);
// set.add(475);
// set.add(75);
// set.add(-75);
//方式三:
set.add(new Person("Tom",35));
set.add(new Person("Jerry",32));
set.add(new Person("HanMeimei",32));
set.add(new Person("Lilei",3));
set.add(new Person("YaoMing",23));
set.add(new Person("张学友",33));
set.add(new Person("李嘉诚",33));
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
@Test
public void test2(){
Set set = new LinkedHashSet();
set.add(new String("BB"));
set.add(new String("BB"));
set.add("AA");
set.add(123);
set.add(123);
set.add(new Person("Tom", 12));
set.add(new Person("Tom", 12));
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
/**
* Set作为Collection的子接口,没有定义额外的方法。
*
*
* Set:存储无序的、不可重复的数据
* 1.无序性: != 随机性。 添加的元素,需要计算其哈希值,此哈希值决定了在底层的存储位置。从存储位置上看,是无序的。
*
* 2.不可重复性:保证Set集合中的不同对象使用对象所属类的equals()判断的话,一定返回false.
*
* 3.如何向Set中添加一个元素?哈希算法。
* 向Set中添加元素a,首先通过hashCode(),计算元素a的哈希值,此哈希值就决定了此元素在Set底层存储的位置。
* 如果此存储的位置上没有元素,则此元素a添加成功。如果此存储的位置上有元素b,则调用元素a所在类的equals()方法,
* 将元素b作为参数传递过去。如果返回值为true,则表示元素a和b相同,则元素a添加不成功。如果返回值为false,则
* 认为元素a和元素b不相同。此时元素a可以添加成功的。元素a与元素b使用链表存储。
* (jdk 7.0 :a指向b;jdk8.0:b指向a)
*
*
* 4.向Set中添加的元素,要求其所在的类要重写两个方法:
* equals() 和 hashCode().
* 5. 必须要求添加的元素所在的类中重写的equals() 和 hashCode() 保持一致性。
*/
@Test
public void test1(){
Set set = new HashSet();
set.add(123);
set.add(123);
set.add(new String("BB"));
set.add(new String("BB"));
set.add("AA");
set.add(new Person("Tom", 12));
set.add(new Person("Tom", 12));
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
public class Person implements Comparable{
private String name;
private int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
@Override
public int hashCode() { //return age + name.hashCode();
//为什么使用31? 质数。 2 * 32 最高效的方式? 2 << 5 ; 7 17 31
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
// return false;
}
//此方法的重写,就决定了向TreeSet中添加数据的顺序。
@Override
public int compareTo(Object o) {
if(o instanceof Person){
Person p = (Person)o;
int value = this.age - p.age;
if(value != 0){
return value;
}else{
return this.name.compareTo(p.name);
}
}
return 0;
}
}
public class Customer {
private String name;
private int age;
public Customer(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Customer [name=" + name + ", age=" + age + "]";
}
}
Set 接口
- Set接口是Collection的子接口,set接口没有提供额外的方法
- Set 集合不允许包含相同的元素,如果试把两个相同的元素加入同一个 Set 集合中,则添加操作失败。
- Set 判断两个对象是否相同不是使用 == 运算符,而是根据 equals 方法
Set实现类之一:HashSet
hashCode() 方法
- 如果两个元素的 equals() 方法返回 true,但它们的 hashCode() 返回值不相等,hashSet 将会把它们存储在不同的位置,但依然可以添加成功。
- 对于存放在Set容器中的对象,对应的类一定要重写equals()和hashCode(Object obj)方法,以实现对象相等规则。
- 重写 hashCode() 方法的基本原则
- 在程序运行时,同一个对象多次调用 hashCode() 方法应该返回相同的值
- 当两个对象的 equals() 方法比较返回 true 时,这两个对象的 hashCode() 方法的返回值也应相等
- 对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值
Set实现类之二:LinkedHashSet
- LinkedHashSet 是 HashSet 的子类
- LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,但它同时使用链表维护元素的次序,这使得元素看起来是以插入顺序保存的。
- LinkedHashSet插入性能略低于 HashSet,但在迭代访问 Set 里的全部元素时有很好的性能。
- LinkedHashSet 不允许集合元素重复。
Set实现类之三:TreeSet
- TreeSet 是 SortedSet 接口的实现类,TreeSet 可以确保集合元素处于排序状态。
- Comparator comparator()
- Object first()
- Object last()
- Object lower(Object e)
- Object higher(Object e)
- SortedSet subSet(fromElement, toElement)
- SortedSet headSet(toElement)
- SortedSet tailSet(fromElement)
- reeSet 两种排序方法:自然排序和定制排序。默认情况下,TreeSet 采用自然排序。
排 序——自然排序
排 序——定制排序
【面试题】
HashSet set = new HashSet();
Person p1 = new Person(1001,"AA");
Person p2 = new Person(1002,"BB");
set.add(p1);
set.add(p2);
p1.name = "CC";
set.remove(p1);
System.out.println(set);
set.add(new Person(1001,"CC"));
System.out.println(set);
set.add(new Person(1001,"AA"));
System.out.println(set);
其中Person类中重写了hashCode()和equal()方法
9-6 Map接口
Map接口
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import org.junit.Test;
/**
* java.util.Map:
*
* |------HashMap:Map的主要实现类;线程不安全的,效率高;可以存储null的key和value
* (存储结构:jdk7.0 数组+链表; jdk8.0 数组+链表+红黑树)
* |-----LinkedHashMap:HashMap的子类,可以按照添加的顺序遍历。对于频繁的遍历效率较高。
* (在HashMap存储结构的基础上,使用了一对双向链表来记录添加元素的顺序)
* |------TreeMap:可以按照key的指定的属性进行排序。底层实现:红黑树
* |------Hashtable:Map的古老实现类;线程安全的,效率低;不可以存储null的key和value
* |-----Properties:Hashtable的子类,常常用来处理属性文件。其key和value都是String型。
*
* 面试题:HashMap和Hashtable的对比。
*/
public class MapTest {
@Test
public void test1(){
Map map = new HashMap();
map = new Hashtable();
map.put(null, 123);
}
/**
* Map:存储的是双列数据:key-value
* 1.所有的key构成的集合是Set:无序的、不可重复的
* 2.所有的value构成的集合是Collection:无序的、可以重复的
* 3.一个key-value构成一个Entry
* 4.所有的Entry构成的集合是Set:无序的、不可重复的
*
*
* HashSet的底层是使用HashMap进行存储的。
* HashMap的所有的key构成集合就是HashSet。
*
*
* 面试题:HashMap的底层实现原理?
* HashMap map = new HashMap();//底层创建了长度为16的Entry数组
* 向HashMap中添加entry1(key,value),需要首先计算entry1中key的哈希值(根据key所在类的hashCode()计算
* 得到),此哈希值经过处理以后,得到在底层Entry[]数组中要存储的位置i.如果位置i上没有元素,则entry1直接添加成功。
* 如果位置i上已经存在entry2(或还有链表存在的entry3,entry4),则需要通过循环的方法,依次比较entry1中key和其他
* 的entry是否equals.如果返回值为true.则使用entry1的value去替换equals为true的entry的value.如果遍历一遍以后,
* 发现所有的equals返回都为false,则entry1仍可添加成功。entry1指向原有的entry元素。
*
* 默认情况下,如果添加元素的长度 >= DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR (默认值为12)
* 且新要添加的数组位置不为null的情况下,就进行扩容。默认扩容为原有长度的2倍。将原有的数据复制到新的数组中。
*
*
* jdk 8.0 :
* 1.HashMap map = new HashMap();//默认情况下,先不创建长度为16的数组。
* 2.当首次调用map.put()时,再创建长度为16的数组
* 3.当数组指定索引位置的链表长度>8时,且map中的数组的长度> 64时,此索引位置上的所有entry使用红黑树进行存储。
*
*
* LinkedHashMap:
* HashSet:
* LinkedHashSet:
*
*/
}
Map接口
- Map与Collection并列存在。用于保存具有映射关系的数据:Key-Value
- Map 中的 key 和 value 都可以是任何引用类型的数据
- Map 中的 key 用Set来存放,不允许重复,即同一个 Map 对象所对应的类,须重写hashCode()和equals()方法。
- 常用String类作为Map的“键”。
- key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到唯一的、确定的 value
Map 常用方法
Map常用方法
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;
import org.junit.Test;
public class MapTest {
//Map的常用类:
//Hashtable的子类:Properties.
//特点:key和value都是String类型
//应用:常常用来处理属性文件
@Test
public void test5(){
Properties pros = new Properties();
try {
FileInputStream fis = new FileInputStream("jdbc.properties");
pros.load(fis);
String name = pros.getProperty("name");
String password = pros.getProperty("password");
System.out.println("name = " + name + ",password = " + password);
} catch (Exception e) {
e.printStackTrace();
}
}
/*
* 测试TreeMap
*
* TreeMap可以按照key中指定的属性进行排序:自然排序 ; 定制排序
*/
@Test
public void test4(){
//自然排序
Map map = new TreeMap();
map.put("name5", "Tom5");
map.put("name4", "Tom51");
map.put("name7", "Tom53");
map.put("name2", "Tom55");
map.put("name9", "Tom57");
System.out.println(map);
//定制排序:
Comparator com = new Comparator(){
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof Customer && o2 instanceof Customer){
Customer c1 = (Customer)o1;
Customer c2 = (Customer)o2;
return c1.getAge() - c2.getAge();
}
return 0;
}
};
Map map1 = new TreeMap(com);
map1.put(new Customer("Tom", 21), 1004);
map1.put(new Customer("Tom", 22), 1002);
map1.put(new Customer("Tom", 6), 1001);
map1.put(new Customer("Tom", 99), 1005);
System.out.println(map1);
}
//测试LinkedHashMap
@Test
public void test3(){
Map map = new HashMap();
map = new LinkedHashMap();
map.put("id", 1001);
map.put("name", "Tom");
map.put("salary", 5600.2);
System.out.println(map);
}
//Map中的常用方法测试:
/*
* 如何遍历Map
Set keySet()
Collection values()
Set entrySet()
总结:
增:put(Obejct key,Object value)
删:remove(Object key)
改:put(Obejct key,Object value)
查:get(Object key)
长度:size()
遍历:keySet() / values() / entrySet()
List (index--->数据)/ Set(很少用) / Map (key --- 数据)
*/
@Test
public void test2(){
Map map = new HashMap();
map.put("id", 1001);
map.put("name", "Tom");
map.put("salary", 5600.2);
//1.遍历所有的key:keySet()
Set keySet = map.keySet();
Iterator iterator = keySet.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println();
//2.遍历所有的value:values();
Collection values = map.values();
for(Object o : values){
System.out.println(o);
}
System.out.println();
//3.遍历所有的key-value
//方式一:
Set keySet1 = map.keySet();
for(Object key : keySet1){
System.out.println(key + "---->" + map.get(key));
}
//方式二:
Set entrySet = map.entrySet();
for(Object o : entrySet){
Map.Entry entry = (Map.Entry)o;
System.out.println(entry.getKey() + "********" + entry.getValue());
}
}
/*
Object put(Object key,Object value)
Object remove(Object key)
void putAll(Map t)
void clear()
Object get(Object key)
boolean containsKey(Object key)
boolean containsValue(Object value)
int size()
boolean isEmpty()
boolean equals(Object obj)
*/
@Test
public void test1(){
Map map = new HashMap();
//添加:put(Object key,Object value)
map.put("name", "Tom");
map.put("id", 1001);
map.put("salary", 5600.2);
System.out.println(map);
//修改:put(Object key,Object value)
map.put("name", "Jerry");
System.out.println(map);
//删除:Object remove(Object key)
Object value = map.remove("id");
System.out.println(value);
//长度:size()
System.out.println(map.size());
//清空数据:clear()
// map.clear();//与map = null不同;
//是否为空:isEmpty();
System.out.println(map.isEmpty());
//
boolean flag = map.containsKey("salary");
System.out.println(flag);
flag = map.containsValue("Tom");
System.out.println(flag);
}
}
public class Customer {
private String name;
private int age;
public Customer(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Customer [name=" + name + ", age=" + age + "]";
}
}
-
添加、删除操作: + Object put(Object key,Object value) + Object remove(Object key) + void putAll(Map t) + void clear()
- 元视图操作的方法:
- Set keySet()
- Collection values()
- Set entrySet()
- 元素查询的操作:
- Object get(Object key)
- boolean containsKey(Object key)
- boolean containsValue(Object value)
- int size()
- boolean isEmpty()
- boolean equals(Object obj)
Map实现类之一:HashMap
- Map接口的常用实现类:HashMap、TreeMap和Properties。
- HashMap是 Map 接口使用频率最高的实现类。
- 允许使用null键和null值,与HashSet一样,不保证映射的顺序。
- HashMap 判断两个 key 相等的标准是:两个 key 通过 equals() 方法返回 true,hashCode 值也相等。
- HashMap 判断两个 value相等的标准是:两个 value 通过 equals() 方法返回 true。
HashMap的存储结构
JDK 7及以前版本:HashMap是数组+链表结构(即为链地址法)
JDK 8版本发布以后:HashMap是数组+链表+红黑树实现。
Map实现类之二:LinkedHashMap
- LinkedHashMap 是 HashMap 的子类
- 与LinkedHashSet类似,LinkedHashMap 可以维护 Map 的迭代顺序:迭代顺序与 Key-Value 对的插入顺序一致
Map实现类之三:TreeMap
- TreeMap存储 Key-Value 对时,需要根据 key-value 对进行排序。TreeMap 可以保证所有的 Key-Value 对处于有序状态。
- TreeMap 的 Key 的排序:
- 自然排序:TreeMap 的所有的 Key 必须实现 Comparable 接口,而且所有的 Key 应该是同一个类的对象,否则将会抛出 ClasssCastException
- 定制排序:创建 TreeMap 时,传入一个 Comparator 对象,该对象负责对 TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现 Comparable 接口
- TreeMap判断两个key相等的标准:两个key通过compareTo()方法或者compare()方法返回0。
- 若使用自定义类作为TreeMap的key,所属类需要重写equals()和hashCode()方法,且equals()方法返回true时,compareTo()方法应返回0。
Map实现类之四:Hashtable
- Hashtable是个古老的 Map 实现类,线程安全。
- 与HashMap不同,Hashtable 不允许使用 null 作为 key 和 value
- 与HashMap一样,Hashtable 也不能保证其中 Key-Value 对的顺序
- Hashtable判断两个key相等、两个value相等的标准,与hashMap一致。
Map实现类之五:Properties
- Properties 类是 Hashtable 的子类,该对象用于处理属性文件
- 由于属性文件里的 key、value 都是字符串类型,所以 Properties 里的 key 和 value 都是字符串类型
- 存取数据时,建议使用setProperty(String key,String value)方法和getProperty(String key)方法
- Properties pros = new Properties();
- pros.load(new FileInputStream("jdbc.properties"));
- String user = pros.getProperty("user");
- System.out.println(user);
9-7 Collections工具类
Collections工具类
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
/**
* Collections:用来操作集合框架(Collection / Map)的工具类。 ---->Arrays数组工具类。
*
* 面试题:Collection 和 Collections 区别?
*
*/
public class CollectionsTest {
/*
*
reverse(List):反转 List 中元素的顺序
shuffle(List):对 List 集合元素进行随机排序
sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换
Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素
Object min(Collection)
Object min(Collection,Comparator)
int frequency(Collection,Object):返回指定集合中指定元素的出现次数
void copy(List dest,List src):将src中的内容复制到dest中
boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换 List 对象的所有旧值
*/
@Test
public void test1(){
List list = new ArrayList();
list.add(34);
list.add(134);
list.add(-34);
list.add(-34);
list.add(334);
list.add(56);
list.add(77);
System.out.println(list);
// Collections.reverse(list);
//
// System.out.println("reverse:" + list);
//
// Collections.shuffle(list);
// System.out.println("shuffle:" + list);
//
// Collections.sort(list);
// System.out.println("sort:" + list);
// Collections.swap(list, 0, 2);
// System.out.println("swap:" + list);
// int frequency = Collections.frequency(list, -34);
// System.out.println(frequency);
/*
* copy(List dest,List src):将src中的内容复制到dest中
*/
//错误的:
// List dest = new ArrayList();
// Collections.copy(dest, list);
// System.out.println(dest);
//正确的:
List dest = Arrays.asList(new Object[list.size()]);
Collections.copy(dest, list);
System.out.println(dest);
//list:线程不安全的。
List newList = Collections.synchronizedList(list);
//newList:线程安全的。
}
}
操作集合的工具类:Collections
操作数组的工具类:Arrays
- Collections 是一个操作 Set、List 和 Map 等集合的工具类
- Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法
- 排序操作:(均为static方法)
- reverse(List):反转 List 中元素的顺序
- shuffle(List):对 List 集合元素进行随机排序
- sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
- sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
- swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换
查找、替换
- Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
- Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素
- Object min(Collection)
- Object min(Collection,Comparator)
- int frequency(Collection,Object):返回指定集合中指定元素的出现次数
- void copy(List dest,List src):将src中的内容复制到dest中
- boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换 List 对象的所有旧值
同步控制
- ollections 类中提供了多个 synchronizedXxx() 方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题
补充:Enumeration
- Enumeration 接口是 Iterator 迭代器的 “古老版本”
Enumeration stringEnum = new StringTokenizer("a-b*c-d-e-g", "-");
while(stringEnum.hasMoreElements()){
Object obj = stringEnum.nextElement();
System.out.println(obj);
}
练 习
1.请用随机数输入10个整数保存到List中,并按倒序、从大到小的顺序显示出来
2.请把学生名与考试分数录入到集合中,并按分数显示前三名成绩学员的名字。
3.一个文件中有很多人员信息如:
张 三
李 四
王 五
李 按时若多撒多
输出姓氏及次数
import org.junit.Test;
import java.io.*;
import java.text.DecimalFormat;
import java.util.*;
public class CollectionTest {
@Test
public void test1() {
//[0,1)
//double random = Math.random();
//System.out.println(random);
List<Integer> list = new ArrayList<>();
Random r = new Random();
for (int i = 0; i < 10; i++) {
//如果这个值要取[1,10)
Integer value = (int) (r.nextDouble() * 10 + 1);
list.add(value);
}
// 倒序方法一
// for (int i = 0; i < list.size(); i++) {
//// for (int j = 0; j < list.size()-1; j++) {
//// if(list.get(i)>list.get(j)){
//// Integer temp = list.get(j);
//// list.set(j,list.get(i)) ;
//// list.set(i,temp) ;
//// }
//// }
//// }
//// System.out.println(list);
// 倒序方法二
// System.out.println(list);
// Collections.sort(list);
// System.out.println(list);
// Collections.reverse(list);
// System.out.println(list);
// 倒序方法三
System.out.println(list);
// list.sort(new Comparator<Integer>() {
// @Override
// public int compare(Integer o1, Integer o2) {
// return o2-o1;
// }
// });
list.sort((o1, o2) -> {
return o2 - o1;
});
System.out.println(list);
}
@Test
public void test2() {
Map<Student, Student> map = new TreeMap<>();
String name = "松岛枫把老师把你的空间辣酸奶的经费困难辣椒";
final DecimalFormat format = new DecimalFormat("#.##");
int count = 0;
i:
{
for (int i = 0; i < 20; i++) {
int nameLenth = (int) (Math.random() * 3 + 2);
String aaFianlName = "";
if (count == 10) {
break i;
}
for (int i1 = 0; i1 < nameLenth; i1++) {
char aaName = name.charAt((int) (Math.random() * name.length()));
aaFianlName += aaName;
}
Random random = new Random();
Double v = random.nextDouble() * 101;
String format1 = format.format(v);
double finalSource = Double.parseDouble(format1);
if (!map.containsKey(new Student(aaFianlName, finalSource))) {
map.put(new Student(aaFianlName, finalSource), new Student(aaFianlName, finalSource));
count++;
}
}
}
System.out.println(map);
Set<Map.Entry<Student, Student>> entries = map.entrySet();
Iterator<Map.Entry<Student, Student>> iterator = entries.iterator();
for (int i = 1; i <= 10; i++) {
Map.Entry<Student, Student> next = iterator.next();
next.getKey();
next.getValue();
System.out.println("第" + i + "名:" + next.getKey() + ",成绩: " + next.getValue());
}
}
@Test
public void test3() {
List<String> ls = new ArrayList<>();
try (FileInputStream fi = new FileInputStream("D:\\day22\\src\\comchengzi\\day22\\exer\\student")) {
String l;
InputStreamReader reader = new InputStreamReader(fi, "UTF-8");
BufferedReader bufferedReader = new BufferedReader(reader);
while ((l = bufferedReader.readLine()) != null) {
ls.add(l);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(ls);
Map<String, Integer> map = new HashMap<>();
for (int i = 0; i < ls.size(); i++) {
String[] split = ls.get(i).split(" ");
if (map.containsKey(split[0])) {
Integer integer = map.get(split[0]);
map.put(split[0], integer + 1);
} else {
map.put(split[0], 0);
}
}
System.out.println(map);
}
}
import java.util.Objects;
public class Student implements Comparable<Student>{
String name;
Double Source;
public Student(String name, Double source) {
this.name = name;
Source = source;
}
@Override
public int compareTo(Student o) {
return (int)(o.Source-this.Source);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", Source=" + Source +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(name, student.name) &&
Objects.equals(Source, student.Source);
}
@Override
public int hashCode() {
return Objects.hash(name, Source);
}
}
student.txt
张 三
李 四
王 五
李 按时若多撒多
您的资助是我最大的动力!
金额随意,欢迎来赏!