set接口
1.set接口
set接口是一个无序的、唯一的容器(排列顺序与添加的顺序无关)
set接口提供的方法
public static void main(String[] args) { /** * 增:add/addAll * 删:clear/remove/removeAll/retainAll * 改: * 查:contains/containsAll * 遍历:iterator * 其他:size/isEmpty */ Set<Integer> set = new HashSet<Integer>(); // [1]添加 // 无序 set.add(10); set.add(3); set.add(20); set.add(0); // 不能添加重复元素 boolean r = set.add(1); System.out.println(set); // 【2】删除 // set.remove(1); // set.clear(); // System.out.println(set); // 【3】查看是否包含 System.out.println(set.contains(1)); // 【4】其他 System.out.println(set.size()); System.out.println(set.isEmpty()); }
set接口的遍历方法
public static void main(String[] args) { Set<String> set = new HashSet<String>(); set.add("banana"); set.add("apple"); set.add("coco"); // 快速遍历 for (String item : set) { System.out.println(item); } // 迭代器 Iterator<String> it = set.iterator(); while(it.hasNext()) { String item = it.next(); System.out.println(item); } }
set接口的实现类常用的有HashSet、LinkHashSet、TreeSet
1.1 HashSet
HashSet是Set接口的实现类,底层数据结构是哈希表。
HashSet是线程不安全的(不保证同步)
1.1.1 哈希表工作原理
1.1.2 添加自定义对象
根据哈希表的工作原理,请存储一个自定义对象到HashSet中。
public class Student { private String id; private String name; private int age; // … @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Student other = (Student) obj; if (age != other.age) return false; if (id == null) { if (other.id != null) return false; } else if (!id.equals(other.id)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } @Override public String toString() { return "Student [id=" + id + ", name=" + name + ", age=" + age + "]"; } }
总结
[1]如果向HashSet中存储元素时,元素一定要实现hashCode方法和equals方法。
[2] 优点:添加、删除、查询效率高;缺点:无序
1.2 LinkedHashSet
LinkedHashSet是Set接口的实现类,底层数据结构哈希表+链表
哈希表用于散列元素;链表用于维持添加顺序。
如果要添加自定义对象元素,也需要重写hashCode和equals方法。
1.3 TreeSet
TreeSet 是Set接口的实现类,底层数据结构是二叉树。
TreeSet 存储的数据按照一定的规则存储。存储规则让数据表现出自然顺序。
1.3.1 TreeSet工作原理
添加一个新元素t的存储的步骤
[1] 如果集合无元素,t直接加入;如果集合有元素,t和根节点比较;
[2] 如果t小于根节点;把t放到根节点的左子树上;重复1-3步骤
[3] t大于根节点;把t放到根节点的右子树上;重复1-3步骤
输出时按照一定的规则:左子树->根节点->右子树
根据TreeSet的工作原理,向TreeSet添加自定义元素?
向TreeSet中添加元素时,一定要提供比较策略,否则会出现ClassCastException。
比较策略分两种:内部比较器和外部比较器
1.3.2 内部比较器
当一个自定义对象实现Comparable并实现compareTo方法时,通过指定具体的比较策略,此时称为内部比较器。
public class Student implements Comparable<Student>{ private String id; private String name; private int age; // 。。。 @Override public String toString() { return "Student [id=" + id + ", name=" + name + ", age=" + age + "]"; } @Override public int compareTo(Student o) { if(this.getAge()<o.getAge()) { return -1; }else if(this.getAge() == o.getAge()) { return 0; }else { return 1; } } }
比较策略的几种情况
[1] 比较策略一般当前对象写在前面,待比较对象也在后面,比较结果默认升序,
如果想要降序,改变两个比较对象的位置即可。
return this.getAge() - o.getAge() ;
1.3.3 外部比较器
当实际开发过程中不知道添加元素的源代码、无权修改别人的代码,此时可以使用外部比较器。
Comparator 位于java.util包中,定义了compare(o1,o2) 用于提供外部比较策略。
TreeSet接受一个指定比较策略的构造方法,这些比较策略的实现类必须实现Comparator
接口。
需求:按照字符串的长度比较
public class Test01 { public static void main(String[] args) { LenComparator lenComparator = new LenComparator(); TreeSet<String> set2 = new TreeSet<String>(lenComparator); set2.add("banana"); set2.add("coco"); set2.add("apple"); set2.add("apple"); System.out.println(set2); } } class LenComparator implements Comparator<String>{ @Override public int compare(String o1, String o2) { return o1.length() - o2.length(); } }
使用匿名内部类优化
public class Test02 { public static void main(String[] args) { TreeSet<String> set2 = new TreeSet<String>(new Comparator<String>() { @Override public int compare(String o1, String o2) { return o1.length() - o2.length(); } }); set2.add("banana"); set2.add("coco"); set2.add("apple"); set2.add("apple"); System.out.println(set2); } }