set接口:
set接口和list接口一样,都是继承于Collection接口,它与Collection接口中的方法基本一致。特点:不允许存储重复元素,元素没有索引。它主要有两个实现类:HashSet(具有哈希表结构,实际是一个HashMap实例,它的没有顺序,但是查询速度非常快,底层也不是同步的 )和LinkedHashSet
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
// set接口:继承于Collection接口,不允许存储重复元素,且没有索引,因此不能使用带索引的方法,也不能使用普通的for遍历
// set接口主要有两个实现类:HashSet和
// 1.HashSet特点:1.不允许存储重复元素 2.没有索引,不能使用带索引的方法,也不能使用普通的for遍历 3.无序的集合,存取元素的顺序可能不同 4.底层是一个哈希表结构(查询速度非常快)
public class setInterfaceClass {
public static void main(String[] args) {
// 使用多态的方式创建一个HashSet集合:
Set<Integer> st = new HashSet<Integer>();
// 使用add方法向集合中添加元素:
st.add(1);
st.add(2);
st.add(2);
st.add(4);
System.out.println(st); // [1, 2, 3, 4]
// 遍历st集合:不能使用for遍历没有索引的集合:一般使用迭代器或增强for遍历没有索引的集合:
// for (int i = 0; i < st.size();i++) {
// System.out.println(st[i]);
//}
Iterator<Integer> it = st.iterator();
while (it.hasNext()) {
int num = it.next();
System.out.println(num); // 分别打印出了每个元素,但是2只打印了一次,因为存元素时不会存入重复的元素
}
for (int i : st) {
System.out.println(i); // 分别打印出了每个元素,但是2只打印了一次,因为存元素时不会存入重复的元素
};
}
}
哈希表:
HashSet集合存储数据的结构就是使用哈希表,那么什么是哈希表?想要了解哈希表,那么就先了解哈希值。
哈希值 :一个十进制的整数,由系统随机给出(实际就是对象的地址值,但是并非物理地址,而是模拟出来的地址,在Object类中有一个hashCode方法可以获取对象的哈希值),
定义一个继承了Object的类:
// 定义一个TestClass类并继承Object
public class TestClass extends Object{
// 点进Object查找hashCode方法可以看到源码:public native int hashCode(); 此方法没有方法体,而是有一个native,表示该方法调用了本地系统方法
}
使用TestClass类创建对象,并使用hashCode方法:
// 哈希值:一个由系统随机给出的对象整数地址值,hashCod方法可以返回这个哈希值:
public class hashCodeMethodsClass {
public static void main(String[] args) {
// 创建一个TestClass类的对象,又因为TestClass类继承了Object,因此可以调用hashCode方法:
TestClass tc = new TestClass();
int hashValue = tc.hashCode();
System.out.println(hashValue); // 1967205423 ,每次执行都会得到不同的值
TestClass tc2 = new TestClass();
int hashValue2 = tc2.hashCode();
System.out.println(hashValue2); // 42121758 ,每次执行都会得到不同的值
System.out.println(tc == tc2); // false
// 如何我们重写hashCode方法,那么返回值就一直是一个定值,虽然返回值的哈希值是相同的,但是这两个对象也是不相等的,因为物理地址不同
// String类的哈希值:
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1.hashCode()); // 96354
System.out.println(s2.hashCode()); // 96354
System.out.println(s1 == s2); // false
// 字符串不相同,哈希值也是有可能相同的,如:
System.out.println("重地".hashCode()); // 1179395
System.out.println("通话".hashCode()); // 1179395
}
}
哈希表结构:
jdk1.8之前哈希表 = 数组+链表,jdk1.8之后哈希表= 数组+ 红黑树(提高查询速度)
set集合不能存储重复元素的原理 :
import java.util.HashSet;
// set集合不允许存储重复元素的原理:
public class setNoRepet {
public static void main(String[] args) {
HashSet<String> st = new HashSet<String>();
String s1 = new String("abc");
String s2 = new String("abc");
st.add(s1);
st.add(s2);
st.add("重地");
st.add("通话");
st.add("abc");
System.out.println(st); // [重地, 通话, abc] ,每次添加元素底层都会调用hashCode计算哈希值,并将这个哈希值水平存到数组中,如果有相同的哈希值元素被添加,那么会继续调用equals方法进一步比较两个元素是否相同,如果相同则不会存储,否则会在垂直方向将元素存到对应哈希值下面
}
}
HashSet存储自定义类型元素:
给HashSet中存放自定义元素时,需要重写对象中hashCode和equals方法,建立自己的比较方法,才能保证HashSet集合中的对象唯一。
自定义类:
import java.util.Objects;
public class Person {
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public Person() {
}
// 重写equals和hashCode方法代码可以alt+insert自定生成:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
使用HashSet存储自定义元素:
import java.util.HashSet;
// HashSet存储自定义元素:set集合存储元素唯一,以后如果要在HasHset中存储元素,一定要覆重写hashCode和equals方法:
public class customElementSaveHashSet {
public static void main(String[] args) {
Person p1 = new Person("小明",12);
Person p2 = new Person("小明",12);
Person p3 = new Person("小红",22);
HashSet<Person> ps = new HashSet<>();
ps.add(p1);
ps.add(p2);
ps.add(p3);
System.out.println(p1.hashCode()); //1967205423
System.out.println(p2.hashCode()); //42121758
System.out.println(p3.hashCode()); //20671747
System.out.println(ps); // [Person@13b6d03, Person@282ba1e, Person@75412c2f], 存进去了两个对象
// 上面是没有重写hashCode方法和equals方法,会存三个值进去,下面是重写了Person类中的hashCode和equals方法的结果:
System.out.println(p1.hashCode()); //23458766
System.out.println(p2.hashCode()); //23458766
System.out.println(p3.hashCode()); //23653828
System.out.println(ps); // [Person@165f3ce, Person@168edc4]
}
}
LinkedHashSet集合:
LinkedHashSet是一个具有可预知迭代顺序set接口的哈希表和链表的实现,继承于HashSet集合,与hashSet不同之处在于LinkedHashSet维护着一个运行于所有条目的双重链接列表,此链接列表定义了迭代顺序(按照将元素插入到set中的顺序,且插入的顺序不受set中重新插入元素影响)
import java.util.HashSet;
import java.util.LinkedHashSet;
// LinkedHashSet继承于HashSet集合,底层是一个哈希表(数组+链表/红黑树)和链表构成,可以看出是双链,其中一条是记录元素存储顺序的。
public class LinkedHashSetClass {
public static void main(String[] args) {
// 创建一个HashSet集合:
HashSet<String> st = new HashSet<>();
st.add("WWW");
st.add("abc");
st.add("abc");
st.add("hello");
System.out.println(st); // [abc, WWW, hello] ,可以看到遍历的顺序和存储的顺序是不一致且不允许重复存储的。
// 创建一个LinkedHashSet集合:
LinkedHashSet<String> ls = new LinkedHashSet<>();
ls.add("WWW");
ls.add("abc");
ls.add("abc");
ls.add("hello");
System.out.println(ls); // [WWW, abc, hello] ,可以看到与HashSet集合不一致的是:元素存储迭代是有顺序的,且也是不重复
}
}
可变参数:
在jdk1.5之后,如果定义的方法需要接收多个参数,且参数数据类型一致,那么我们可以对其传入参数简化书写:(参数类型…形参名),当方法的参数类型确定而参数个数不确定时可以使用可变参数,可变参数实际是一个数组(传递几个参数就会创建一个存几个元素的数组 )将每个参数存起来了,如:
public class VarParamClass {
public static void main(String[] args) {
getSum(); // 0
getSum(2,4); // 2
}
// 一个方法的可变参数列表只能有一个参数,也就是说可变的参数只能有一个:
public static void getSum(int...num) {
System.out.println(num.length);
};
// 如果有多个参数,其中有一个可变参数,那么这个可变参数放到最后面试可行的:
public static void getResult(String s,int n, Boolean b, int...num) {
System.out.println(num.length);
};
// 当有多个参数时,可变参数不能放到其他位置,只能放到末尾:
// public static void getReut(int...num,String s,int n, Boolean b) {
// System.out.println(num.length);
//};
}
提示:本文图片等素材来源于网络,若有侵权,请发邮件至邮箱:810665436@qq.com联系笔者删除。
笔者:苦海