你了解集合?那你倒是给我说说啊!【3】
八、Set接口
Set是Collection的子接口,Set接口定义了一种规范,也就是说明该容器不记录元素的添加顺序,也不允许元素重复。
8.1、Set集合的特点
8.2、Set接口常用的实现类
8.3、HashSet
8.3.1、HashSet原理

HashSet底层采用哈希表实现,元素对象的HashCode值决定了在哈希表中存储的位置,当往HashSet中添加新元素的时候,先会判断该位置是否有元素:
每一个存储到哈希表中的对象,都得重写hashCode和equals方法来判断是否是同一个对象,在一般情况下,equals为true的时候,hashCode应该也相等,Idea可以自动生成这些方法
8.3.2、HashSet常用方法
package day14_ArrayList2.classing;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* @author Xiao_Lin
* @version 1.0
* @date 2020/12/17 19:38
*/
public class SetApi {
public static void main(String[] args) {
Set set = new HashSet<>();
//添加操作,向列表中添加元素
set.add("will");
set.add("wolf");
set.add("code");
set.add("lucy");
//查询操作
System.out.println("集合中的所有元素为:"+set);
System.out.println("元素数量:"+set.size());
System.out.println("是否存在某个元素:"+set.contains("111"));
System.out.println("是否存在某个元素:"+set.contains("code"));
//删除元素
set.remove("code");
System.out.println("删除后的集合为:"+set);
//增强for循环遍历
for (String ele : set){
System.out.println(ele);
}
//迭代器遍历
Iterator it = set.iterator();
while (it.hasNext()){
System.out.println( it.next());
}
}
}
package day14_ArrayList2.classing;
/**
重写equals和hashCode方法
* @author Xiao_Lin
* @version 1.0
* @date 2020/12/17 11:34
*/
public class Student {
private String name;
private Integer sn;
private String classname;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Student student = (Student) o;
if (name != null ? !name.equals(student.name) : student.name != null) {
return false;
}
if (sn != null ? !sn.equals(student.sn) : student.sn != null) {
return false;
}
return classname != null ? classname.equals(student.classname) : student.classname == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (sn != null ? sn.hashCode() : 0);
result = 31 * result + (classname != null ? classname.hashCode() : 0);
return result;
}
public Student(String name, Integer sn, String classname) {
this.name = name;
this.sn = sn;
this.classname = classname;
}
public Student(){
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getSn() {
return sn;
}
public void setSn(Integer sn) {
this.sn = sn;
}
public String getClassname() {
return classname;
}
public void setClassname(String classname) {
this.classname = classname;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", sn=" + sn +
", classname='" + classname + '\'' +
'}';
}
}
8.4、TreeSet
TreeSet底层采用了红黑树算法,会对存储的元素对象默认使用自然排序(升序)
必须保证TreeSet集合中的元素对象是相同的数据类型,否则报错
import java.util.Set;
import java.util.TreeSet;
public class TreeSetDemo{
public static void main(String[] args) {
Set set = new TreeSet<>();
set.add("wolf");
set.add("will");
set.add("sfef");
set.add("allen");
System.out.println(set);//[allen, sfef, will, wolf]
}
}
8.4.1、TreeSet底层数据结构详解
第一步
插入第一个节点,无需比较,直接作为根节点

第二步
插入第二个节点,和wolf根节点比较,如果小于根节点就作为左子树,如果大于根节点就放在右边,作为右子树

第三步
插入第三个节点,先和wolf根节点比较,较小,左移动作为左子树,因为左边已经有了一颗左子树,所以再和will节点进行比较,较小,继续放在左边作为左子树

第四步
由于TreeSet是平衡二叉树,如果树不平衡(左右子树差值大于1),会对节点进行调整

第五步
插入第四个节点,先和根节点will进行比较,较小,左移,再和sfef节点作比较,依然较小,左移

8.4.2、Comparable接口
在缺省的情况下,TreeSet中的元素会采用自然排序(从小到大),此时要求元素对象必须实现接口,大多数的JDK自带的类都实现了该接口,比如说最常见的八大包装类和String TreeSet会调用元素的comparaTo()方法来比较元素的大小关系,然后将集合元素按照升序排列
public interface Comparable {
public int compareTo(T o);
}
比较规则
当前元素大于传进来的元素,返回正整数1,优先级较高
当前元素小于传进来的元素,返回负整数-1,优先级较低
当前元素等于传进来的元素,返回0,此时认为两个对象是同一个对象
如果我们自定义一个类,且需要存储到TreeSet中,此时我们需要让该类实现Comparable接口,并且覆盖compareTo()方法,在该方法中编写比较规则
package day14_ArrayList2.classing;
/**
* @author Xiao_Lin
* @version 1.0
* @date 2020/12/17 11:34
*/
public class Student implements Comparable{
private String name;
private Integer sn;
private String classname;
//比较规则
@Override
public int compareTo(Student student) {
//当前对象大于传进来的对象
if (this.sn > student.sn){
return 1;
}else if (this.sn < student.sn){
return -1;
}
return 0;
//可以简化为;
//return this.sn - student.sn;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Student student = (Student) o;
if (name != null ? !name.equals(student.name) : student.name != null) {
return false;
}
if (sn != null ? !sn.equals(student.sn) : student.sn != null) {
return false;
}
return classname != null ? classname.equals(student.classname) : student.classname == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (sn != null ? sn.hashCode() : 0);
result = 31 * result + (classname != null ? classname.hashCode() : 0);
return result;
}
public Student(String name, Integer sn, String classname) {
this.name = name;
this.sn = sn;
this.classname = classname;
}
}
8.4.3、Comparator接口
TreeSet除了支持默认的自然排序外,还支持自定义排序,此时需要在构建TreeSet对象时传递接口的实现类对象,Comparator表示比较器,里面封装了比较规则。
此时compare方法返回0,则认为两个对象是同一个对象,返回正数排前面,返回负数排后面。
public interface Comparator {
int compare(T o1, T o2);
}
示范
class User {
private String name;
import java.util.TreeSet;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
public String toString() {
return "User [name=" + name + ", age=" + age + "]";
}
}
public class App {
public static void main(String[] args) {
Set set = new TreeSet<>(new NameLengthComparator());
set.add(new User("James", 30));
set.add(new User("Bryant", 22));
set.add(new User("Allen", 28));
set.add(new User("Will", 17));
System.out.println(set);//
}
}
//封装了比较规则
class NameLengthComparator implements java.util.Comparator {
public int compare(User o1, User o2) {
int ret = o1.getName().length() - o2.getName().length();
if (ret > 0) {
return 1;
} else if (ret < 0) {
return -1;
} else {
if (o1.getAge() > o2.getAge()) {
return 1;
} else if (o1.getAge() < o2.getAge()) {
return -1;
} else {
return 0;
}
}
}
}
九、Map接口
Map翻译为映射

从结构图上看,Map并不是集合,而是类似两个集合的映射关系,所以Map中没有实现Collection接口
在Map中,要求A集合的每一个元素(key)都可以在B集合中找到唯一的值(value)与之对应,意味着A集合中的元素是不可以重复的而B集合中的元素却可以重复,所以A集合应该是一个Set集合,而B集合是一个List集合

其实一个Map中就由很多的key-value(kv键值对)组成的,每一个键值对我们用Entry表示

其实我们也可以把Map看成是多个Entry的集合

总的来说,我们还是习惯把Map称为集合,不过和List、Set不同,List、Set是单元素集合,Map是双元素集合
单元素集合:每一次只能存储一个元素,比如List、Set
双元素集合:每次需要存储两个元素(一个key一个value),比如Map
注意:
Map接口并没有继承Collection接口也没有继承Iterable(可迭代的)接口,所以不可以直接对Map使用for-each遍历操作
其中value就表示存储的数据,而key就是这个value的名字
9.1、Map常用的API
9.1.1、添加操作
9.1.2、删除操作
Object remove(Object key):从Map中删除指定key的键值对,并返回被删除key对应的value
9.1.3、修改操作
无专门的方法,可以调用put方法,存储相同key,不同value的键值对,可以覆盖原来的。
9.1.4、查询操作
9.1.5、Lambda 8表达式集合遍历
map.forEach((k,v)->{
System.out.println(k+" "+v);
});
9.2、HashMap
HashMap底层是基于哈希表算法,Map中存储的key对象和HashCode值决定了在哈希表中存储的位置,因为Map中的key是Set,所以不能保证添加的先后顺序,也不允许重复
import java.util.HashMap;
import java.util.Map;
public class HashMapDemo1{
public static void main(String[] args) {
Map map = new HashMap<>();
map.put("girl1", "西施");
map.put("girl2", "王昭君");
map.put("girl3", "貂蝉");
map.put("girl4", "杨玉环");
System.out.println("map中有多少键值对:"+map.size());
System.out.println(map);
System.out.println("是否包含key为girl1:"+map.containsKey("girl1"));
System.out.println("是否包含value为貂蝉:"+map.containsValue("貂蝉"));
//替换key为girl3的value值
map.put("girl3", "小乔");
System.out.println(map);
//删除key为girl3的键值对
map.remove("girl3");
System.out.println(map);
}
}
9.2.1、Map的迭代遍历
//获取Map中所有的key
Set keys = map.keySet();
System.out.println("Map中所有key:"+keys);
//获取Map中所有的value
Collection values = map.values();
System.out.println("Map中所有value:"+values);
//获取Map中所有的key-value(键值对)
Set> entrys = map.entrySet();
for (Entry entry : entrys) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key+"->"+value);
}
9.2.2、练习
统计一个字符串中每隔字符出现次数
package day14_ArrayList2.classing;
import java.util.HashMap;
import java.util.Scanner;
/**
* @author Xiao_Lin
* @version 1.0
* @date 2020/12/17 16:54
*/
public class Test1 {
//需求:统计一个字符串中每一个字符出现的次数
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String str = scanner.nextLine();
HashMap map = new HashMap();//新建一个map,map的key存储字符,value存储出现的次数
//进行map的遍历
for (int i = 0 ;i
9.3、TreeMap
TreeMap底层的key是基于红黑树,因为Map中的key也是一个Set集合,所以不能保证添加的先后顺序,且也不允许重复,因为Map中存储的key会默认使用自然排序(从小到大),和TreeSet一样,除了可以使用自然排序也可以自定义自己的排序
import java.util.Map;
import java.util.TreeMap;
public class App {
public static void main(String[] args) {
Map map = new TreeMap<>();
map.put("girl4", "杨玉环");
map.put("girl2", "王昭君");
map.put("key1", "西施");
map.put("key3", "貂蝉");
System.out.println(map);
//-------------------------------------------
map = new TreeMap<>(map);
System.out.println(map);
}
}
package day14_ArrayList2.classing;
import java.util.Comparator;
import java.util.TreeSet;
/**
* @author Xiao_Lin
* @version 1.0
* @date 2020/12/17 15:55
*/
public class TreeSet1 {
public static void main(String[] args) {
//使用自定义的比较器,需要传入一个Comparator接口的实现类,这里使用匿名内部类
TreeSet set = new TreeSet(new Comparator() {
@Override
public int compare(String s1, String s2) {
if (s1.length()==s2.length()){
return s1.compareTo(s2);
}else {
return s1.length()-s2.length();
}
}
});
set.add("s12");
set.add("see");
set.add("s1234");
System.out.println(set);
}
}