java set集合
文章目录
set集合与Collection基本相同,没有提供任何额外的方法,实际上set就是Collection,只不过是行为略有不同(set不允许有相同元素)
会介绍set的3种实现类HashSet、TreeSet、EnumSet
1.HashSet
HashSet 具有以下特点:
1. 不能保证元素的排列序列,顺序可能与添加的顺序不同
2. 不是同步的,假设有了两个以上的线程,同时修改了HashSet的元素,则必须通过代码来保证同步
3. 集合元素可以是空值
4. HashSet集合判断两个元素相等,是通过对象的equals()和hashCode()两个方法都相等,所以添加到HashSet集合元素对象需要重写equals()和hashCode()两个方法
public class Student
{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Student() {
super();
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
public Student(String name, int age) {
super();
this.name = name;
this.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;
// 对于任何非null的引用值x,x.equals(null)必须返回false
if (obj == null)
return false;
// 通过 getClass 判断比较对象类型是否相等
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
// 引入java8 Objects 如果两者相等,返回true(含两者皆空的情形),否则比较两者值是否相等
return Objects.equals(this.age, other.age) && Objects.equals(this.name, other.name);
}
}
LinkedHashSet是HashSet的一个子类,LinkedHashSet集合也是根据元素的hashCode值来决定元素的存储位置,但它同时使用联表维护元素的次序,这样使得圆水泥看起来是以插入的顺序保存的。性能略低于HashSet的性能。
HashSet students = new HashSet();
Student student = new Student("a", 12);
Student student1 = new Student("a", 12);
Student student2 = new Student("c", 12);
Student student3 = new Student("d", 12);
Student student4 = new Student("e", 12);
Student student5 = new Student("f", 12);
students.add(student);
students.add(student1);
students.add(student2);
students.add(student3);
students.add(student4);
students.add(student5);
students.remove(student3);
students.add(student3);
System.out.println(students);
//[Student [name=a, age=12], Student [name=c, age=12], Student [name=d, age=12], Student [name=e, age=12], Student [name=f, age=12]]
Student student = new Student("a", 12);
Student student1 = new Student("a", 12);
Student student2 = new Student("c", 12);
Student student3 = new Student("d", 12);
Student student4 = new Student("e", 12);
Student student5 = new Student("f", 12);
LinkedHashSet linkedStudents = new LinkedHashSet();
linkedStudents.add(student);
linkedStudents.add(student1);
linkedStudents.add(student2);
linkedStudents.add(student3);
linkedStudents.add(student4);
linkedStudents.add(student5);
linkedStudents.remove(student3);
linkedStudents.add(student3);
System.out.println(linkedStudents);
//[Student [name=a, age=12], Student [name=c, age=12], Student [name=e, age=12], Student [name=f, age=12], Student [name=d, age=12]]
输出LinkedHashSet 集合的元素时,元素的顺序总是与添加的元素一致。
2.TreeSet
TreeSet 具有以下特点:
1.TreeSet不是根据插入的顺序排序的,而是根据元素的实际值大小来排序的
2.TreeSet支持两种排序,一个是自然排序,一个是定制排序
3.如果将一个类对象加入TreeSet需要实现Comparable接口,重写compareTo方法
TreeSet treeSet = new TreeSet();
treeSet.add(-1);
treeSet.add(-9);
treeSet.add(10);
treeSet.add(4);
System.out.println(treeSet.first());//-9
System.out.println(treeSet.last());//10
根据上面程序运行的结果可以知道,TreeSet不是根据插入的顺序排序的,而是根据元素的实际值大小来排序的
2.1 自然排序
TreeSet 会调用集合元素的compareTo(Object obj)方法来计较元素之间的大小关系,然后将结合按升序排列,这种方式就是自然排序
2.1.1 java的一些常用类已经实现了Comparable接口
下面是实现了Comparable接口的常用类:
1.Integer:所有数值类型的包装类
2.Character:按照字符的UNICODE值进行比较
3.Boolean: true对应的包装类实例大于false对应的包装类实例
4.String:按字符串中字符的UNICODE值进行比较
5.Date、Time:后面的时间、日期比前面的时间、日期大
2.1.2 自定义类实现Comparable接口
将一个自定义对象添加到TreeSet时,则该对象的类必须实现Comparable接口,否则程序出现异常。
java提供了一个Comparable接口,该接口里定义了一个compareTo(Object obj)方法,该方法返回一个整数值。例如obj1.compareTo(obj2),如果改方法返回0,则表明两者相等,如果该方法返回一个正整数,则表明obj1大于obj2;如果该方法返回一个负数则表明obj1小于obj2。对于TreeSET集合而言,它判断两个对象是否相等的唯一标准是:两个对象通过compareTo方法比较式返回0——如果返回0,TreeSet则会人为它们相等,否则就认为它们不相等
TreeSet objTreeSet = new TreeSet();
Product a = new Product(3.14);
Product b = new Product(5.67);
Product c = new Product(3.14);
objTreeSet.add(a);
objTreeSet.add(b);
objTreeSet.add(c);
System.out.println(objTreeSet);
//[Product [price=3.14], Product [price=5.67]]
2.2 定制排序
TreeSet 的自然排序是根据集合元素的大小,TreeSet 将它们以升序排列。如果需要实现定制排序,则可以通过Comparator接口帮助。该接口里包含一个int compare(T o1, T o2),该方法用于比较o1和o2,如果该方法返回正整数,则表明o1大于o2;如果该方法返回0,则表明o1等于o2;如果该方法返回负整数,则表明o1小于o2。
如果需要实现定制排序,则需要在创建TreeSet集合对象时,提供一个Comparator对象与该TreeSet集合关联,由该Comparator对象负责集合源的排序逻辑。由于Comparator 是一个函数式接口,因此可以使用Lambda表达式来代替Comparator对象。
public class Food
{
private double price;
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public Food(double price) {
super();
this.price = price;
}
@Override
public String toString() {
return "Food [price=" + price + "]";
}
public Food()
{
super();
// TODO Auto-generated constructor stub
}
}
public class FoodComparator implements Comparator<Food> {
@Override
public int compare(Food o1, Food o2)
{
//从小到大排序
if (o1.getPrice() > o2.getPrice())
{
return 1;
}
else if (o1.getPrice() == o2.getPrice())
{
return 0;
}
else
{
return -1;
}
}
}
FoodComparator foodComparator = new FoodComparator();
TreeSet foodTreeSet = new TreeSet(foodComparator);
Food food1 = new Food(1);
Food food2 = new Food(2);
Food food3 = new Food(3);
foodTreeSet.add(food3);
foodTreeSet.add(food1);
foodTreeSet.add(food2);
TreeSet foodTreeSet2 = new TreeSet(( o1, o2)->{
Food f1 = (Food)o1;
Food f2 = (Food)o2;
return f1.getPrice() > f2.getPrice() ? 1 : f1.getPrice() == f2.getPrice() ? 0 : -1;
}) ;
foodTreeSet2.add(food3);
foodTreeSet2.add(food1);
foodTreeSet2.add(food2);
System.out.println(foodTreeSet);
System.out.println(foodTreeSet2);
上述代码可以看出,由于Comparator 是一个函数式接口,因此可以使用Lambda表达式来代替Comparator对象。代码由此变得简洁许多。
2.3 附加讲解TreeMap
TreeMap map = new TreeMap();
map.put(3, 2);
map.put(1, 2);
map.put(5, 2);
map.put(4, 2);
System.out.println(map);
TreeMap productMap = new TreeMap();//{1=2, 3=2, 4=2, 5=2}
productMap.put(new Product(3), 1);
productMap.put(new Product(2), 5);
productMap.put(new Product(5), 2);
productMap.put(new Product(4), 10);
System.out.println(productMap);//{Product [price=2.0]=5, Product [price=3.0]=1, Product [price=4.0]=10, Product [price=5.0]=2}
TreeMap<Product, Integer> productMap2 = new TreeMap<Product, Integer>((o1,o2)-> {Product p1 = (Product)o1; Product p2 = (Product)o2; return p1.getPrice() > p2.getPrice() ? 1 : p1.getPrice() == p2.getPrice() ? 0 : -1;});
productMap2.put(new Product(3), 1);
productMap2.put(new Product(2), 5);
productMap2.put(new Product(5), 2);
productMap2.put(new Product(4), 10);
System.out.println(productMap2);//{Product [price=2.0]=5, Product [price=3.0]=1, Product [price=4.0]=10, Product [price=5.0]=2}
TreeMap key值部分等同于TreeSet
3.EnumSet
EnumSet 是一个专为枚举类设计的集合类,EnumSet 中的所有元素都必须是指定枚举类型的枚举值。
EnumSet 类没有暴露任何构造器来创建该类实例,程序通过该类的方法来参加EnumSet 类实例。Enum 类提供了如下常用方法来创建EnumSet对象:
实例:
public class EnumSetTest
{
enum Color
{
BLUE,YELLOW,RED,GREEN;
}
public static void main(String[] args)
{
EnumSet enumSet = EnumSet.allOf(Color.class);
System.out.println(enumSet);//[BLUE, YELLOW, RED, GREEN]
EnumSet noneEnumSet = EnumSet.noneOf(Color.class);
System.out.println(noneEnumSet);//[]
EnumSet enumSet1 = EnumSet.of(Color.BLUE,Color.RED);
enumSet1.add(Color.GREEN);
System.out.println(enumSet1);//[BLUE, RED, GREEN]
EnumSet enumSet2 = EnumSet.range(Color.YELLOW, Color.GREEN);
System.out.println(enumSet2);//[YELLOW, RED, GREEN]
EnumSet enumSet3 = EnumSet.complementOf(enumSet2);
System.out.println(enumSet3);//[BLUE]
}
}