一、集合框架Set(HashSet哈希表存储、重复元素存储底层探究)
1、Set简介
① 元素是无序(存入和取出的顺序不一定一致),元素不可以重复;
② 查看Api会发现,set集合的功能与Collection集合的功能是一致的(就方法调用而言);
③ HashSet哈希表存储
- HashSet存入重复元素
<1>同姓名同年龄视为同一个人 ,重复元素 - 从代码上看,equals方法没有被调用
④ 底层数据结构是哈希表
2、set集合不能存放重复元素的问题
代码显示:
package com.qiuwenfan.set;
import java.util.HashSet;
import java.util.Set;
public class SetDemo {
public static void main(String[] args) {
Set set = new HashSet<>();
set.add("aaa");
set.add("bbb");
set.add("ccc");
set.add("abc");
set.add("aaa");
System.out.println(set.size());
}
}
运行结果:
注意:当出现重复时数据不会添加
3、HashSet哈希表存储、重复元素存储底层探究
① 元素是无序(存入和取出的顺序不一定一致)
② set.add 底层调用了hashCode/equals
package com.qiuwenfan.set;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class SetDemo {
public static void main(String[] args) {
Set set = new HashSet<>();
set.add(new Person("aaa", 18, 1500));
set.add(new Person("bbb", 19, 2500));
set.add(new Person("ccc", 28, 4500));
set.add(new Person("abc", 30, 1000));
set.add(new Person("aaa", 18, 1500));
System.out.println(set.size());
Iterator it = set.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}
}
class Person{
private String name;
private int age;
private int money;
public Person() {}
public Person(String name, int age, int money) {
super();
this.name = name;
this.age = age;
this.money = money;
}
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 int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", money=" + money + "]";
}
@Override
public int hashCode() {
System.out.println("hashCode------"+this.name);
int code = this.name.hashCode()+this.age;
System.out.println(code);
return code;
}
@Override
public boolean equals(Object obj) {
Person p = (Person) obj;
return this.name.equals(p.name) && this.age == p.age;
}
}
图1
图2
hashCode/equals调用后
注意:图1,图2为两次运行结果,且顺序不相同
二、集合框架TreeSet(自然排序、数据结构二叉树、比较器排序)
1、TreeSet自然排序
TreeSet可以对set集合中元素进行排序
String实现了Comparable接口,所以可以直接进行排序
引用数据类型想要排序,必须实现Comparable接口
基础代码展示:
package com.qiuwenfan.set;
import java.util.TreeSet;
/**
* 2、集合框架TreeSet(自然排序、数据结构二叉树、比较器排序)
* treeSet容器是根据二叉树的排序规则对容器中的元素进行排序的 自然排序(元素自身具有比较性)
* @author qiuwenfan
*
*/
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet set = new TreeSet<>();
set.add(26);
set.add(20);
set.add(22);
set.add(36);
set.add(16);
System.out.println(set);
}
}
输出结果:
注意:其他引用数据类型没有实现Comparable接口,那么会出现java.lang.ClassCastException: com.javaxl.Peroon cannot be cast to java.lang.Comparable,实现引用数据类型实现此接口就没问题了。
如图:
引用数据类型排序,实现Comparable接口
代码演示:
package com.qiuwenfan.set;
import java.util.Iterator;
import java.util.TreeSet;
/**
* 2、集合框架TreeSet(自然排序、数据结构二叉树、比较器排序)
* treeSet容器是根据二叉树的排序规则对容器中的元素进行排序的 自然排序(元素自身具有比较性)
* @author qiuwenfan
*
*/
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet<Object> set = new TreeSet<>();
set.add(new Person("aaa", 88, 1500));
set.add(new Person("bbb", 19, 2500));
set.add(new Person("ccc", 28, 4500));
set.add(new Person("abc", 30, 1000));
set.add(new Person("aaa", 18, 1500));
// abc/bc/de/dea/def
// System.out.println(set);
Iterator it = set.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}
}
class Person implements Comparable<Person>{
private String name;
private int age;
private int money;
public Person() {}
public Person(String name, int age, int money) {
super();
this.name = name;
this.age = age;
this.money = money;
}
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 int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", money=" + money + "]";
}
@Override
public int hashCode() {
System.out.println("hashCode------"+this.name);
int code = this.name.hashCode()+this.age;
System.out.println(code);
return code;
}
@Override
public boolean equals(Object obj) {
Person p = (Person) obj;
return this.name.equals(p.name) && this.age == p.age;
}
/**
* 让年龄具备比较性
* 注意:
* 在做自然排序方法重写的时候,一定先判断主要条件,还要判断次要条件
*
*/
@Override
public int compareTo(Person o) {
int num = o.money-this.money;
if(num == 0) {
return o.age - this.age;
}
return num;
}
}
演示结果:
注意:TreeSet排序的第一种方式,让元素自身具有比较性;
元素需要实现Comparable接口,覆盖compareTo方法;
这种方式也被称为元素的自然顺序,或者叫做默认顺序。
2、TreeSet数据结构(二叉树)
可以对set集合进行排序,底层数据结构是二叉树;
保证元素唯一性的依据是,compareTo方法return 0
二叉树图解:
问:那么如何让TreeSet集合中的元素怎么存进去怎么取出来呢?
答:
compareTo方法返回值为正数,返回值写死,那么就是怎么存进去怎么取出来。
compareTo方法返回值为负数数,返回值写死,那么就是先进后出。
3、TreeSet比较器排序
① 定义一个类,实现comparator接口,实现compare方法
② 当两种排序都存在时,比较器排序优先级更高
代码演示:
package com.qiuwenfan.set;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
/**
* 2、集合框架TreeSet(自然排序、数据结构二叉树、比较器排序)
* treeSet容器是根据二叉树的排序规则对容器中的元素进行排序的 自然排序(元素自身具有比较性)
* a、自然排序
* b、比较器排序(应对活动规则的不断改变更新所带来的,代码频繁修改的不便的情况)
* @author qiuwenfan
*
*/
public class TreeSetDemo {
public static void main(String[] args) {
// TreeSet<Object> set = new TreeSet<>();
// TreeSet<Person> set = new TreeSet<>(new PersonAgeMoneyComp());
TreeSet<Person> set = new TreeSet<>(new PersonMoneyAgeComp());
set.add(new Person("张三", 88, 8500));
set.add(new Person("李四", 19, 2500));
set.add(new Person("王五", 28, 4500));
set.add(new Person("赵六", 28, 1000));
set.add(new Person("张三", 18, 8500));
// abc/bc/de/dea/def
// System.out.println(set);
Iterator it = set.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}
}
/**
*
* 对年龄和钱进行的排序(年少多金)
* @author
*
*/
class PersonAgeMoneyComp implements Comparator<Person>{
@Override
public int compare(Person o1, Person o2) {
int num = o1.getAge() - o2.getAge();
if(num == 0) {
return o2.getMoney() - o1.getMoney();
}
return num;
}
}
/**
*
* 对年龄和钱进行的排序(有钱并且年轻)
* @author
*
*/
class PersonMoneyAgeComp implements Comparator<Person>{
@Override
public int compare(Person o1, Person o2) {
int num = o2.getMoney() - o1.getMoney();
if(num == 0) {
return o1.getAge() - o2.getAge();
}
return num;
}
}
class Person implements Comparable<Person>{
private String name;
private int age;
private int money;
public Person() {}
public Person(String name, int age, int money) {
super();
this.name = name;
this.age = age;
this.money = money;
}
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 int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", money=" + money + "]";
}
@Override
public int hashCode() {
System.out.println("hashCode------"+this.name);
int code = this.name.hashCode()+this.age;
System.out.println(code);
return code;
}
@Override
public boolean equals(Object obj) {
Person p = (Person) obj;
return this.name.equals(p.name) && this.age == p.age;
}
/**
* 让年龄具备比较性
* 注意:
* 在做自然排序方法重写的时候,一定先判断主要条件,还要判断次要条件
*
*/
@Override
public int compareTo(Person o) {
int num = o.money-this.money;
if(num == 0) {
return o.age - this.age;
}
return num;
}
}
图片结果(年少多金):
图片结果(有钱并且年轻):
当元素自身不具备比较性时,或者具备的比较性不是所需要的;
注意:这时需要让集合自身具备比较性
在集合初始化时,就有了比较方式;
③ 因此比较器用的多一些,在实际开发中,很多时候,引用数据类型是否具有比较性,或者比较规则,可能不由开发人员自己决定,
④ 那么开发人员想要对应的引用数据类型按照自己的排序方式进行排列,那么就需要实现comparator接口,实现compare方法
三、泛型(概述及使用、泛型类、泛型方法、静态方法泛型、泛型接口)
一、好处/为什么出现?
- 不使用泛型的情况下,会将未知的错误表现在运行时期
如果说用代码去处理了,这个可能发现的错误,那么运行时期的错误就不会暴露出来
当增加String类型的时候,会出现转化异常
代码演示:
public class FanXinDemo {
public static void main(String[] args) {
List c=new ArrayList();
c.add(22);
c.add(23);
c.add(25);
c.add(27);
c.add(30);
c.add("s");
// ctrl+shift+o 快速导包
Iterator it = c.iterator();
while(it.hasNext()) {
Object obj = it.next();
int num = (int) obj;
if(num % 2 == 0) {
System.out.println(num);
}
}
}
}
图片结果:
解决方法:
public class FanXinDemo {
public static void main(String[] args) {
List c=new ArrayList();
c.add(22);
c.add(23);
c.add(25);
c.add(27);
c.add(30);
c.add("s");
// ctrl+shift+o 快速导包
Iterator it = c.iterator();
while(it.hasNext()) {
Object obj = it.next();
if(obj instanceof Integer) {
int num = (int) obj;
if(num % 2 == 0) {
System.out.println(num);
}
}
}
}
}
图片结果:
泛型的好处
- 将运行期的异常转换成编译期的错误、让程序员更早的发现,从而解决代码的隐患
- 提高了代码健壮性
代码演示:
public class FanXinDemo {
public static void main(String[] args) {
List<Integer> c=new ArrayList();
c.add(22);
c.add(23);
c.add(25);
c.add(27);
c.add(30);
// c.add("s");
// ctrl+shift+o 快速导包
Iterator it = c.iterator();
while(it.hasNext()) {
Object obj = it.next();
int num = (int) it.next();
if(num % 2 == 0) {
System.out.println(num);
}
}
}
}
注意:当加入String类型的值时会直接显示错误(上述代码中的c.add(“s”))
二、泛型类、泛型接口、泛型方法
对于编码而言有什么更深层次的好处呢?
代码:
* 购物车项目
* 订单模块,用户模块、商品模块
* Class OrderDao{//订单模块
* public List<Order> list(Order order){
* public int add(Order order){}
* public int edit(Order order){}
* public int del(Order order){}
*
* }
*
* Class UserDao{//用户模块
* public List<User> list(User User){
* public int add(User User){}
* public int edit(User User){}
* public int del(User User){}
*
* }
*Class ProductDao{//商品模块
*
* }
*
* ————不使用泛型的情况————
* ————使用泛型的情况————
*
* Class BaseDao<T>{
* public List<T> list(T t){}
* public int add(T t){}
* public int edit(T t){}
* public int del(T t){}
* }
*
* Class OrderDao extends BaseDao<Order>{}
* Class UserDao extends BaseDao<User>{}
* Class ProductDao extends BaseDao<Product>{}
*