java.util.Set 接口和 java.util.List 接口一样,同样继承自 Collection 接口,它与 Collection 接口中的方法基本一致,并没有对 Collection 接口进行功能上的扩充,只是比 Collection 接口更加严格了。与 List 接口不同的是,Set 接口中元素无序,并且都会以某种规则保证存入的元素不出现重复。
Set 接口继承了 Collection 接口,因此可以使用 Collection 接口中的所有方法。由于 Set 集合中的元素不能重复,因此在向 Set 集合中添加元素时,需要先判断新增元素是否已经存在于集合中,再确定是否执行添加操作。
HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时就是使用这个实现类。HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取和查找性能。 当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode( ) 方法来得到该对象的 hashCode 值,然后根据该 hashCode 值决定该对象在 HashSet 中的存储位置。如果有两个元素通过 equals( ) 方法比较返回 true,但它们的 hashCode( ) 方法返回值不相等,HashSet 将会把它们存储在不同的位置,依然可以添加成功。也就是说,HashSet 集合判断两个元素相等的标准是两个对象通过 equals( ) 方法比较相等,并且两个对象的 hashCode( ) 方法返回值也相等。
♝ 集合元素值可以是 null。 ♝ 不能保证元素的排列顺序,顺序可能与添加顺序不同。 ♝ HashSet 不是同步的,如果多个线程同时访问一个 HashSet,假设有两个或者两个以上线程同时修改了 HashSet 集合时,则必须通过代码来保证其同步。
set 集合存放 字符串
public class Test {
public static void main(String[] args) {
HashSet<String> set = new HashSet<String>();
set.add("刘备");
set.add("关羽");
set.add("张飞");
set.add("刘备");
System.out.println(set);
}
}
set 存放 对象
// 学生类
public class Stu {
private String name;
private Integer age;
public Stu(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Stu{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
----------------------------------------------------------------------
// 测试类
public class Test {
public static void main(String[] args) {
HashSet<Stu> set = new HashSet<>();
set.add(new Stu("刘备", 1000));
set.add(new Stu("刘备", 1000));
System.out.println(set);
}
}
为什么同的对象都会存到 set 集合,我们在 Stu 类中没有重写 equals、hashcode 方法,用的都是 Object 中的方法,当使用 Object 中的 equals 方法时,比较的是地址值,被认为不是一样的,这将导致 HashSet 会把这两个对象保存在 Hash 表的不同位置,从而使两个对象都可以添加成功。
set 存放 对象,重写 equals、hashcode
// 学生类
public class Stu {
private String name;
private Integer age;
public Stu(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Stu stu = (Stu) o;
return name.equals(stu.name) &&
age.equals(stu.age);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Stu{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
----------------------------------------------------------------------
// 测试类
public class Test {
public static void main(String[] args) {
HashSet<Stu> set = new HashSet<>();
set.add(new Stu("刘备", 1000));
set.add(new Stu("刘备", 1000));
System.out.println(set);
}
}
如果需要把某个类的对象保存到 HashSet 集合中,必须重写这个类的 equals( ) 方法和 hashCode( ) 方法时,如果两个对象通过 equals( ) 方法比较返回 true,这两个对象的 hashCode 值也应该相同。
HashSet 还有一个子类 LinkedHashSet,LinkedHashSet 集合也是根据元素的 hashCode 值来决定元素的存储位置,但它同时使用链表维护元素的次序,这样使得元素看起来是以插入的顺序保存的。也就是说,当遍 历LinkedHashSet 集合里的元素时,LinkedHashSet 将会按元素的添加顺序来访问集合里的元素。 LinkedHashSet 需要维护元素的插入顺序,因此性能略低于 HashSet 的性能,但在迭代访问 Set 里的全部元素时将有很好的性能,因为它以链表来维护内部顺序。 虽然 LinkedHashSet 使用了链表记录集合元素的添加顺序,但 LinkedHashSet 依然是 HashSet,因此它依然不允许集合元素重复(由哈希表保证唯一性,链表保证存取一致)。
TreeSet 是 SortedSet 接口的实现类,TreeSet可以确保集合元素处于排序状态。
方法名 | 说明 |
---|---|
Comparator comparator( ) | 如果 TreeSet 采用了定制排序,则该方法返回定制排序所使用的 Comparator;如果 TreeSet 采用了自然排序,则返回 null |
Object first( ) | 返回集合中的第一个元素 |
Object last( ) | 返回集合中的最后一个元素 |
Object lower(Object e) | 返回集合中位于指定元素之前的元素 |
Object higher(Object e) | 返回集合中位于指定元素之后的元素 |
如果把一个对象添加到 TreeSet 时,则该对象的类必须实现 Comparable 接口,否则程序将会抛出异常。
在 JDK1.5 之后,如果我们定义一个方法需要接受多个参数,并且多个参数类型一致,我们可以对其简化成如下格式:修饰符 返回值类型 方法名(参数类型... 形参名){ }
底层使用数组实现,可以当作数组使用,完全等价与修饰符 返回值类型 方法名(参数类型[] 形参名){ }
只是后面这种定义,在调用时必须传递数组,而前者可以直接传递数据即可。
示例
public class ChangeArgs {
public static void main(String[] args) {
int[] arr = { 1, 4, 62, 431, 2 };
// 传递数组
System.out.println("传递数组: " + getSum(arr));
// 传递数据
System.out.println("传递数据: " + getSum( 1, 4, 62, 431, 2 ));
}
public static int getSum(int... arr) {
int sum = 0;
for (int a : arr) {
sum += a;
}
return sum;
}
}
注意 ♜ 一个参数列表只能由一个可变参数 ♜ 如果由多个参数,可变参数位于最后