Set接口
HashSet集合介绍
import java.util.HashSet;
import java.util.Iterator;
public class Demo03 {
public static void main(String[] args) {
HashSet<String> s = new HashSet<>();
s.add("晴天");
s.add("稻香");
s.add("稻香");
s.add("七里香");
System.out.println(s.size());
//遍历1
for (String str:s) {
System.out.println(str);
}
//遍历2
Iterator<String> iterator = s.iterator();
while (iterator.hasNext()) {
String next = iterator.next();
System.out.println(next);
}
}
}
- 输出结果如下,说明集合中不能存储重复元素:
HashSet集合存储数据的结构(哈希表)
-
什么是哈希表呢?
-
在JDK1.8之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的数据都存储在一个链表里。 但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,哈 希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。
-
简单的来说,哈希表是由数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的,
-
为了方便大家的理解我们结合一个存储流程图来说明一下:
- 总而言之,JDK1.8引入红黑树大程度优化了HashMap的性能,那么对于我们来讲保证HashSet集合元素的唯一, 其实就是根据对象的hashCode和equals方法来决定的。如果我们往集合中存放自定义的对象,那么保证其唯一, 就必须复写hashCode和equals方法建立属于当前对象的比较方式。
HashSet存储自定义类型元素
-
给HashSet中存放自定义类型元素时,需要重写对象中的hashCode和equals方法,建立自己的比较方式,才能保证HashSet集合中的对象唯一。
-
定义Dog类
import java.util.Objects;
public class Dog {
private String brand;
private int age;
public Dog() {
}
public Dog(String brand, int age) {
this.brand = brand;
this.age = age;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Dog dog = (Dog) o;
return age == dog.age &&
Objects.equals(brand, dog.brand);
}
@Override
public int hashCode() {
return Objects.hash(brand, age);
}
}
- 定义测试类:
import java.util.HashSet;
public class Test {
public static void main(String[] args) {
HashSet<Dog> dogs = new HashSet<>();
Dog d1 = new Dog("哈士奇",5);
Dog d2 = new Dog("哈士奇",5);
dogs.add(d1);
dogs.add(d1);
dogs.add(d2);
System.out.println(dogs.size());
}
}
LinkedHashSet
- 我们知道HashSet保证元素唯一,可是元素放进去是没有顺序的,那么我们要保证有序,怎么办呢?在HashSet下面有一个子类
java.util.LinkedHashSet
,它是链表和哈希表组合的一个数据存储结构。演示代码如下:
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
public class Demo04 {
public static void main(String[] args) {
Set sets = new LinkedHashSet<>();
sets.add("晴天");
sets.add("晴天");
sets.add(7.8);
sets.add(true);
sets.add(true);
sets.add(7);
sets.add(7.8);
System.out.println(sets.size());
System.out.println(sets);
Set sets1 = new HashSet<>();
sets1.add("晴天");
sets1.add("晴天");
sets1.add(7.8);
sets1.add(true);
sets1.add(true);
sets1.add(7);
sets1.add(7.8);
System.out.println(sets1.size());
System.out.println(sets1);
}
}
可变参数
-
在JDK1.5之后,如果我们定义一个方法需要接收多个参数,并且多个参数类型一致,我们可以对其简化成如下格式:
修饰符 返回值类型 方法名(参数类型...形参名){}
- 其实这个书写完全等价与
修饰符 返回值类型 方法名(参数类型[] 形参名){}
-
只是第二种定义在调用时必须传递数组,而前者可以直接传递数据即可。
-
JDK1.5以后,出现了简化操作。…用在参数之上,称之为可变参数。
-
同样是代表数组,但是在调用这个带有可变参数的方法时,不用创建数组(这就是简单之处),直接将数组中的元素作为实际参数进行传递,其实编译成的class文件,将这些元素先封装到一个数组中,再进行传递。这些动作都在编译.class文件时,自动完成了。
-
代码演示:
public class Demo05 {
public static void main(String[] args) {
int[] s ={10,20,30,40,50};
int num = num(s);
System.out.println(num);
}
public static int num(int...num) {
int s = 0;
for (int arr:num) {
s+=arr;
}
return s;
}
}