【JavaSE 第十七天】
一、泛型 Generic
泛型技术是 JDK 版本的一大升级,源自于 JDK1.5
泛型就是集合类 <泛型> (尖括号)
// 无泛型写法
public static void main(String[] args) {
/**
* JDK1.4 及以前没有泛型技术,是这样写:
* 1.集合可以存储任何数据类型
* 2.添加元素的数据类型是 Object
*/
List list = new ArrayList();
list.add("a");
list.add(1);
Iterator it = list.iterator();
while (it.hasNext()){
Object obj = it.next(); // 不能类型转换
System.out.println(obj);
}
}
// 这种写法可以存储任意数据类型,但是如果进行类型转换就会出现错误,不安全,所以不能进行类型转换,导致无法使用某些其他功能
1. 泛型的安全机制
软件升级:安全性提高,修复 Bug 错误,改善用户体验,增加功能,提升性能
JDK1.5 被称为:里程碑版本
泛型作用:强制集合存储固定的数据类型
泛型的书写格式:
集合类<存储的数据类型> 变量名 = new 集合类<存储的数据类型>();
(后面尖括号里的类型可以不写:JDK7 里面把不写的尖括号叫做:钻石操作符)
加入泛型后,程序的安全性提升了:
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("a");
list.add(1); // 编译错误,数据类型不匹配
Iterator<String> it = list.iterator();
while (it.hasNext()){
String obj =it.next(); // 类型转换不需要
System.out.println(obj);
}
}
- 使用泛型的好处:
- 程序安全性提高
- 程序的代码量减少
- 避免了类型的强制转换
- 把程序的问题,由运行时期,提前到编译时期(不用泛型只有到运行时期才会报错,而使用泛型在编译时期就会报错)
2. 泛型中的 E 问题
E 没有什么实际价值,只是一个变量而已
这个变量的特殊性:等待接收指定的数据类型
ArrayList<E>
// 创建对象
ArrayList<String> al = new ArrayList<String>();
// new 创建完对象之后 E 不在是 E 了,变成 String
public boolean add(String e) {
}
3. 自定义泛型类
自定义泛型类:
/**
* 定义一个类,类名叫工厂
* 自定义泛型类
* Factory<变量名>
* 只是变量名而已,符合标识符规则即可
*/
public class Factory<QQ> {
private QQ q;
public void setQ(QQ q){
this.q = q;
}
public QQ getQ(){
return q;
}
}
测试:
public static void main(String[] args) {
// 创建对象 Factory 类对象
// Factory factory = new Factory(); // 如果这么写就是 没有泛型 的写法,QQ 就是 Object
// 泛型的写法:
Factory<String> factory01 = new Factory<String>(); // QQ 是什么类型,只有等到尖括号中指定之后才能确定
factory01.setQ("abc");
String s = factory01.getQ();
System.out.println(s);
// 改变尖括号中的类型:
Factory<Double> factory02 = new Factory<Double>(); // QQ 是什么类型,只有等到尖括号中指定之后才能确定
factory02.setQ(1.5);
Double q = factory02.getQ();
System.out.println(q);
}
4. 泛型方法
/**
* 泛型的方法,作用于方法参数上(或返回值)
*/
public class Factory<Q> {
// 普通方法:
public void print(Q q){
System.out.println(q);
}
/**
* 静态方法:
* Q 是非静态的, 因为 Q 的数据类型,是 new 创建的时候指定的
*
* 静态方法参数中的泛型,不能和类一样(静态不能调用非静态的问题)
* 静态方法的泛型,需要在方法上单独定义
* 写在返回值类型的前面
*/
public static <T> void staticMethod(T q){
System.out.println(q);
}
}
调用:
public static void main(String[] args) {
// 泛型普通方法的调用
Factory<Integer> factory=new Factory<>();
factory.print(1213);
// 泛型静态方法的调用,参数随意
Factory.staticMethod("true");
}
5. 泛型接口
两种做法:
- 实现类实现接口,不实现泛型
- 实现类实现接口,同时指定泛型
定义泛型接口
// 泛型接口
public interface Inter <T> {
public abstract void inter(T t);
}
实现接口:
/**
* 实现接口,不理会泛型
* 对象创建的时候,才指定类型
*/
public class InterImpl01<T> implements Inter<T>{
public void inter(T t){
System.out.println(t);
}
}
实现接口:
/**
* 实现接口,同时指定泛型
*/
public class InterImpl02 implements Inter<String> {
public void inter(String s) {
System.out.println("s==" + s);
}
}
测试:
public class GenericTest {
public static void main(String[] args) {
// 第一种:
Inter<String> in01 = new InterImpl01<String>(); //对象创建的时候,指定类型
in.inter("ok");
// 第二种:
Inter in02 = new InterImpl02(); // 不能再次指定类型,已经在实现接口过程中指定了类型
in2.inter("hello");
}
}
6. 泛型通配符
// 泛型的通配符 (类似于文件的通配符: *.jpg)
public class GenericTest {
public static void main(String[] args) {
// 第一种类型的集合:
List<String> stringList = new ArrayList<String>();
stringList.add("abc");
stringList.add("bbc");
// 第二种类型的集合:
List<Integer> integerList = new ArrayList<Integer>();
integerList.add(1);
integerList.add(2);
// 调用:
each(stringList);
each(integerList);
}
/**
* 定义方法,可以同时迭代器,遍历这两个集合
* 方法的参数,是要遍历的集合,由于先前不确定是哪个集合
* 定义参数,写接口类型,不要写实现类
*/
public static void each(List<?> list){ // 在这里写通配符
// 方法的参数,由于遍历的集合先前并不知道,所以把集合当作参数传递参数,参数不要写实现类,要写接口
Iterator<?> it = list.iterator();
while (it.hasNext()){
Object obj = it.next();
// 不允许用默认类型去接收,由于没有明确的类型,所以迭代器的方法是 Object,无法指定具体的类型
System.out.println(obj);
}
}
}
7. 泛型限定
泛型限定:限制的是数据类型
<? extends Company>
传递类型可以是 Company 或者是它的子类<? extends E>
传递 E 类型或者是 E 的子类,泛型上限限定写法<? super E >
传递 E 类型或者是 E 的父类,泛型下限限定写法
// 事先创建一个公司类,一个开发部类,一个财务部类,开发部类和财务部类继承自公司类,并分别定义 work() 方法
public static void main(String[] args) {
// 创建集合,存储员工对象
// 开发部 集合
List<Development> devList = new ArrayList<Development>();
// 存储开发部员工对象
// 第一个对象
Development d1 = new Development();
d1.setName("张三");
d1.setId("开发部001");
// 第二个对象
Development d2 = new Development();
d2.setName("李四");
d2.setId("开发部002");
// 放入集合
devList.add(d1);
devList.add(d2);
// 财务部 集合
List<Financial> finList = new ArrayList<Financial>();
// 存储财务部员工对象
// 第一个对象
Financial f1 = new Financial();
f1.setName("王五");
f1.setId("财务部001");
// 第二个对象
Financial f2 = new Financial();
f2.setName("赵六");
f2.setId("财务部002");
// 放入集合
finList.add(f1);
finList.add(f2);
// 打印两个集合:
System.out.println(devList);
System.out.println(finList);
// 调用遍历方法传递参数
each(devList);
each(finList);
}
/**
* 1.要求:定义方法,同时遍历2个集合
* 遍历的同时取出集合元素,调用方法 work()
* 2.解决方法:由于使用 ? 接收任何一个类型
* 所以使它只能接收 Company 和子类对象
* 所以明确父类,不能明确子类
*/
public static void each(List<? extends Company> list){
Iterator<? extends Company> it = list.iterator(); // 迭代器的类型跟随集合
while (it.hasNext()){
// 取出元素
Company obj =it.next(); // 明确父类类型,不需要强转类型
obj.work();
}
}
8.泛型擦除
编译之前有泛型约束,编译之后没有泛型约束。泛型约束就是在编译时执行检察,在编译之后就进行了泛型擦除
例如:
List<Integer> intList = new ArrayList<Integer>();
List<String> intList = new ArrayList<String>();
// 这两者在编译执行完之后就都变为了:ArrayList<Object>
使用反射验证擦除(因为反射操作的是 Class,编译之后得到的文件),泛型擦除的目的就是为了减少占用内存,否则本例中就会有两个类型的对象
二、 增强型的 for 循环
JDK1.5 出现的特性:循环的特性
由来:
- Collection 是单列集合的顶级接口,但是到 JDK1.5 后, Collection 继承了一个父接口
- java.lang.Iterable 接口:实现这个接口,就可以成为 “foreach” 语句的目标
- Collection、List、Set 都实现了该接口,包括数组
(Map 没有实现)
1. 增强型 for 循环的格式
for(数据类型 变量名 : 集合或者数组){}
- 遍历数组:
/**
* for 循环遍历数组
*/
public static void forArray(){
int[] arr = {1,3,5,7,9};
for(int i : arr){
System.out.println(i); // 1 3 5 7 9
System.out.println(i+1); // 2 4 6 8 10
}
System.out.println("arr=="+arr[0]); // 结果是 1
}
// (但是 Java 实际上这种 for 循环的格式,并不存在,编译特效,因为反编译 .class 依旧是原来的 for 循环格式)
- 遍历集合:
/**
* for 循环遍历集合
*/
public static void forList(){
List<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
for(String s : list){
System.out.println(s);
}
}
// (遍历集合的 for 循环反编译 .class 是迭代器)
2. 单列集合转成数组
import java.util.ArrayList;
import java.util.List;
public class CollectionTest {
/**
* 单列集合转成数组
* Collection 接口中的方法 toArray()
*/
public static void main(String[] args) {
List<String> list=new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
// 集合对象调用方法 toArray()
String[] str = list.toArray(new String[list.size()]);
// 转换成数组就变为定长,不可以再改变长度
for(String s:str){
System.out.println(s);
}
}
}
三、Map 集合
java.util.Map 接口,是双列集合的顶级接口(Map 集合和 Collection 集合没有任何关系)
Map 集合容器每次存储两个对象,一个对象称为键(Key),另一个对象称为值(Value)(键值集合)
在一个Map的集合容器中,键保证唯一性,不包含重复键,每个键只能对应一个值
Map<k,v>
:
K | V |
---|---|
K 存储键的数据类型 | V 存储值的数据类型 |
- K 和 V 称为:映射键值对
- 这种一一对应的关系称为:映射
- 在 Map 集合中 value 可以随便写,但是 key 必须保证唯一性
- 键和值是一一对应的关系,(好比是夫妻关系,并以结婚证作为证明)对应关系(结婚证)也是对象
- 对象也会产生 接口 Entry(这个接口是一个内部接口)(这个接口的实现类才能体现一一对应的映射关系),有几个对应关系就有几个 Entry 接口(对象多了之后就会装入集合),所以 Map 集合可以利用映射关系进行遍历
- Map 存储的方法:
V put(K,V)
1. Map 接口的方法
V put(K,V)
存储键值对,如果没有存储重复键,返回的就是 null,如果存储重复键,返回被覆盖之前的值
/**
* put 方法,存储键值对
* Map 接口的实现类 HashMap
*/
public static void mapPut(){
// 创建对象,指定键的数据类型,值的数据
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("a",1);
map.put("b",2);
map.put("c",3);
map.put("d",4);
Integer value = map.put("c",5); // 如果没有存储重复键,返回的就是 null;存储重复键,返回被覆盖之前的值
System.out.println("map = " + map);
System.out.println("value = " + value);
}
V get(K)
通过键获取值,参数传递键,找这个键对应的值,如果没有这个键就返回 null
/**
* V get(K)通过键获取值,参数传递键,找这个键对应的值,没有这个键返回 null
*/
public static void mapGet(){
// 创建对象,指定键的数据类型,值的数据
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("a",1);
map.put("b",2);
map.put("c",3);
map.put("d",4);
// 通过键找值
Integer value = map.get("f"); // null
System.out.println(value);
}
boolean containsKey(K)
判断集合是否包含这个键,包含返回 trueboolean containsValue(V)
判断集合是否包含这个值,包含返回 trueint size()
返回集合长度,Map 集合中键值对的个数V remove(K)
移除指定的键值对,返回被移除之前的值Collection<V> values()
Map 集合中的所有的值拿出,存储到 Collection 集合
/**
* boolean containsKey(K) 判断集合是否包含这个键,包含返回 true
* boolean containsValue(V) 判断集合是否包含这个值,包含返回 true
* int size() 返回集合长度,Map 集合中键值对的个数
* V remove(K) 移除指定的键值对,返回被移除之前的值
* Collection<V> values() Map 集合中的所有的值拿出,存储到 Collection 集合
*/
public static void mapMethod(){
// 创建集合,键是整数,值是String
Map<Integer,String> map = new HashMap<Integer, String>();
map.put(1,"a");
map.put(2,"b");
map.put(3,"c");
map.put(4,"d");
map.put(5,"e");
// 1.boolean containsKey(K) 判断集合是否包含这个键,包含返回 true
boolean b = map.containsKey(1);
System.out.println("集合中包含键:"+b);
// 2.boolean containsValue(V) 判断集合是否包含这个值,包含返回 true
b = map.containsValue("c");
System.out.println("集合中包含值:"+b);
// 3.int size() 返回集合的长度
int size = map.size();
System.out.println("集合长度:"+size);
// 4.V remove(K) 移除指定的键值对,返回被移除之前的值
String value = map.remove(1);
System.out.println("被删除之前的值:"+value);
System.out.println(map);
// 5.Collection<V> values() Map 集合中的所有的值拿出,存储到 Collection 集合
Collection<String> coll = map.values();
for(String s : coll){
System.out.println(s);
}
}
2. Map 集合的遍历:键找值
- 实现思想:
- Map接口中定义了方法
keySet()
使所有的键,存储到 Set 集合 - 遍历 Set 集合(迭代器)
- 取出 Set 集合元素 Set 集合的元素是 Map 集合的键
- Map 集合的方法
get()
传递键,获取值
- Map接口中定义了方法
public static void mapKeySet(){
Map<String,String> map = new HashMap<String, String>();
map.put("a","java");
map.put("b","c++");
map.put("c","php");
map.put("d","python");
map.put("e","erlang");
// Map 接口定义了方法 keySet() 所有的键,存储到 Set 集合
Set<String> set = map.keySet();
// 遍历 Set 集合
Iterator<String> it = set.iterator();
// 取出 Set 集合元素
while (it.hasNext()){
String key = it.next();
// Map 集合的方法 get() 传递键获取值
String value = map.get(key);
System.out.println(key+"="+value);
}
}
3. 实现类的外部接口和实现类内部接口问题
①定义接口和抽象方法以及内部接口和抽象方法:
public interface Outer {
// 内部接口
interface Inner{ // 内部接口有固定的修饰符:public static
void inner(); // 内部接口的抽象方法
}
void outer(); //外部接口的抽象方法
}
②实现接口:
/**
* InnerImplement 实现接口
* 1. 实现外部接口,不需要完成内部接口的方法重写
*/
public class InnerImplement implements Outer {
public void outer(){}
}
③实现内部接口:
/**
* InnerImplement 实现接口
* 2. 实现内部接口,也不需要完成外部接口的方法重写
* 因为内部接口有固定的修饰符:public static
*/
public class InnerImplement implements Outer.Inner {
public void inner(){}
}
了解实现类的外部接口和实现类内部接口问题之后,可以进行 Map 集合的另一种遍历,使用内部接口
4. Map 集合的遍历:键值对映射关系
- 实现思想:
- Map 接口的方法
Set< Map.Entry<Key,Value> > entrySet()
- 方法返回 Set 集合,集合中存储的元素,比较特殊
- 存储的是 Map 集合中,键值对映射关系的对象,用内部接口
Map.Entry
表示
- 遍历 Set 集合
- 取出 Set 集合的元素
- 取出的是
Map.Entry
接口对象(实现类) - 使用接口的对象方法:
getKey()
,getValue()
- 取出的是
- Map 接口的方法
public static void mapEntrySet(){
Map<String,String> map = new HashMap<String, String>();
map.put("a","java");
map.put("b","c++");
map.put("c","php");
map.put("d","python");
map.put("e","erlang");
// Map 接口的方法 Set< Map.Entry<Key,Value> > entrySet()
Set<Map.Entry<String,String>> set = map.entrySet();
// 遍历 Set 集合
Iterator<Map.Entry<String,String>> it = set.iterator(); // 迭代器泛型要和集合一致
while (it.hasNext()){
// 取出 Set 集合的元素
Map.Entry<String,String> entry = it.next(); // 取出的是接口的实现类
// 接口的对象方法:getKey() getValue()
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key +"="+ value);
}
}
四、Map 接口的实现类 HashMap
HashMap 是一个 Map 接口的实现类
- HashMap 集合特点:
- 是哈希表结构
- 保证键唯一性,用于键的对象,必须重写
hashCode()
,equals()
方法 - 线程不安全集合,运行速度快
- 集合允许使用 null,作为键或者值
定义一个 Person 类:
public class Person {
private String name;
private int age;
public Person(){}
public Person(String name, int age) {
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 +
'}';
}
// 重写 hashCode(),equals() 方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
/**
* HashMap 集合
* 键是字符串,值是 Person
*/
public static void hashMap1(){
Map<String, Person> map = new HashMap<String, Person>();
map.put("a",new Person("张三",20));
map.put("b",new Person("张三",20));
map.put("c",new Person("张三",20));
map.put(null,null);
// 由于 Map 集合无法使用增强 for 循环遍历所以要把它转换成 Set 集合
// 一、第一种遍历方法:
// 1.完整写法:
Set<String> set = map.keySet();
for(String key : set()){
Person person = map.get(key);
System.out.println(key+"="+ person);
}
// 2.压缩写法:
// Set<String> set = map.keySet();
for(String key : map.keySet()){
// Person person = map.get(key);
System.out.println(key+"="+ map.get(key));
}
// 二、第二种遍历方法:
for(Map.Entry<String,Person> entry : map.entrySet()){
System.out.println(entry.getKey()+"==="+entry.getValue());
}
/**
* HashMap 集合
* 键是 Person,值是 String
*/
public static void hashMap2(){
Map<Person,String> map = new HashMap<Person, String>();
map.put(new Person("a",20),"广东");
map.put(new Person("b",22),"香港");
map.put(new Person("b",22),"广西"); // 不重写 hashCode(),equals()方法会出现两个 b,如果hashCode(),equals()方法在 Person 类中被重写则就会有一个,前一个会被覆盖
map.put(new Person("c",24),"澳门");
map.put(new Person("d",26),"深圳");
System.out.println("map = " + map);
}
1. HashMap 的子类 LinkedHashMap
LinkedHashMap 继承 HashMap 实现 Map 接口,LinkedHashMap 底层实现原理是哈希表
特性:①双向链,②存取有序, ③线程不安全,运行速度快,④其它的特性和父类 HashMap 一样
(LinkedHashSet 依赖的是 LinkedHashMap,LinkedHashSet 本身没有什么功能)
public static void main(String[] args) {
Map<String,String> map = new LinkedHashMap<String, String>();
map.put("a","qq");
map.put("b","qq");
map.put("c","qq");
System.out.println(map); // 怎么存怎么取
}
五、 Map 接口的实现类 Hashtable 集合类
Map 接口的实现类 Hashtable,Hashtable 类诞生于 JDK1.0 版本,Map 接口诞生于 JDK1.2 版本,Hashtable 类从 JDK1.2 开始,改进为实现 Map 接口
- Hashtable 类的特点:
- 底层数据结构是哈希表
- 线程安全,运行速度慢,所以被更加先进的 HashMap 取代(Hashtable 的使用和 HashMap 一样)
- 不允许 null 值,null 键,,存储 null 会直接抛出空指针异常
1. Hashtable 的子类 Properties
- Properties 集合特点:
- 继承 Hashtable,实现 Map 接口
- 底层是哈希表结构
- 线程是安全的,运行速度慢
- 集合没有泛型的写法,键和值的数据类型锁定为 String 类型
- 集合有自己的特有方法
- 此集合可以和 IO 流对象结合使用,实现数据的持久存储
- 它其中有一个方法和 IO 相关:load (输入流)
/**
* 集合存储键值对
* Properties 集合存储方法:setProperty(String key,String value) (其实这其中还是调用的 put() 方法)
*/
public static void prop1(){
Properties prop = new Properties();
prop.setProperty("a","1");
prop.setProperty("b","2");
prop.setProperty("c","3");
System.out.println(prop);
}
/**
* 集合取出元素
* Properties 集合取出方法:getProperty(String key)
*/
public static void prop2(){
Properties prop = new Properties();
prop.setProperty("a","1");
prop.setProperty("b","2");
prop.setProperty("c","3");
System.out.println(prop);
String value = prop.getProperty("a");
System.out.println(value);
}
/**
* 集合遍历
* Properties 类的方法:stringPropertyNames() [等效于 map.keySet()] 返回 Set 集合
* Set 集合存储的是 Properties 集合的所有键
*/
public static void prop3(){
Properties prop = new Properties();
prop.setProperty("a","1");
prop.setProperty("b","2");
prop.setProperty("c","3");
Set<String> set = prop.stringPropertyNames();
for(String key : set){
System.out.println(key +"="+ prop.getProperty(key));
}
}
六、Collection 接口下的 List 接口的实现类 Vector 集合类
List 接口的实现类 Vector,命运和 Hashtable 一样
Vector 类诞生于 JDK1.0 版本,List 接口诞生于 JDK1.2 版本
- Vector类的特点:
- 底层实现结构是数组
- 数组的默认容量是10,每次扩容是:原来的长度 * 2
- 线程安全,运行速度慢,所以被 ArrayList 取代(Vector 的使用和 ArrayList 一样)
七、 Collection 接口下的 Map 接口的实现类 TreeMap 集合
- TreeMap 集合的特点:
- 底层实现是红黑树结构 (添加查询速度都比较快)
-
- 线程不安全,但是运行速度快
- 存储到 TreeMap 中的元素,要求对键进行排序(与值无关)
- 排序依据:
- 对象的自然顺序:作为键的对象,实现了接口 Comparable(只要实现了这个接口就叫做具备了自然顺序)
- 可以自己提供比较器,需要实现接口 Comparator,(比较器的优先级高)
- 排序依据:
应用之前定义的 Person 类,并进行实现接口 Comparable ,重写抽象方法:
public class Person implements Comparable<Person>{
/**
* 为使 TreeMap 集合存储对象后可以取出对象,所以要实现接口 Comparable
* 重写抽象方法,这样 Person 就具备了自然顺序
*/
private String name;
private int age;
/**
* 进行比较
* compareTo 此方法由 集合 TreeMap 调用
* 传递相关的参数:集合中后来的参数是 this ,先来的对象是参数 p
*/
public int compareTo(Person p){
return this.age - p.age; // 这样 Person
}
public Person(){}
public Person(String name, int age) {
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 boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
方法:
/**
* TreeMap 集合存储对象
* Person 作为键,字符串作为值
*/
public static void treeMap01(){
Map<Person,String> map = new TreeMap<Person, String>();
map.put(new Person("a",20),"广东");
map.put(new Person("b",19),"广西");
System.out.println("map = " + map);
// 为了具备自然顺序,调用的比较的方法返回值是 0 时,后者就会覆盖前者
}
定义一个 Student 类:
public class Student {
private String name;
private int age;
public Student(){}
public Student(String name, int age) {
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 boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
自定义的比较器:
/**
* 自定义的比较器,实现接口 Comparator
*/
class MyCom implements Comparator<Student>{
/**
* 方法compare 是TreeMap调用
* 传递参数,后来的对象传递到s1, 已经有的对象传递到s2
*/
public int compare(Student s1, Student s2){
return s1.getAge() - s2.getAge();
}
}
方法:
/**
* TreeMap 集合存储对象
* Student 作为键,字符串作为值
* 自定义的比较器排序
*/
public static void treeMap02(){
Map<Student,String> map = new TreeMap<Student, String>( new MyCom() );
map.put(new Student("a",20),"广东");
map.put(new Student("b",19),"广西");
System.out.println("map = " + map);
}
八、案例:客户的关系管理系统
需求:
- 必须提供用户的菜单(展示本系统的所有功能,用户选择)
- 定义类描述客户的数据,属性:姓名,年龄,邮件
- 客户数据,存储在集合,定义集合,存储客户对象
- 初始化数据,程序启动,集合中存储一些数据
- 添加客户数据 (录入信息),重名的不能添加
- 修改客户数据,判断是否存在用户,检测姓名
- 删除客户数据,判断是否存在用户,检测姓名
- 查询数据:集合遍历
定义用户类:
/**
* 客户类 对象
*/
public class Customer {
private String name; // 姓名
private int age; // 年龄
private String email; // 电子邮件
public Customer(){}
public Customer(String name, int age, String email) {
this.name = name;
this.age = age;
this.email = email;
}
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;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "Customer{" +
"name='" + name + '\'' +
", age=" + age +
", email='" + email + '\'' +
'}';
}
}
客户关系管理系统:
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
* 客户关系管理系统
*/
public class CustomerManager {
// 定义集合(增删改查)
private List<Customer>customerList=new ArrayList<>();
// 共性方法,成为成员私有变量
private Scanner scanner=new Scanner(System.in);
// 集合数据的初始化(让系统中先存在一部分数据)
private void init(){
customerList.add(new Customer("张三",20,"zs@qq.com"));
customerList.add(new Customer("李四",22,"ls@sina.com"));
}
// 构造器(外界一 new 创建对象,就会有数据)
public CustomerManager(){
init();
}
// 提供用户的功能菜单
public void menu(){
while (true){ // 只有在读入退出系统时才退出
System.out.println("欢迎进入客户的关系管理系统");
System.out.println("1.添加用户的数据");
System.out.println("2.修改用户的数据");
System.out.println("3.删除用户的数据");
System.out.println("4.查找用户的数据");
System.out.println("5.退出系统");
System.out.println("请输入编号,选择功能");
String number=scanner.nextLine();
switch (number){
case "1":
// 调用增加客户信息的方法
addCustomer();
break;
case "2":
// 调用修改客户信息的方法
break;
case "3":
// 调用删除客户信息的方法
deleteCustomer();
break;
case "4":
// 调用查找客户信息的方法
selectCustomer();
break;
case "5":
System.exit(0); //虚拟机停止运行
default:
System.out.println("输入错误!");
break;
}
}
}
// 添加客户的方法
private void addCustomer(){
// 提示
System.out.println("选择的是添加客户数据");
System.out.println("请输入姓名:");
String name=scanner.nextLine();
// 遍历集合,取出每个客户的对象,检查是否重名,禁止输入重名
for(Customer customer:customerList){
if(customer.getName().equals(name)){
// 重名禁止添加
System.out.println("姓名已存在,请重新选择功能:");
return; // 循环结束,方法结束
}
}
System.out.println("请输入年龄:");
int age=Integer.parseInt(scanner.nextLine()); // 转换数据类型吗,nextInt() 会有异常
System.out.println("输入邮箱地址:");
String email=scanner.nextLine();
// 把用户的数据,存储到 customer 对象,存储集合
customerList.add(new Customer(name,age,email));
// 提示:
System.out.println("数据添加成功!");
}
// 查询客户数据 遍历集合
private void selectCustomer(){
if(customerList.isEmpty()){ // 判断集合是否为空,没有元素直接结束方法
System.out.println("对不起,没有该数据");
return; // 方法结束
}
for(Customer customer:customerList){
System.out.println(customer);
}
}
// 删除用户数据
private void deleteCustomer(){
System.out.println("选择的是删除用户功能");
System.out.println("请输入要删除的姓名:");
String name=scanner.nextLine();
// 定义变量,保存可以删除的索引
int index=-1;
// 遍历集合,查找集合中是否有这个名字
for(int x=0;x<customerList.size();x++){
Customer customer=customerList.get(x);
if(customer.getName().equals(name)){
// 如果集合中有这个名字,循环结束
index=x; // 记录名字出现的索引
break;
}
}
if(index==-1){
System.out.println("对不起,没有该用户");
return;
}
customerList.remove(index);
System.out.println("删除成功!");
}
}
测试:
public static void main(String[] args) {
// 启动程序,调用菜单方法
new CustomerManager().menu();
}