Bootstrap

你了解集合?那你倒是给我说说啊!【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);
  }
}