JAVA集合的概念总结-单列集合
- 集合我们首先分为单列集合和键值对集合(双列集合)
- 单列集合
- ArrayList集合 初始容量为10 底层是可变数组,特点是查询快,增删慢。查询快的原因就是应为底层是数组,具有 索引!
- LinkedList集合是线程不安全的,但是效率高,底层是链表,特点是增删快,查询慢,原因是应为没有索引,所以查询慢!
- set集合
- 只是个人总结,如有不全,或者错误,希望大家指出!谢谢
集合我们首先分为单列集合和键值对集合(双列集合)
单列集合
单列集合(Collection)又两个子类List和Set,我们首先说下List集合
1.List集合的特点
- 有顺序
- 有顺序:存取顺序一致
- 元素可重复
2.List集合遍历方式
- 普通for
- 增强for
- 迭代器
3.List集合常用实现类
- ArrayList
- LinkedList
- Vector
3.List集合常用方法
- List接口的常用方法
* boolean add(E e)
* E remove(int index)
* boolean remove(Object o)
* E set(int index, E element)
* E get(int index)
* int size()
ArrayList集合 初始容量为10 底层是可变数组,特点是查询快,增删慢。查询快的原因就是应为底层是数组,具有 索引!
成员方法
1.1: 添加元素 :
public boolean add(E e); 添加元素,添加到集合的最后 .
Public void add( int index , E element ); 在指定的索引处添加一个元素
1.2 获取元素 :
public E get ( int index ); 返回指定索引处的元素(E是一种返回值类型)
集合长度 : public int size () ; 返回集合中的元素的个数
1.3 删除元素 :
public E remove (int index); 删除指定处的元素 ,返回删除是否成功
public E remove (int index , E element ) 修改指定索引处的元素, 返回被被删除的元素
1.4 修改元素 :
public E set ( int index , E element ) ; 修改指定索引处的元素, 返回被修改的元素
集合中的泛型只能是引用类型。集合中要存储基本数据类型的时候 泛型要使用其包装类
LinkedList集合是线程不安全的,但是效率高,底层是链表,特点是增删快,查询慢,原因是应为没有索引,所以查询慢!
LinkedList提供了方便首尾操作的方法
void addFirst(E e):将指定元素插入此列表的开头
void addLast(E e):将指定元素插入此列表的结尾
E getFirst():返回此列表的第一个元素
E getLast():返回此列表的最后一个元素
E removeFirst():移除并返回此列表的第一个元素
E removeLast():移除并返回此列表的最后一个元素
set集合
1.set接口特点
Set接口的特点:
*无序:存取顺序不一致,但不随机(LinkedHashSet是有序的)
*元素不可重复
*没有索引
*包含方法:
boolean add(E e)
void clear()
boolean remove(Object o)
没有获取单个元素和修改单个元素的方法
2.Set集合的遍历方式
*增强for
*迭代器
3.Set集合常用子类
*HashSet
*LinkedHashSet
*继承于HashSet,但是能包装存取顺序一致
4.HashSet的特点
*无序,无索引,元素不可重复
*底层结构:哈希表
5.HashSet的基本使用
*HashSet是Set接口的实现类:不包含重复元素,没有索引,且无序
*LinkedSet继承于HashSet,但是Set接口的实现类:不包含重复元素,没有索引,但有序
6.HashSet存储字定义对象
当自定义对象要放入到集合中时都要重写hashCode()和equals()方法,来保证元素的唯一性。
*示例代码
需求:
- 定义一个学生类,包含姓名和年龄。
- 创建多个Student对象存储到HashSet集合中。
- 要求属性值完全相同的对象只存储一个。
-
public class Student {
private String name;
private int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Student() {
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 String toString() {
return “Student [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;
Student other = (Student) 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;
}
}
public class HashSetDemo03 {
public static void main(String[] args) {
// 创建集合对象:存储学生
HashSet stus = new HashSet<>();
stus.add(new Student(“jack”,23)); // 3456445 % 16 = 1
stus.add(new Student(“rose”,24)); // 3456445 % 16 = 1
stus.add(new Student(“jack”,23)); // 3454556 % 16 = 1
System.out.println(stus);
}
}
7.LinkedHashSet的特点
Set:元素不重复,没有索引,添加和删除元素都比较快
*HashSet:元素不重复,没有索引,元素无序
*LinkedHashSet:元素不重复(哈希表实现),没有索引,元素有序(链表实现)
8.LinkedHashSet的使用
特点:继承HashSet,能够保证存取顺序一致
示例代码
/
LinkedHashSet存储元素
*/
public class LinkedHashSetDemo4 {
public static void main(String[] args) {
// 创建集合对象:存储学生
LinkedHashSet set = new LinkedHashSet<>();
set.add(“fdsaf”);
set.add(“xxxr”);
set.add(“3213f”);
set.add(“aadfasf”);
set.add(“435fdsaf”);
set.add(“435fdsaf”);
System.out.println(set);
}
}
* 小结
- 当使用HashSet存储自定义类型,如果没有重写该类的hashCode与equals方法,
则判断是否重复的依据是根据对象地址值,如果想通过内容比较元素是否相同,
需要重写该类的hashcode与equals方法
哈希表数据结构
1.哈希表数结构:数组+链表实现,添加和查询都比较快
2.查看API对比set进行迭代所需时间和HashSet实例的大小(元素的数量)和底层HashMap实例(
桶的数量)的“容量”的合成比例
3.LinkedList的元素数量越多,迭代需要的时间就越多,LinkedList查询慢
4.如果迭代性能很重要,则不要将初始容量设置的太高(或将加载因子设置的太低)
*.初始容量:创建HashSet时的数组大小,默认是16
*.加载因子:默认0.75,当元素大于“容量”加载因子时会创建一个新的数组将HashSet扩容
对象的哈希值
*就是一个十进制整数。是通过Object类的hashCode()方法获得:int hashCode();
*默认Object类的hashCode的方法返回的哈希值是该对象在内存的地址值。
*哈希值是对象存储到哈希表的重要依据。
PS.Object的hashcode方法
*对象存储在HashSet的数组哪个位置和对象的HashCode有关
*Object类:
public int hashCode();是一个Native方法(c/c++),默认情况下对象的hashcode其实
是对象的内存地址值的十进制
*String,类重写过hashCode
哈希表的存储过程
哈希表的存储过程(存取原理):每存入一个新的元素都要走以下五步
* (1)调用对象的hashCode()方法,获得要存储元素的哈希值。
* (2)将哈希值与表的长度(即数组的长度)进行求余运算得到一个整数值,该值就是新元素要存放的
位置(即是索引值)。
- 如果索引值对应的位置上没有存储任何元素,则直接将元素存储到该位置上。
- 如果索引值对应的位置上已经存储了元素,则执行第3步。
*(3)遍历该位置上的所有旧元素,依次比较每个旧元素的哈希值和新元素的哈希值是否相同。
- 如果有哈希值相同的旧元素,则执行第4步。
- 如果没有哈希值相同的旧元素,则执行第5步。
* (4)比较新元素和旧元素的地址是否相同
- 如果地址值相同则用新的元素替换老的元素。停止比较。
- 如果地址值不同,则新元素调用equals方法与旧元素比较内容是否相同。
- 如果返回true,用新的元素替换老的元素,停止比较。
- 如果返回false,则回到第3步继续遍历下一个旧元素。
* (5)说明没有重复,则将新元素存放到该位置上并让新元素记住之前该位置的元素。
.哈希表的存储过程
1.判断hashCode
*不相同
-->2.直接存储
*相同
-->2.判断equals方法
*相同-->判断为同一元素
*不相同-->直接存储
*ArrayList的contains方法判断元素是否重复原理。
*调用传入元素的equals方法依次与集合中的旧元素所比较,从而根据返回的布尔值判断是否有重复元素。
* 当ArrayList存放自定义类对象时,当自定义类在未重写equals方法前,判断是否重复的依据是地址值。
如果想根据内容判断是否为重复元素,则需要重写自定义类的equals方法。
* 底层依赖于equals()方法。
HashSet判断对象是否重复的原理
HashSet的add/contains等方法判断元素是否重复原理?
- 将新元素与集合中已有的旧元素的HashCode值进行比较。
- 哈希值相同,再通过比较地址值或equals方法比较内容是否相同。
·只要地址或内容相同,则表示包含。否则表示不包含。
- 哈希值不同,直接返回false表示不包含。
* 底层依赖于hashCode()和equals()。
HashSet构造方法分析
比如:加载因子是0.75,数组的长度为16,其中存入16 * 0.75 = 12个元素。如果再存入第十三个(>12)元素。
那么此时会扩充哈希表(再哈希),底层会开辟一个长度为原长度2倍的数组。把老元素拷贝到新数组中,
再把新元素添加数组中。当存入元素数量 > 哈希表长度 * 加载因子,就要扩容,因此加载因子决定扩容时机。
hashCode和equals方法面试题
* 两个Person对象p1和p2,如果p1.hashCode() == p2.hashCode(),则p1.equals(p2)一定是true吗?
答: 不一定
- 两个Person对象p1和p2,如果p1.equals(p2)为true。则 p1.hashCode() == p2.hashCode()一定是true吗?
答:一定
- hashCode的官方协定
1. 如果根据equals(Object)方法,两个对象是相等的。那么对这两个对象中的每个对象调用hashCode方法
都必须生成相同的整数结果。
2. 如果根据equals(java.lang.Object)方法,两个对象不相等,那么对这两个对象中的任一对象上调用
hashCode 方法不要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整
数结果可以提高哈希表的性能。
只是个人总结,如有不全,或者错误,希望大家指出!谢谢