Java语言基础(Set集合)
Set集合概述及特点
Set集合概述及特点:元素唯一 , 一个不包含重复元素的 collection
HashSet存储字符串并遍历
- HashSet 元素唯一,无序(存取顺序不一致)
- HashSet 底层数据结构是哈希表:是元素为链表的数组,具有链表和数组的特点 像新华字典(JDK1.7)
- 构造一个新的空 set,其底层 HashMap 实例的默认初始容量是 16,加载因子是 0.75。
代码示例:
import java.util.HashSet;
public class SetDemo {
public static void main(String[] args) {
//构建一个set对象
HashSet<String> set = new HashSet<>();
set.add("张三");
set.add("李四");
set.add("李四");
set.add("王梅");
set.add("黎明");
set.add("张三");
for (String s : set) {
System.out.println(s);
}
// HashSet 元素唯一,无序(存取顺序不一致)
}
}
运行结果:
李四
张三
黎明
王梅
HashSet保证元素唯一性
HashSet 底层数据结构是哈希表.,HashSet 不是线程安全的 集合元素可以是 null
哈希表:是一个元素为链表的数组,综合了数组和链表的优点 (像新华字典一样) (JDK1.7之前)
HashSet的唯一性和无序性:
- 当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值。
- 然后根据 hashCode 值决定该对象所代表的元素在数组中的索引值。
- 要放入的元素的值是否和该索引处的链表上的各个元素相等,若不相等就将新的元素接在表尾;若相等就不链接。(保证集合元素的唯一性)
- 数据在存入的时候是根据哈希值来确定元素在数组中的索引值。(无序性)
HashSet 集合判断两个元素相等的标准:
两个对象通过 hashCode() 方法比较相等,并且两个对象的 equals() 方法返回值也相等。
结论:HashSet 保证元素唯一性是靠元素重写hashCode()和equals()方法来保证的,如果不重写则无法保证。
代码示例:
import java.util.HashSet;
public class SetDemo2 {
public static void main(String[] args) {
HashSet<Integer> set = new HashSet<>();
set.add(100);
//长按ctrl,选择HashSet点击打开
// public HashSet() {
// map = new HashMap<>();
//}
//长按ctrl,选择add点击打开
//public boolean add (E e){
// return map.put(e, PRESENT) == null;
//}
//长按ctrl,选择put 点击打开
//public V put (K key, V value){
// return putVal(hash(key), key, value, false, true);
//}
//长按ctrl,选择putVal 点击打开
// final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
// boolean evict) {
// Node<K,V>[] tab; Node<K,V> p; int n, i;
// if ((tab = table) == null || (n = tab.length) == 0)
// n = (tab = resize()).length;
// if ((p = tab[i = (n - 1) & hash]) == null)
// tab[i] = newNode(hash, key, value, null);
// else {
// Node<K,V> e; K k;
// if (p.hash == hash &&
// ((k = p.key) == key || (key != null && key.equals(k))))
// e = p;
// else if (p instanceof TreeNode)
// e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
// else {
// for (int binCount = 0; ; ++binCount) {
// if ((e = p.next) == null) {
// p.next = newNode(hash, key, value, null);
// if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
// treeifyBin(tab, hash);
// break;
// }
// if (e.hash == hash &&
// ((k = e.key) == key || (key != null && key.equals(k))))
// break;
// p = e;
// }
// }
// if (e != null) { // existing mapping for key
// V oldValue = e.value;
// if (!onlyIfAbsent || oldValue == null)
// e.value = value;
// afterNodeAccess(e);
// return oldValue;
// }
// }
// ++modCount;
// if (++size > threshold)
// resize();
// afterNodeInsertion(evict);
// return null;
// }
//static final int hash (Object key){
// int h;
// return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
//}
set.add(200);
set.add(100);
set.add(300);
set.add(90);
set.add(109);
for (Integer integer : set) {
System.out.println(integer);
}
}
}
HashSet存储自定义对象保证元素唯一性
代码示例:
import java.util.HashSet;
public class MyTest {
public static void main(String[] args) {
//HashSet集合能够保证元素的唯一性,是靠元素重写hashCode()方法和equals方法来保证的,
// 如果元素不重写则无法保证
//HashSet 底层用的是HashMap来存的
Student s1 = new Student("王五", 25);
Student s2 = new Student("王五", 25);
Student s3 = new Student("王五", 25);
Student s4 = new Student("王五", 252);
Student s5 = new Student("王五2", 235);
Student s6 = new Student("王五3", 25);
Student s7 = new Student("王五4", 25);
HashSet<Student> hashSet = new HashSet<>();
hashSet.add(s1);
hashSet.add(s2);
hashSet.add(s3);
hashSet.add(s4);
hashSet.add(s5);
hashSet.add(s6);
hashSet.add(s7);
for (Student student : hashSet) {
System.out.println(student.getName() + "==" + student.getAge());
}
}
}
class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
int hash = Objects.hash(name, age);
return hash;
}
}
运行结果:
王五4==25
王五==25
王五3==25
王五==252
王五2==235
HashSet存储自定义对象保证元素唯一性图解
LinkedHashSet的概述
数据结构 有两个 链表和哈希表
链表保证有序 哈希表保证元素唯一
代码示例:
import java.util.LinkedHashSet;
public class MyTest {
public static void main(String[] args) {
// LinkedHashSet 底层数据结构是链表和哈希表,元素有序且唯一 ,链表保证了元素有序,哈希表保证了元素
LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("A");
linkedHashSet.add("B");
linkedHashSet.add("D");
linkedHashSet.add("E");
linkedHashSet.add("C");
linkedHashSet.add("E");
linkedHashSet.add("C");
linkedHashSet.add("E");
linkedHashSet.add("C");
for (String s : linkedHashSet) {
System.out.println(s);
}
}
}
A
B
D
E
C
TreeSet存储Integer类型的元素并遍历
TreeSet集合的特点: 底层数据结构是二叉树,元素唯一,他最大的特点是能够对元素进行排序
排序:
1. 自然排序(空参构造)
2. 使用比较器排序(有参构造)
到底使用的是哪一种的排序取决于,构造方法.
1. 用空参构造,那么就使用的是自然排序
2. 有参构造,可以使用比较器来排序
注意:使用TreeSet集合进行元素的自然排序,那么对元素有要求,要求这个元素
必须实现Comparable接口 否则无法进行自然排序
保证元素的唯一性是靠compareTo方法的返回值来确定如果返回0 表示两个元素相等
代码示例:
import java.util.TreeSet;
public class MyTest {
public static void main(String[] args) {
// TreeSet 底层数据结构是二叉树,元素唯一,他最大的特点是能够对元素进行排序
TreeSet<Integer> treeSet = new TreeSet<>();
treeSet.add(20);
treeSet.add(18);
treeSet.add(23);
treeSet.add(22);
treeSet.add(17);
treeSet.add(24);
treeSet.add(19);
treeSet.add(18);
treeSet.add(24);
for (Integer integer : treeSet) {
System.out.println(integer);
}
}
}
运行结果:
17
18
19
20
22
23
24
代码示例2:
import java.util.TreeSet;
import java.util.Objects;
public class MyTest {
public static void main(String[] args) {
//按照学生的年龄大小来排序
Student s1 = new Student("王五", 21);
Student s11 = new Student("王五", 21);
Student s123 = new Student("王五3333333333", 21);
Student s2 = new Student("王五", 22);
Student s3 = new Student("王五111", 25);
Student s4 = new Student("王五3333", 252);
Student s5 = new Student("王五22222222222", 235);
Student s6 = new Student("王五3", 25);
Student s7 = new Student("王五2222222224", 2665);
Student s8 = new Student("王五5", 288);
Student s9 = new Student("王五22226", 285);
Student s10 = new Student("王五7", 255);
TreeSet<Student> treeSet = new TreeSet<>();
treeSet.add(s1);
treeSet.add(s11);
treeSet.add(s2);
treeSet.add(s3);
treeSet.add(s4);
treeSet.add(s5);
treeSet.add(s6);
treeSet.add(s7);
treeSet.add(s8);
treeSet.add(s9);
treeSet.add(s10);
treeSet.add(s123);
//如果我们使用的是自然排序:那么对元素是有要求的,要求元素必须实现一个Comparable接口重写里面的comPareTo方法,根据此方法的返回值的正负0 来决定元素在二叉树的位置
for (Student student : treeSet) {
System.out.println(student);
}
}
}
public class Student implements Comparable<Student> {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student s) {
//比较逻辑是按照年龄大小来排序
int num = this.age - s.age;
//当年龄相同不能说明他是同一个对象,还得比较姓名
int num2 = num == 0 ? this.name.compareTo(s.name) : num;
//if(num==0){
// num=this.name.compareTo(s.name);
//}
return num2;
}
}
运行结果:
Student{name='王五', age=21}
Student{name='王五3333333333', age=21}
Student{name='王五', age=22}
Student{name='王五111', age=25}
Student{name='王五3', age=25}
Student{name='王五22222222222', age=235}
Student{name='王五3333', age=252}
Student{name='王五7', age=255}
Student{name='王五22226', age=285}
Student{name='王五5', age=288}
Student{name='王五2222222224', age=2665}
代码示例2:
import java.util.Comparator;
import java.util.TreeSet;
public class MyTest3 {
public static void main(String[] args) {
//TreeSet:可以使用比较器排序,你采用有参构造,给他传入一个比较器
//构造方法摘要
//TreeSet()
//构造一个新的空 set,该 set 根据其元素的自然顺序进行排序。
//TreeSet(Comparator < ? super E > comparator)
//构造一个新的空 TreeSet,它根据指定比较器进行排序。
//接口 Comparator<T >
//int compare (T o1, T o2)
//比较用来排序的两个参数。
Comparator comparator = new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
//按照姓名长度来拍
int num = s1.getName().length() - s2.getName().length();
int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
return num3;
}
};
TreeSet<Student> treeSet = new TreeSet<>(comparator);
treeSet.add(new Student("王五", 21));
treeSet.add(new Student("王五1111", 212));
treeSet.add(new Student("王五21111", 241));
treeSet.add(new Student("王五3111", 2771));
treeSet.add(new Student("王五4111", 251));
treeSet.add(new Student("王五5111", 2661));
treeSet.add(new Student("王五611111", 2661));
treeSet.add(new Student("王五7", 21733));
treeSet.add(new Student("王五7", 21733));
treeSet.add(new Student("王五7", 21337));
treeSet.add(new Student("王五7", 217));
treeSet.add(new Student("王五7", 217));
for (Student student : treeSet) {
System.out.println(student);
}
}
}
public class MyComparator implements Comparator<Student> {
@Override
public int compare(Student s1, Student s2) {
//按照年龄来排
int num = s1.getAge() - s2.getAge();
int num2=num==0?s1.getName().compareTo(s2.getName()):num;
return num2;
}
}
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
运行结果:
Student{name='王五', age=21}
Student{name='王五7', age=217}
Student{name='王五7', age=21337}
Student{name='王五7', age=21733}
Student{name='王五1111', age=212}
Student{name='王五3111', age=2771}
Student{name='王五4111', age=251}
Student{name='王五5111', age=2661}
Student{name='王五21111', age=241}
Student{name='王五611111', age=2661}