Java的集合Set和Map的简单精辟讲解
set - - - 接口
- **解释:**存储的数据是无序的,不可以重复。
- 分类:
- HashSet - - - 类: 底层是hash表,线程不安全。
- TreeSet - - - 类: 底层是二叉树,线程不安全。
- HashSet:
- 去重的原理: 是通过调用元素内部的hashcode和equals方法实现去重,首先调用hashcode方法,比较两个元素的哈稀值,如果不同的时候,直接认为是两个对象,停止比较,如果相同,再去调用equals比较。
- 注意的点:hashset本身不能排序,对于自定义的类,要想按照自己自定义的规则去重,必须重写hashcode和equals方法。
- 实例:使用hashset实现对person1类对象的去重
class Person1 {
String name;
int age;
public Person1() {
// TODO Auto-generated constructor stub
}
public Person1(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
//重写hashCode方法
@Override
public int hashCode() {
// TODO Auto-generated method stub
return (this.name + this.age * 1000) .hashCode();
}
@Override
public boolean equals(Object obj) {
if( !(obj instanceof Person1)) {
throw new ClassCastException();
}
Person1 person = (Person1) obj;
return person.age == this.age && person.name.equals(this.name);
}
}
public class Demo7 {
public static void main(String[] args) {
HashSet<String> set = new HashSet<>();
//在string中重写了hashCode和equals方法。
//在add内部实现了去重的功能,默认调用的是字符串的hashCode和equals方法,实现去重
set.add("java1");
set.add("java2");
set.add("java2");
set.add("java5");
set.add("java3");
set.add("java4");
System.out.println(set);
HashSet<Person1> set1 = new HashSet<>();
//在string中重写了hashCode和equals方法。
//在add内部实现了去重的功能,默认调用的是字符串的hashCode和equals方法,实现去重
set1.add(new Person1("小明", 20));
set1.add(new Person1("小明", 20));
set1.add(new Person1("小红", 20));
set1.add(new Person1("小李", 20));
set1.add(new Person1("小花", 20));
set1.add(new Person1("小白", 20));
System.out.println(set1);
}
}
* 分析**<font color=#FF0000>(重点)</font>**:要重写Person1的HashCode和equals方法,按照自己定义的规则来重写,比较规则是按照年龄和性别比较。先是用hashcode方法来比较年龄,来确定它是不是同一个年龄,如果年龄相同了,那么则判定他们的姓名,相同则是同一个对象,否则,就不是同一个对象。
- TreeSet:
- 说明:可以实现去重和排序,使用TreeSet存储字符串的时候,自动实现了排序。
- 能够排序的原因:在add:方法中调用了字符串的 compareTo方法,compareTo方法是来自于Comparable接口。
- 排序规则:按字典顺序比较两个字符串
public class Demo8 {
public static void main(String[] args) {
//默认是升序并且是按照字典排序
set.add("java2");
set.add("java8");
set.add("java3");
set.add("java4");
set.add("java4");
System.out.println(set);
//实例:将Person2的对象存储入TreeSet,实现排序和去重
TreeSet<Person2> set1 = new TreeSet<>();
set1.add(new Person2("bobo", 20));
set1.add(new Person2("bobo", 18));
set1.add(new Person2("bobo3", 200));
set1.add(new Person2("bobo5", 20));
set1.add(new Person2("bobo", 20));
System.out.println(set1);
}
}
class Person2 implements Comparable<Object>{
String name;
int age;
public Person2(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person2 [name=" + name + ", age=" + age + "]";
}
//重写compareTo方法
//按照姓名和年龄比较,只要姓名和年龄相同就认为是一个人
@Override
public int compareTo(Object o) {
//容错处理
if (!(o instanceof Person2)) {
throw new ClassCastException();
}
//向下转型
Person2 person2 = (Person2)o;
//先按照年龄比,年龄相同再按照姓名比
int num = age - person2.age;
return num==0?name.compareTo(person2.name):num;
}
}
分析 (重点):能够实现排序的原因是方法中调用了字符串的 compareTo方法,compareTo方法是来自于Comparable接口。有时候我们需要按照自己定义的规则去实现TreeSet的排序规则的话,就得实现comparator接口,并且重写compare方法,也就是重新定义一个比较器。
-
比较器的创建
class ComWithLength implements Comparator<Object> { @Override public int compare(Object o1, Object o2) { // TODO Auto-generated method stub if(!(o1 instanceof String)) throw new ClassCastException("类型转换错误"); if(!(o2 instanceof String)) throw new ClassCastException("类型转换错误"); //向下转型 String string1 = (String) o1; String string2 = (String) o2; //字符串按照从短到长排序,长度相同按照字典排序 int num = string1.length() - string2.length(); return num == 0 ? string1.compareTo(string2) : num; } }
- 说明:要是用的时候需要将这个比较器传给这个集合实现自己的排序规则。
-
比较器的使用
public class Demo9 { public static void main(String[] args) { //创建一个比较器对象 ComWithLength comWithLength = new ComWithLength(); //将比较器对象指定给当前的TreeSet对象。 TreeSet<Object> treeSet = new TreeSet<>(comWithLength); treeSet.add("java1"); treeSet.add("java2"); treeSet.add("java1"); treeSet.add("java"); System.out.println(treeSet); } }
- 说明:要传入一个比较器才能够实现自定义比较,因为人工的比较器优先级高于系统的。
Map - - - 接口
- **解释:**本身是一个接口,存储的是键值对,一个元素就是一个键(key)值(value)对,key必须是唯一。的.value可以相同
- 分类:
- HashMap - - - 类: 底层是hash表,线程不安全。
- TreeMap - - - 类: 底层是二叉树,线程不安全。
- Map接口的一些方法(用代码来看):
public class Demo8 {
public static void main(String[] args) {
//介绍Map接口的方法
Map<String, String> map = new HashMap<>();
//1.增加
//V put(K key,V value) 增加一个键值对
//关于返回值:如果当前的key没有对应值,返回null.如果已经有值了,会将原来被替换的值返回.
map.put("01", "java");
map.put("02", "iOS");
map.put("03", "html");
map.put("04", "python");
System.out.println(map.put("01", "php"));
System.out.println(map);
//void putAll(Map<? extends K,? extends V> map) 增加多个
//2.删除
//V remove(Object key) 根据key删除元素
map.remove("01");
System.out.println(map);
//void clear() 删除全部
//3.获取
//V get(Object key) 根据key查找元素
System.out.println(map.get("02"));
//int size() 获取键值对的个数
System.out.println(map.size());
//Set<K> keySet() 遍历方法一
//Set<Map.Entry<K,V>> entrySet() 遍历方法二
//4.常用的判断
//boolean isEmpty()
System.out.println(map.isEmpty());//false
//boolean containsKey(K key) 是否包含当前的key
System.out.println(map.containsKey("02"));//true
//boolean containsValue(V value) 是否包含当前的value
System.out.println(map.containsValue("html"));//true
}
}
-
Map的遍历:
- 方法一:得到所有的key,存放在一个Set中.利用set的迭代器遍历得到key,在利用key得到value。
public class Demo9 { public static void main(String[] args) { Map<String, String> map = new HashMap<>(); map.put("01", "java"); map.put("03", "php"); map.put("05", "BigData"); map.put("02", "python"); System.out.println(map); //通过Set<K> keySet() 遍历方法一 Set<String> set1 = map.keySet(); //获取迭代器 Iterator<String> iterator = set1.iterator(); while (iterator.hasNext()) { String key = iterator.next(); //再去获取value System.out.println("key:"+key+" value:"+map.get(key)); } } }
- 方法二:得到每个键值对对应的映射关系类型的对象(entry/实体),存放在一个Set中,利用set的迭代器遍历得到entry,再利用entry的方法得到value和key。
public class Demo9 { public static void main(String[] args) { Map<String, String> map = new HashMap<>(); map.put("01", "java"); map.put("03", "php"); map.put("05", "BigData"); map.put("02", "python"); System.out.println(map); //Set<Map.Entry<K,V>> entrySet() 遍历方法二 //先得到装着entry的set Set<Map.Entry<String, String>> set = map.entrySet(); //获取迭代器 Iterator<Map.Entry<String, String>> iterator2 = set.iterator(); while (iterator2.hasNext()) { Map.Entry<String, String> entry = iterator2.next(); //entry.setValue("hah"); //再去获取key,value System.out.println("key:"+entry.getKey()+" value:"+entry.getValue()); } System.out.println(map); } }
- 说明:Entry是Map内的一个静态接口。因为有了集合中的键值对才有映射关系.而当前的映射关系又是对集合内部的描述,所以要将Entry映射关系接口放入Map接口中。
-
HashMap:
- 去重的原理:是通过调用元素内部的hashcode和equals方法实现去重,首先调用hashcode方法,比较两个元素的哈稀值,如果不同的时候,直接认为是两个对象,停止比较,如果相同,再去调用equals比较。
- 注意的点:hashMap本身不能排序,对于自定义的类,要想按照自己自定义的规则去重,必须重写hashcode和equals方法。
- 实例:将Animal类的对象存入HashMap,名字和年龄相同的认为是同一条狗。
class Animal{ String name; int age; public Animal(String name, int age) { super(); this.name = name; this.age = age; } @Override public String toString() { return "Animal [name=" + name + ", age=" + age + "]"; } //重写hashCode @Override public int hashCode() { return name.hashCode()+age*1000; } //重写equals @Override public boolean equals(Object obj) { if (!(obj instanceof Animal)) { throw new ClassCastException("类型转化错误"); } Animal animal = (Animal)obj; return age==animal.age && name.equals(animal.name); } }
public class Demo10 { public static void main(String[] args) { HashMap<String, String> map = new HashMap<>(); //在put方法中调用了作为key的String的hashCode和equals方法 map.put("01", "java"); map.put("03", "java2"); map.put("05", "java4"); map.put("02", "java6"); map.put("01", "java9"); System.out.println(map); //实例:将Animal类的对象存入HashMap,名字和年龄相同的认为是同一条狗 HashMap<Animal, String> map1 = new HashMap<>(); //在put方法中调用了作为key的Animla的hashCode和equals方法 map1.put(new Animal("拉布拉多", 20), "java"); map1.put(new Animal("柯基", 10), "java2"); map1.put(new Animal("黑贝", 2), "java4"); map1.put(new Animal("茶杯犬", 21), "java6"); map1.put(new Animal("藏獒", 12), "java9"); map1.put(new Animal("藏獒", 12), "java9"); System.out.println(map1); } }
-
TreeMap:
- 说明:可以实现去重和排序,实现排序,去重,类似于TreeSet。
- 方法一:让key去实现Comparable接口。
class Animal1 implements Comparable<Animal1>{
String name;
int age;
public Animal1(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Animal1 [name=" + name + ", age=" + age + "]";
}
//重写compareTo方法
@Override
public int compareTo(Animal1 o) {
int num = age-o.age;
return num==0?name.compareTo(o.name):num;
}
}
public class Demo11 {
public static void main(String[] args) {
TreeMap<String, String> map = new TreeMap<>();
//String实现了Comparable接口的compareTo方法,所以实现了排序和去重
map.put("01", "java");
map.put("03", "java2");
map.put("05", "java4");
map.put("02", "java6");
map.put("01", "java9");
System.out.println(map);
TreeMap<Animal1, String> map1 = new TreeMap<>();
map1.put(new Animal1("labu", 20), "java");
map1.put(new Animal1("keji", 20), "java2");
map1.put(new Animal1("zangao", 12), "java9");
map1.put(new Animal1("zangao", 12), "java9");
System.out.println(map1);
}
}
- 分析**(重点)**:能够实现排序的原因是方法中调用了字符串的 compareTo方法,compareTo方法是来自于Comparable接口。
- 方法二:创建自己的比较器
- 比较器的创建
class MyCompera implements Comparator<Now2>{
@Override
public int compare(Now2 o1, Now2 o2) {
int num = o1.age - o2.age;
return num == 0 ? o1.name.compareTo(o2.name):num;
}
}
class Now2 {
public String name;
public int age;
public Now2() {
super();
// TODO Auto-generated constructor stub
}
public Now2(String name, int age) {
super();
this.name = name;
this.age = age;
}
}
- 说明:要是用的时候需要将这个比较器传给这个集合实现自己的排序规则。
- 比较器的使用
public class Three {
public static void main(String[] args) {
TreeMap<Now2, String> treeMap = new TreeMap<>(new MyCompera());
}
}
- 说明:要传入一个比较器才能够实现自定义比较,因为人工的比较器优先级高于系统的。