Set集合
一、1、集合框架Set
1. set集合不能存放重复元素的问题
- 不能存放重复元素 (字符串、八大基本数据类型)
2. HashSet哈希表存储、重复元素存储底层探究 - list.contains可以判重是因为底层调用了epuals方法
- Set.add 底层调用了hashCode/epuals方法
下图验证结果:
验证原码:
package com.wangjing.set;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class SetDemo {
public static void main(String[] args) {
Set set=new HashSet<>();
set.add("laohu");
set.add("laoqiu");
set.add("laochen");
set.add("laoliu");
set.add("laohu");
System.out.println(set.size());
}
}
- set集合中的元素是无序的
下图验证结果:
验证原码:
package com.wangjing.set;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
public class SetDemo {
public static void main(String[] args) {
Set set=new HashSet<>();
set.add(new Person("laohu", 19, 1000));
set.add(new Person("laoqiu", 16, 1500));
set.add(new Person("laochen", 18, 1200));
set.add(new Person("laoliu", 20, 2500));
set.add(new Person("laohu", 19, 1000));
Iterator it=set.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}
}
class Person{
private String name;
private int age;
private int 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 + "]";
}
public Person(String name, int age, int money) {
super();
this.name = name;
this.age = age;
this.money = money;
}
public Person() {
super();
}
}
当重写hashCode()方法的时候两个人比较的信息都是相同的时候打印内存地址也是相同的,下图所示:
重写hashCode()方法的代码:
@Override
public int hashCode() {
System.out.println("hashCode-------"+this.name);
int code=this.name.hashCode() + this.age;
System.out.println(code);
return code;
}
当即重写hashCode()和equals()的时候两个人对比的信息相同的时候就只会打印一个出来,下图所示:
重写equals()方法的代码:
@Override
public boolean equals(Object obj) {
Person p= (Person) obj;
return this.name.equals(p.name) && this.age==p.age;
}
二、集合框架TreeSet
1、TreeSet容器根据二叉树的排序规则对容器中元素进行排序的
自然排序(元素自身具有比较性)下图所示:
上图验证原码:
package com.wangjing.set;
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet set=new TreeSet<>();
set.add(28);
set.add(22);
set.add(25);
set.add(38);
set.add(21);
System.out.println(set);
}
}
当你执行这样一行代码的时候:
package com.wangjing.set;
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet set=new TreeSet<>();
set.add(new Person("laohu", 19, 1000));
set.add(new Person("laoqiu", 16, 1500));
set.add(new Person("laochen", 18, 1200));
set.add(new Person("laoliu", 20, 2500));
set.add(new Person("laohu", 19, 1000));
System.out.println(set);
}
}
class Person {
private String name;
private int age;
private int 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 + "]";
}
public Person(String name, int age, int money) {
super();
this.name = name;
this.age = age;
this.money = money;
}
public Person() {
super();
}
@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;
}
}
就会报一个转换类型异常下图所示:
如果要把这个异常解决,就要实现接口Comparable并且重写Comparable里面的compareTo方法,而compareTo这个方法就是让元素具有比较性的方法
如下图所示,当你要按年龄从小到大排序的时候:
上图验证的原码:
package com.wangjing.set;
import java.util.Iterator;
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet set=new TreeSet<>();
set.add(new Person("laohu", 19, 1000));
set.add(new Person("laoqiu", 16, 1500));
set.add(new Person("laochen", 18, 1200));
set.add(new Person("laoliu", 20, 2500));
set.add(new Person("laohu", 19, 1000));
// 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 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 + "]";
}
public Person(String name, int age, int money) {
super();
this.name = name;
this.age = age;
this.money = money;
}
public Person() {
super();
}
@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) {
// TODO Auto-generated method stub
return this.age-o.age;
}
}
当你需要修改需求的时候只需要修改compareTo方法里面的需求代码即可,如下图所示的需求是先要显示年龄大再显示钱多的:
主意:在自然排序方法重写的时候,一定先判断主要条件,还要判断次要条件
当你在需求方面都是不一样的时候并不需要去修改compareTo的原码,只要写比较器即可,这个也是TreeSet的自然比较器
下图示例
注意:class PersonAgeMoney implements Comparator重写的方法导的是 java.util包里面的而public int compareTo(Person o)导的是java.lang的:
上图验证原码:
package com.wangjing.set;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet set=new TreeSet<>(new PersonAgeMoney());
set.add(new Person("laohu", 19, 1000));
set.add(new Person("laoqiu", 16, 1500));
set.add(new Person("laochen", 18, 1200));
set.add(new Person("laoliu", 20, 2500));
set.add(new Person("laohu", 19, 1000));
// System.out.println(set);
Iterator it=set.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}
}
class PersonAgeMoney 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;
}
}
class Person implements Comparable<Person>{
private String name;
private int age;
private int 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 + "]";
}
public Person(String name, int age, int money) {
super();
this.name = name;
this.age = age;
this.money = money;
}
public Person() {
super();
}
@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;
}
三,泛型
运行如下代码:
package com.wangjing.set;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class TypeDemo {
public static void main(String[] args) {
List c=new ArrayList<>();
c.add(22);
c.add(23);
c.add(25);
c.add(28);
c.add(55);
c.add("c");
Iterator it=c.iterator();
while(it.hasNext()) {
int num=(int) it.next();
if(num%2==0) {
System.out.println(num);
}
}
}
}
就会把转换异常,下图所示:
好处:
1.将运行期的异常转换成编译期的错误,让程序员更早的发现,从而解决代码的隐患
2.提高了代码的健壮性
3.可以简化代码,还可以重复利用你写的代码
下图加了泛型之后:
**泛型对于程序员来说是个节省时间提高效率的好帮手:
再写dao包时我们普通的写法是:
* 购物车项目
* 订单模块OrderDao,用户模块UserDao、商品模块ProductDao
* 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>{}
只要写好一个基础泛型后,其他的都能用上,区别只是改变‘T’而已
泛型类:
泛型方法:
public BaseDao(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定
this.key = key;
}
静态方法泛型:
public static <T> void add(T t){}
1
泛型接口:
//定义一个泛型接口
public interface BaseDao<T> {
public T next();
}