Set集合
Set集合最大的特点就是不允许保存重复元素。其也是Collection子接口。
Set接口简介
在JDK1.9之前Set集合与Collection集合的定义并无差别,Set继续使用了Collection接口中提供的方法进行操作,但是从JDK1.9之后Set集合也像List集合一样扩充了一些static方法,set定义:
public inteface Set<E> extends Collection<E>
需要注意的是Set集合并不项List集合那样扩充了许多新方法,所以无法使用List集合中提供的get()方法,即无法实现指定索引数据的获取。
从JDK1.9之后,Set集合也提供了像List集合中类似的of()静态方法,下面就使用此方法进行Set集合特点验证。
验证Set集合特征
package per.lyz.demo;
import java.util.Set;
public class Set_Study {
public static void main(String[] args) {
Set<String> all = Set.of("hello",",","world","!","hello","world"); //set集合数据保存,并且设置重复内容
all.forEach(System.out::println);
}
}
运行结果:
Exception in thread “main” java.lang.IllegalArgumentException: duplicate element: hello
当使用of()这个新方法的时候如果发现集合之中存在有重复元素则会直接抛出异常,这与传统的Set集合不保存重复元素的特点相一致,只不过自己抛出了异常而已。
Set集合的常规使用形式一定是依靠子类进行实例化的,所以Set接口中有两个常用子类:“HashSet”、“TreeSet”。
HashSet子类
HashSet是Set接口中使用最多的一个子类,其最大的特点就是保存的数据是无序的。
HashSet继承结构
public class HashSet<E> extends AbstractSet<E> implements Set<E>,Clonable,Serializable
这种继承形式和ArrayList非常相似。
观察HashSet子类
package per.lyz.demo;
import java.util.HashSet;
import java.util.Set;
public class Set_Study {
public static void main(String[] args) {
Set<String> all = new HashSet<String>();
all.add("hello");
all.add("hello"); //重复元素
all.add("World");
all.add("!");
all.forEach(System.out::println);
}
}
可以发现HashSet子类的操作特点:
- 不允许保存重复元素(Set接口定义);
- 保存数据是无序的;
TreeSet子类
Set接口的另外一个子类是TreeSet,与HashSet最大的区别是TreeSet中保存的数据是有序的,TreeSet定义:
public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>,Clonable,Serializable
TreeSet继承结构
在这个子类中依然继承了AbstractSet父抽象类,同时又实现了一个NavigableSet父接口。
使用TreeSet子类
package per.lyz.demo;
import java.util.TreeSet;
import java.util.Set;
public class Set_Study {
public static void main(String[] args) {
Set<String> all = new TreeSet<String>();
all.add("Hello");
all.add("Hello"); //重复元素
all.add("World");
all.add("!");
all.forEach(System.out::println);
}
}
当利用TreeSet保存的数据的时候所有的数据都会按照数据的升序进行自动排序处理。
分析TreeSet子类排序操作
分析之后发现TreeSet子类中保存的数据是允许排序的,但是这个类必须实现Comparable接口,因为只有实现了此接口,才能确认对象的大小关系。
Tips:TreeSet本质上是利用TreeMap子类实现的集合数据的存储,而TreeMap(树)就需要根据Comparable来确定大小关系。
实现自定义类排序
package per.lyz.demo;
import java.util.Set;
import java.util.TreeSet;
public class Set_Study {
public static void main(String[] args) {
Set<Person> all = new TreeSet<Person>();
all.add(new Person("lyz",18));
all.add(new Person("zky",18)); //年龄相同,数据不同
all.add(new Person("ww", 19)); //数据重复
all.add(new Person("ww", 19)); //数据重复
all.add(new Person("ls",78));
all.forEach(System.out::println);
}
}
class Person implements Comparable<Person>{
private String name;
private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
@Override
public int compareTo(Person per) {
if(this.age < per.age) {
return -1;
} else if(this.age > per.age) {
return 1;
} else {
return this.name.compareTo(per.name);
}
}
}
使用自定义类进行比较处理的时候,一定要将该类中所有属性都依次进行大小关系的匹配,否则某个属性相同的时候也将被认为是重复数据,所以TreeSet是利用Comparable来确认重复数据的。
由于TreeSet在操作中需要将所有属性进行比对,这样的开发难度太高了,所以实际开发中应该首选HashSet子类进行存储。
分析重复元素消除
TreeSet是利用了Comparable接口来实现重复元素判断,但是Set集合的整体特征都是不允许保存重复元素,但是HashSet判断重复元素的方式并不是利用Comparable接口完成的,而是使用Object类中提供的方法实现的。
- 对象编码:
public int hashCode();
- 对象比较:
public boolean equals(Object obj);
在进行重复元素进行判断的时候,首先利用hashCode()进行编码的匹配,如果该编码不存在则表示数据不存在,证明没有重复,如果该编码存在,则进一步进行对象比较处理,如果发现重复则此数据不允许保存。
如果使用Eclipse开发工具,则可以帮助开发者自动创建hashCode()与equals()方法简化操作。
实现数据重复处理
package per.lyz.demo;
import java.util.Set;
import java.util.HashSet;
public class Set_Study {
public static void main(String[] args) {
Set<Person> all = new HashSet<Person>();
all.add(new Person("lyz",18));
all.add(new Person("zky",18)); //年龄相同,数据不同
all.add(new Person("ww", 19)); //数据重复
all.add(new Person("ww", 19)); //数据重复
all.add(new Person("ls",78));
all.forEach(System.out::println);
}
}
class Person{
private String name;
private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
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;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
在Java程序中真正的重复元素处理利用的就是hashCode()与equals()方法共同作用完成的,而只有在排序要求的情况下(TreeSet)才利用Comparable接口来实现。