Java集合-Set
Set(java.util.Set)接口,Set中存放的一组没有重复数据的集合,换句话说,同样的元素在Set中只能出现一次。Set接口是一个标准的JAVA接口,是Collection的子类,所以Set继承了Collection 的所有特性。可以向Set中添加任何java对象,如果Set不是类型化的,没有使用Java泛型,那么您甚至可以在同一个集合中混合不同类型(类)的对象,实际开发中很少这么做。
Set和List
Set和List非常相似,两个接口都代表着元素的集合,但是也有一些明显的不同。这些差异反映在Set和List接口包含的方法中 ,List中可以存在相同的元素,而Set中不允许有重复的元素。第二个不同是,Set中的元素是没有顺序的,List中的元素是有顺序的,List可以按顺序进行迭代。
Set的例子
下面是简单的Set例子:
package com.jenkov.collections; import java.util.HashSet; public class SetExample { public static void main(String[] args) { Set setA = new HashSet(); String element = “123”; setA.add(element); System.out.println( setA.contains(element) ); } }
上面例子创建一个HashSet,HashSet是Set接口的一个实现,然后往里面添加一个String对象的元素,添加完后检查是否包含此元素。
Set的实现
作为Collection 子类型,Collection 接口中的所有方法在Set接口中也可用。既然Set是个接口,实例化时就要使用具体的实现类。可以选择下面几个Collections API中的实现:
- java.util.EnumSet
- java.util.HashSet
- java.util.LinkedHashSet
- java.util.TreeSet
在迭代集合时,每一个集合实现在元素的顺序以及在集合中插入和访问元素所需的时间(big O表示法)方面的行为都略有不同。HashSet由HashMap支持,它不保证迭代元素时元素的顺序。LinkedHashSet 和HashSet 是不一样的,LinkedHashSet 可以保证元素的顺序与添加元素时的顺序一样,重新插入LinkedHashSet中已存在的元素不会更改此顺序。TreeSet 同样可以保证元素的迭代顺序,但是元素的顺序是排序后的顺序,换句话就是顺序是调用了 Collections.sort() 后的顺序,这个顺序由它们的自然顺序决定(如果它们实现了Comparable的话),或者由一个特定的Comparator 实现来确定。Set在java.util.concurrent中也有一些实现类,这个以后会讲述。
创建Set
下面是创建 Set实例:
package com.jenkov.collections; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; import java.util.TreeSet; public class SetExample { public static void main(String[] args) { Set setA = new HashSet(); Set setB = new LinkedHashSet(); Set setC = new TreeSet(); } }
Set的泛型
默认可以在Set中添加Object,Java5以后新加了泛型,可以限制Set集合中的数据类型,下面是例子:
Set<MyObject> set = new HashSet<MyObject>(); Set中只能添加MyObject实例对象,访问或者迭代时不需要强制类型转换: for(MyObject anObject : set){ //do someting to anObject... }
Set添加元素
可以通过add()方法向Set中添加元素,这个方法继承自Collection.下面是例子:
Set<String> setA = new HashSet<>(); setA.add("element 1"); setA.add("element 2"); setA.add("element 3");
调用了三次add()方法。
迭代Set元素
有两种方法迭代Set:
- 使用Iterator迭代 Set
- 使用 for-each 循环
这两种方法下面都会讲述,迭代元素的顺序取决于使用的Set实现类。
使用Iterator迭代Set
使用Iterator迭代Set,首先需要从Set中获取Iterator,下面是例子:
Set<String> setA = new HashSet<>(); setA.add("element 1"); setA.add("element 2"); setA.add("element 3"); Iterator<String> iterator = set.iterator(); while(iterator.hasNext(){ String element = iterator.next(); }
使用For-Each循环迭代Set
第二种方式是使用for-each循环迭代Set,下面是代码:
Set set = new HashSet(); for(Object object : set) { String element = (String) object; }
Set接口实现了Iterable接口,所以Set可以使用for-each循环迭代,如果Set使用了泛型,那么迭代循环内部可以使用泛型的类型 :
Set<String> set = new HashSet<>(); for(String str : set) { System.out.println(str); }
使用Stream API迭代Set
第三种方式是使用 Stream API迭代Set ,迭代时必须从Set中获取Stream,下面是代码:
Set<String> set = new HashSet<>(); set.add("one"); set.add("two"); set.add("three"); Stream<String> stream = set.stream(); stream.forEach((element) -> { System.out.println(element); });
从Set中移除元素
可以通过Set的remove(Object o)方法,从Set中移除一个元素,下面是例子:
set.remove("object-to-remove");
没有办法更加Set的索引删除元素,因为元素的顺序取决于Set的具体实现。
从Set中移除所有元素
可以调用Set的clear()方法,移除Set中所有的元素:
set.clear();
将另外一个集合的所有元素加到Set中
List接口中一个addAll()方法,可以增加 另一个 Collection (List或者Set)中的所有元素到 Set中,Set中也有对应的方法:
Set<String> set = new HashSet<>(); set.add("one"); set.add("two"); set.add("three"); Set<String> set2 = new HashSet<>(); set2.add("four"); set2.addAll(set);
代码执行后,set2中包含的元素是one,two , three 和four 。
移除另外一个集合在本集合中存在的所有元素
Set接口中存在removeAll()方法,就移除本集合和给定的Collection中同时存在的元素,在集合论中,这被称为集合与其他集合之间的区别,下面是代码:
Set<String> set = new HashSet<>(); set.add("one"); set.add("two"); set.add("three"); Set set2 = new HashSet(); set2.add("three"); set.removeAll(set2);
执行完代码后,set中包含的元素是one 和 two。元素three被移除,因为在set和set2中都存在。
保留两个集合中同时存在的元素
Set接口同样也有保留两个集合中同时存在的元素,也就是两个集合的交集 :
Set<String> set = new HashSet<>(); set.add("one"); set.add("two"); set.add("three"); Set<String> set2 = new HashSet<>(); set2.add("three"); set2.add("four"); set.retainAll(set2);
执行完代码后,set中只包含了three元素,因为只有这个元素同时存在set和set2中。
Set大小
可以通过Set的size()方法查看Set的大小:
Set<String> set = new HashSet<>(); set.add("123"); set.add("456"); set.add("789"); int size = set.size();
执行后,size的值是3,因为Set中已经加入了3个元素。
检查Set是否是空
可以调用Set的isEmpty()方法,检查Set是否是空:
Set<String> set = new HashSet<>(); boolean isEmpty = set.isEmpty();
执行代码后isEmpty 的值是空,因为Set中没有元素。也可以通过Set的size()是否是0来判断Set是否是空:
Set<String> set = new HashSet<>(); boolean isEmpty = (set.size() == 0);
执行代码后,isEmpty的值是true,因为Set中没有元素,所以size()的值是0.
Set是否包含某个指定元素
可以通过contains()方法检查Set中是否包含给定的元素,下面是例子:
Set<String> set = new HashSet<>(); set.add("123"); set.add("456"); boolean contains123 = set.contains("123");
执行后contains123的值是true,因为Set中包含字符串123。
为了确定Set中是否包含每个元素,Set内部用的equals()方法比较。但是Set中可以添加null值,所以也可以检查Set中是否包含null:
set.add(null); containsElement = set.contains(null); System.out.println(containsElement);
显然,contains()的参数是null, contains()方法比较不使用的==而不是equals()。
将Set转成List
可以通过 List 的 addAll()方法,将Set转成List
Set<String> set = new HashSet<>(); set.add("123"); set.add("456"); List<String> list = new ArrayList<>(); list.addAll(set);
执行后,List中包含 123 和 456。