[学习笔记]Java集合框架之Set集合

Set集合

特点

  • Set集合中的元素是唯一的,不可重复(取决于hashCode和equals方法),也就是说具有唯一性。
  • Set集合中元素不保证存取顺序,并不存在索引。

继承关系

Collection
    |--Set:元素唯一,不保证存取顺序,只可以用迭代器获取元素。
        |--HashSet:哈希表结构,非线程安全,查询速度较快。元素唯一性取决于hashCode和equals方法。
            |--LinkedHashSet:带有双向链表的哈希表结构,非线程安全,保持存取顺序,保持了查询速度较快特点。
        |--TreeSet:平衡排序二叉树(红黑树)结构,非线程安全,按自然排序或比较器存入元素以保证元素有序。
                    元素唯一性取决于ComparaTo方法或Comparator比较器。

常用方法

Set集合的方法和Collection集合的方法几乎一致。

HashSet集合

特点

  • 元素具有唯一性,取决于hashCode和equals方法。
  • 元素不保证存取顺序。
  • 由于采用哈希表数据结构,所以查询较快。
  • 与List一样,建议使用多态的方式,创建HashSet集合并使用Set接口方法。

关于hashCode和equals方法

  • 在以哈希表为数据结构的容器中,其存放地址取决于hashCode方法计算的值,如果hashCode的值相同,则用equals方法判断是否相同,如果都相同则判定为相同元素。
  • 在以哈希表为数据结构的容器使用过程中,已经加入的元素不可以变更hashCode方法所依赖的域的值,否则会导致元素hashCode值已变化,但是其在容器中的位置却没有变化,后果是接下来的remove等操作将因为无法找到该元素而移除失败,进一步导致内存泄露。

元素唯一性示例

Person.java
     
     
     
     
package bean ;
public class Person {
private String name ;
private int age ;
public Person ( 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 ;
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 ;
}
@Override
public String toString () {
return "Person [name=" + name + ", age=" + age + "]" ;
}
}
HashSetDemo.java
      
      
      
      
package Collection ;
import java . util . HashSet ;
import java . util . Iterator ;
import java . util . Set ;
import bean . Person ;
public class HashSetDemo {
public static void main ( String [] args ) {
// Set集合保证元素唯一性。
Set < String > set = new HashSet < String >();
set . add ( "Java1" );
set . add ( "Java2" );
set . add ( "Java1" );
set . add ( "Java1" );
set . add ( "Java2" );
for ( Iterator < String > it = set . iterator (); it . hasNext ();) {
System . out . println ( it . next ());
}
System . out . println ( "-------------------" );
// Set集合存储自定义类时,建议该类重写hashCode和equals方法,这是保证元素唯一性的前提,一般还会重写toString方法。
Set < Person > setObj = new HashSet < Person >();
setObj . add ( new Person ( "Jacob" , 25 ));
setObj . add ( new Person ( "Meteor" , 23 ));
setObj . add ( new Person ( "Tom" , 26 ));
setObj . add ( new Person ( "Llllin" , 27 ));
setObj . add ( new Person ( "Jacob" , 24 ));
for ( Iterator < Person > it = setObj . iterator (); it . hasNext ();) {
System . out . println ( it . next ());
}
}
}

运行结果

Java2
Java1
-------------------
Person [name=Jacob, age=25]
Person [name=Tom, age=26]
Person [name=Jacob, age=24]
Person [name=Meteor, age=23]
Person [name=Llllin, age=27]

不保证元素存取顺序示例

       
       
       
       
package Collection ;
import java . util . HashSet ;
import java . util . Iterator ;
import java . util . Set ;
public class HashSetDemo {
public static void main ( String [] args ) {
// Set集合不保证存取顺序。
Set < String > set = new HashSet < String >();
set . add ( "Java1" );
set . add ( "Java2" );
set . add ( "Java3" );
set . add ( "Java4" );
set . add ( "Java5" );
for ( Iterator < String > it = set . iterator (); it . hasNext ();) {
System . out . println ( it . next ());
}
}
}

运行结果

Java2
Java3
Java4
Java5
Java1

比较器

1. Comparable接口
自定义类如果实现该接口,那么重写该接口唯一的方法comparaTo(E),可以让该类具有可比较性。

2. Comparator接口
实现该接口的类被称之为比较器,一般只具有一个方法,就是重写的这个接口的compara(E o1, E o2)方法,实现两个对象之间的比较。

案例

获取Set集合中元素的最大值(要求元素具有可比性)。
ComparatorByLength.java
         
         
         
         
package comparator ;
import java . util . Comparator ;
public class ComparatorByLength implements Comparator < String > {
@Override
public int compare ( String s1 , String s2 ) {
int result = s1 . length () - s2 . length ();
return result == 0 ? s1 . compareTo ( s2 ) : result ;
}
}

HashSetTest.java
          
          
          
          
package collection . set ;
import java . util . Collections ;
import java . util . HashSet ;
import java . util . Iterator ;
import java . util . Set ;
import comparator . ComparatorByLength ;
public class HashSetTest {
public static void main ( String [] args ) {
/*
* 案例:获取集合最大值
*/
Set < String > set = new HashSet < String >();
set . add ( "Jacob" );
set . add ( "Java" );
set . add ( "Meteor" );
set . add ( "abc" );
set . add ( "it&java" );
set . add ( "zz" );
// 方法一:使用Collections.max()方法(使用的是自然排序方法)。
System . out . println ( Collections . max ( set ));
// 方法二:使用自然排序
System . out . println ( getMax ( set ));
// 方法三:使用比较器(ComparatorByLength)
System . out . println ( getMax ( set , new ComparatorByLength ()));
}
/**
* 使用比较器获取Set集合中的最大值(使用字符串长度比较器)
*
* @param set
* @param comparatorByLength
* @return
*/
public static Object getMax ( Set < String > set , ComparatorByLength comparatorByLength ) {
if ( comparatorByLength == null ) {
throw new NullPointerException ();
}
Iterator < String > it = set . iterator ();
String max = it . next ();
while ( it . hasNext ()) {
String obj = it . next ();
if ( comparatorByLength . compare ( obj , max ) > 0 ) {
max = obj ;
}
}
return max ;
}
/**
* 使用自然排序获取Set集合中的最大值。
*
* @param set
* @return
*/
public static Object getMax ( Set < String > set ) {
Iterator < String > it = set . iterator ();
String max = it . next ();
while ( it . hasNext ()) {
String com = it . next ();
if ( com . compareTo ( max ) > 0 ) {
max = com ;
}
}
return max ;
}
}

运行结果

zz
zz
it&java

LinkedHashSet集合

特点

  • HashSet集合具有的优点LinkedHashSet集合都具有。
  • LinkedHashSet集合在HashSet查询速度快的前提下,能够保持元素存取顺序。

保持元素存取示例

           
           
           
           
package collection;
 
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
 
public class Set_LinkedHashSet {
 
public static void main(String[] args) {
/*
* LinkedHashSet可以保持元素存取顺序
*/
Set<String> set1 = new HashSet<String>();
Set<String> set2 = new LinkedHashSet<String>();
init(set1 );
init(set2 );
 
System.out.println( "HashSet:");
for (Iterator<String> it = set1.iterator(); it.hasNext();) {
System.out.println( it.next());
}
System.out.println( "LinkedHashSet:");
for (Iterator<String> it = set2.iterator(); it.hasNext();) {
System.out.println( it.next());
}
}
 
public static void init(Set<String> set) {
set.add("Java1");
set.add("Java2");
set.add("Java3");
set.add("Java4");
set.add("Java5");
}
}

运行结果

HashSet:
Java2
Java3
Java4
Java5
Java1
LinkedHashSet:
Java1
Java2
Java3
Java4
Java5

TreeSet集合

特点

  • 元素具有唯一性,取决于Comparable.comparaTo方法或比较器Comparator。
  • 元素不保证存取顺序。
  • 由于采用平衡排序二叉树,所以保存的元素均有序。
  • 与List一样,建议使用多态的方式,创建TreeSet集合并使用Set接口方法。
  • 创建TreeSet集合时,必须保证元素已经实现Comparable接口或者传入比较器Comparator。

示例

使用自然排序和比较器加入TreeSet集合进行排序
Person.java
           
           
           
           
package bean;
 
public class Person implements Comparable<Person> {
 
private String name;
private int age;
 
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
 
public Person() {
super();
}
 
public String getName() {
return name;
}
 
public void setName(String name) {
this.name = name;
}
 
public int getAge() {
return age;
}
 
public void setAge( int age) {
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 ;
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;
}
 
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]" ;
}
 
@Override
public int compareTo(Person o) {
int margin = this.name.compareTo( o. name);
return margin == 0 ? this. age - o. age : margin;
}
}
 
ComparatorByAge.java
package comparator;
 
import java.util.Comparator;
 
import bean.Person;
 
public class ComparatorByAge implements Comparator<Person> {
 
@Override
public int compare(Person o1, Person o2) {
int margin = o1.getAge() - o2.getAge();
return margin == 0 ? o1 .compareTo(o2 ) : margin ;
}
}

TreeSetDemo.java
           
           
           
           
package collection.set;
 
import java.util.Collections;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
 
import bean.Person;
import comparator.ComparatorByAge;
import comparator.ComparatorByLength;
 
public class TreeSetDemo {
 
public static void main(String[] args) {
// 使用自然排序将字符串加入TreeSet集合
System.out.println( "字符串(自然排序):" );
Set<String> set = new TreeSet<String>();
set.add("java");
set.add("linkedhashset");
set.add("awesome");
set.add("zzz");
set.add("treeset");
 
// 元素在添加的时候就已经间接调用compareTo方法进行排序
for (Object object : set) {
System.out.println( object);
}
// 使用比较器将字符串按由长至短加入TreeSet集合
System.out.println( "字符串(比较器):" );
Set<String> setComparatorByLength =
new TreeSet<String>(Collections. reverseOrder(new ComparatorByLength()));
setComparatorByLength.add( "java");
setComparatorByLength.add( "linkedhashset");
setComparatorByLength.add( "awesome");
setComparatorByLength.add( "zzz");
setComparatorByLength.add( "treeset");
 
for (String string : setComparatorByLength) {
System.out.println( string);
}
// 使用自然排序将自定义类加入TreeSet集合
System.out.println( "自定义类(自然排序):" );
Set<Person> setObj = new TreeSet<Person>();
setObj.add(new Person( "jacob", 25));
setObj.add(new Person( "meteor", 23));
setObj.add(new Person( "kitty", 22));
setObj.add(new Person( "tom", 26));
for (Iterator<Person> it = setObj.iterator(); it.hasNext();) {
System.out.println( it.next());
}
 
// 使用比较器将自定义类加入TreeSet集合
System.out.println( "自定义类(比较器):" );
TreeSet<Person> setObjComparatorByAge = new TreeSet<Person>(new ComparatorByAge());
setObjComparatorByAge.add( new Person( "jacob", 25));
setObjComparatorByAge.add( new Person( "meteor", 23));
setObjComparatorByAge.add( new Person( "kitty", 22));
setObjComparatorByAge.add( new Person( "tom", 26));
for (Iterator<Person> it = setObjComparatorByAge.iterator(); it .hasNext();) {
System.out.println( it.next());
}
}
}

运行结果

字符串(自然排序):
awesome
java
linkedhashset
treeset
zzz
字符串(比较器):
linkedhashset
treeset
awesome
java
zzz
自定义类(自然排序):
Person [name=jacob, age=25]
Person [name=kitty, age=22]
Person [name=meteor, age=23]
Person [name=tom, age=26]
自定义类(比较器):
Person [name=kitty, age=22]
Person [name=meteor, age=23]
Person [name=jacob, age=25]
Person [name=tom, age=26]


你可能感兴趣的:(Java)