java.util.Set
接口和 java.util.List
接口一样,同样继承自 Collection 接口,它与 Collection 接口中的方法基本一致,并没有对 Collection 接口进行功能上的扩充,只是比 Collection 接口更加严格了。与List接口不同的是, Set 接口中元素无序,并且都会以某种规则保证存入的元素不出现重复。
1.继承于Collection接口,具有增删查改的方法!
2.AbstractCollection抽象类,实现了Collection接口,并实现了里面的一些方法,如isEmpty、contains等。
3.Set的两个实现类,HashSet
和TreeSet
(Set集合遍历元素的方式可以采用:迭代器,foreach)
java.util.HashSet
是 Set 接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的(即存取顺序 不一致)。 java.util.HashSet
底层的实现其实是一个 java.util.HashMap
支持.
HashSet
是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。保证元素唯一性 的方式依赖于: hashCode
与 equals
方法。
HashSet的继承关系
类 HashSet<E>
java.lang.Object
继承者 java.util.AbstractCollection<E>
继承者 java.util.AbstractSet<E>
继承者 java.util.HashSet<E>
类型参数:
E - 此 set 所维护的元素的类型
所有已实现的接口:
Serializable, Cloneable, Iterable<E>, Collection<E>, Set<E>
构造方法:
HashSet()
构造一个新的空 set,其底层 HashMap 实例的默认初始容量是 16,加载因子是 0.75。
HashSet(Collection<? extends E> c)
构造一个包含指定 collection 中的元素的新 set。
HashSet(int initialCapacity)
构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和默认的加载因子(0.75)。
HashSet(int initialCapacity, float loadFactor)
构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和指定的加载因子。
常用方法:
boolean add(E e)
如果此 set 中尚未包含指定元素,则添加指定元素。
void clear()
从此 set 中移除所有元素。
Object clone()
返回此 HashSet 实例的浅表副本:并没有复制这些元素本身。
boolean contains(Object o)
如果此 set 包含指定元素,则返回 true。
boolean isEmpty()
如果此 set 不包含任何元素,则返回 true。
Iterator<E> iterator()
返回对此 set 中元素进行迭代的迭代器。
boolean remove(Object o)
如果指定元素存在于此 set 中,则将其移除。
int size()
返回此 set 中的元素的数量(set 的容量)。
关于数据结构哈希表详解: 哈希表详解
这里简单介绍一下:
在JDK1.8之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。 但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,哈希表存储采用数组+链表+红黑树实现,当链表长度**超过阈值(8)**时,将链表转换为红黑树,这样大大减少了查找 时间。 简单的来说,哈希表是由数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的,如下图所示。
HashSet存储原理:
JDK1.8引入红黑树大程度优化了HashMap的性能,那么对于我们来讲保证HashSet集合元素的唯一, 其实就是根据对象的hashCode和equals方法来决定的。如果我们往集合中存放自定义的对象,那么 保证其唯一, 就必须复写 hashCode
和equals
方法建立属于当前对象的比较方式
import java.util.HashSet;
class test{
public static void main(String []args) {
HashSet<Integer> hs = new HashSet<>();
hs.add(17);
hs.add(3);
hs.add(78);
hs.add(24);
hs.add(13);
System.out.println(hs.add(4));
System.out.println(hs.add(4)); //检验HashSet是否允许重复元素加入
System.out.println(hs); //检验HashSet是否有序
}
}
true
false
[17, 3, 4, 24, 13, 78]
第二个输出false说明不允许元素重复,[17, 3, 4, 24, 13, 78]说明存储的元素无序
给HashSet中存放自定义类型元素时,需要重写对象中的hashCode和equals方法,建立自己的比较方式,才能保 证HashSet集合中的对象唯一
import java.util.HashSet;
import java.util.Iterator;
class Student{
private String name;
private int id;
public Student(){
}
public Student(String name, int id)
{
super();
this.name = name;
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
@Override
public String toString()
{
return "Student [name=" + name + ", id=" + id + "]";
}
@Override
//重写hashCode方法
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + id;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
//重写equals方法
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 (id != other.id)
return false;
if (name == null)
{
if (other.name != null)
return false;
}
else if (!name.equals(other.name))
return false;
return true;
}
}
public class test{
public static void main(String []args) {
HashSet<Student> hs = new HashSet<>();
hs.add(new Student("张三",1));
hs.add(new Student("李四",2));
hs.add(new Student("王五",3));
hs.add(new Student("张三",1));//因为重写了equals方法和hashCode方法所以实际上集合中只有一个张三1
System.out.println(hs.add(new Student("谢六",4)));
System.out.println(hs.add(new Student("谢六",4)));
//第一种打印方式,利用重写的toString()方法和Println()直接打印
System.out.println(hs);
System.out.println("------------");
//第二种打印方式,foreach增强for循环
for(Student s : hs) {
System.out.println(s);
}
System.out.println("------------");
//第三种打印方式,利用Iteator迭代器
Iterator<Student> it = hs.iterator();
while(it.hasNext()) {
Student s = it.next();
System.out.println(s);
}
}
}
true
false
[Student [name=李四, id=2], Student [name=张三, id=1], Student [name=王五, id=3], Student [name=谢六, id=4]]
------------
Student [name=李四, id=2]
Student [name=张三, id=1]
Student [name=王五, id=3]
Student [name=谢六, id=4]
------------
Student [name=李四, id=2]
Student [name=张三, id=1]
Student [name=王五, id=3]
Student [name=谢六, id=4]
我们知道HashSet保证元素唯一,可是元素存放进去是没有顺序的,那么我们要保证有序,怎么办呢? 在HashSet下面有一个子类 java.util.LinkedHashSet
,它是链表和哈希表组合的一个数据存储结构。
特点:存取有序,唯一,效率高
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
public class test {
public static void main(String[] args)
{
Set<String> set = new LinkedHashSet<String>();
set.add("bbb");
set.add("aaa");
set.add("Love");
set.add("abc");
set.add("bbc");
Iterator<String> it = set.iterator();
while (it.hasNext())
{
System.out.println(it.next());
}
}
}
bbb
aaa
Love
abc
bbc
基于 TreeMap 的 NavigableSet 实现。使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator进行排序,具体取决于使用的构造方法。
在存储了大量的需要进行快速检索的排序信息的情况下,TreeSet是一个不错的选择
TreeSet继承关系
类 TreeSet<E>
java.lang.Object
继承者 java.util.AbstractCollection<E>
继承者 java.util.AbstractSet<E>
继承者 java.util.TreeSet<E>
类型参数:
E - 此 set 维护的元素的类型
所有已实现的接口:
Serializable, Cloneable, Iterable<E>, Collection<E>, NavigableSet<E>, Set<E>, SortedSet<E>
继承于AbstractSet,AbstractSet实现了equals和hashcode方法。
实现了NavigableSet接口,意味着它支持一系列的导航方法。比如查找与指定目标最匹配项。
实现了Cloneable接口,意味着它能被克隆。
实现了java.io.Serializable接口,意味着它支持序列化。
构造方法:
TreeSet()
构造一个新的空 set,该 set 根据其元素的自然顺序进行排序。
TreeSet(Collection<? extends E> c)
构造一个包含指定 collection 元素的新 TreeSet,它按照其元素的自然顺序进行排序。
TreeSet(Comparator<? super E> comparator)
构造一个新的空 TreeSet,它根据指定比较器进行排序。
TreeSet(SortedSet<E> s)
构造一个与指定有序 set 具有相同映射关系和相同排序的新 TreeSet
常用方法:
boolean add(E e)
将指定的元素添加到此 set(如果该元素尚未存在于 set 中)。
boolean addAll(Collection<? extends E> c)
将指定 collection 中的所有元素添加到此 set 中。
E ceiling(E e)
返回此 set 中大于等于给定元素的最小元素;如果不存在这样的元素,则返回 null。
void clear()
移除此 set 中的所有元素。
Object clone()
返回 TreeSet 实例的浅表副本。
Comparator<? super E> comparator()
返回对此 set 中的元素进行排序的比较器;如果此 set 使用其元素的自然顺序,则返回 null。
boolean contains(Object o)
如果此 set 包含指定的元素,则返回 true。
Iterator<E> descendingIterator()
返回在此 set 元素上按降序进行迭代的迭代器。
NavigableSet<E> descendingSet()
返回此 set 中所包含元素的逆序视图。
E first()
返回此 set 中当前第一个(最低)元素。
E floor(E e)
返回此 set 中小于等于给定元素的最大元素;如果不存在这样的元素,则返回 null。
SortedSet<E> headSet(E toElement)
返回此 set 的部分视图,其元素严格小于 toElement。
NavigableSet<E> headSet(E toElement, boolean inclusive)
返回此 set 的部分视图,其元素小于(或等于,如果 inclusive 为 true)toElement。
E higher(E e)
返回此 set 中严格大于给定元素的最小元素;如果不存在这样的元素,则返回 null。
boolean isEmpty()
如果此 set 不包含任何元素,则返回 true。
Iterator<E> iterator()
返回在此 set 中的元素上按升序进行迭代的迭代器。
E last()
返回此 set 中当前最后一个(最高)元素。
E lower(E e)
返回此 set 中严格小于给定元素的最大元素;如果不存在这样的元素,则返回 null。
E pollFirst()
获取并移除第一个(最低)元素;如果此 set 为空,则返回 null。
E pollLast()
获取并移除最后一个(最高)元素;如果此 set 为空,则返回 null。
boolean remove(Object o)
将指定的元素从 set 中移除(如果该元素存在于此 set 中)。
int size()
返回 set 中的元素数(set 的容量)。
NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive)
返回此 set 的部分视图,其元素范围从 fromElement 到 toElement。
SortedSet<E> subSet(E fromElement, E toElement)
返回此 set 的部分视图,其元素从 fromElement(包括)到 toElement(不包括)。
SortedSet<E> tailSet(E fromElement)
返回此 set 的部分视图,其元素大于等于 fromElement。
NavigableSet<E> tailSet(E fromElement, boolean inclusive)
返回此 set 的部分视图,其元素大于(或等于,如果 inclusive 为 true)fromElement。
import java.util.Set;
import java.util.TreeSet;
class Test{
public static void main(String []args) {
Set<Integer> st = new TreeSet<>();
st.add(1);
st.add(4);
st.add(5);
st.add(2);
System.out.println(st.add(2)); //验证是否可以添加重复元素
System.out.println(st);
}
}
false
[1, 2, 4, 5]
import java.util.Set;
import java.util.TreeSet;
class test{
public static void main(String []args) {
Set<String> st = new TreeSet<>();
st.add("张三");
st.add("李四");
st.add("王五");
st.add("谢六");
st.add("d");
st.add("a");
st.add("c");
st.add("b");
System.out.println(st.add("王五"));
System.out.println(st);
}
}
false
[a, b, c, d, 张三, 李四, 王五, 谢六]
Java在String和Integer类里重写了comparaTo方法,因此TreeSet可以对其进行升序排列。当我们对自定义的TreeSet进行排序时,就需要自己重写比较方法。如果说没有重写任何比较器(内部或者外部)时,使用TreeSet进行操作会报错。
TreeSet与内部比较器
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
class Student implements Comparable{
String name;
int age;
public Student(){
}
public Student(String name, int age)
{
super();
this.name = name;
this.age = age;
}
@Override
//内部重写比较器
public int compareTo(Object o)
{
Student stu =((Student)o);
return this.age-stu.age; //按照年龄升序排序
}
@Override
public String toString()
{
return "Student [name=" + name + ", age=" + age + "]";
}
}
class test{
public static void main(String []args) {
Set<Student> st = new TreeSet<>();
st.add(new Student("张三",18));
st.add(new Student("李四",19));
st.add(new Student("王五",20));
st.add(new Student("谢六",10));
Iterator<Student> it = st.iterator();
while (it.hasNext())
{
System.out.println(it.next());
}
}
}
Student [name=谢六, age=10]
Student [name=张三, age=18]
Student [name=李四, age=19]
Student [name=王五, age=20]
TreeSet与外部比较器
import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
class Student{
String name;
int age;
public Student(){
}
public Student(String name, int age)
{
super();
this.name = name;
this.age = age;
}
@Override
public String toString()
{
return "Student [name=" + name + ", age=" + age + "]";
}
}
//外部比较器
class OutsideCompare implements Comparator{
@Override
public int compare(Object o1, Object o2)
{
Student st1 = ((Student) o1);
Student st2 = ((Student) o2);
return st1.age-st2.age;
}
}
public class test{
public static void main(String []args)
{
OutsideCompare com = new OutsideCompare();
Set<Student> st = new TreeSet<>(com);//根据指定的比较器定义的构造方法
st.add(new Student("张三",18));
st.add(new Student("李四",19));
st.add(new Student("王五",20));
st.add(new Student("谢六",10));
Iterator<Student> it = st.iterator();
while (it.hasNext())
{
System.out.println(it.next());
}
}
}
欢迎持续关注!
个人博客站:jQueryZk Blog