一.什么是set集合
1.set集合组成
Set 集合是 Java 集合框架中的一种实现,它的底层数据结构可以有多种选择,最常见的包括哈希表、红黑树等。
HashSet 是 Set 接口的一个实现,它底层使用哈希表来实现。具体来说,当创建一个 HashSet 对象时,实际上创建的是一个 HashMap 实例,其中所有的 key 都是要添加到 Set 中的元素,而 value 则统一指定为一个固定值(该值在 JDK 1.8 中是 private final static Object PRESENT = new Object())。
由于哈希表需要固定长度的数据存储空间,而且不同的元素计算出的哈希值可能会相同,因此 HashSet 内部将哈希表划分为多个桶(Bucket),每个桶中存储的是哈希值相同的元素。当需要插入一个元素时,先计算该元素的哈希值,然后根据哈希值找到它应当存放的桶。如果该桶中已经存在一个元素,那么判断是否和待插入元素相等,如果相等,则不再插入。否则,就需要把待插入元素放入桶中。
因为不同元素的哈希值有可能会相同,因此桶中会存储有 “链表” 或 “红黑树”,这些数据结构能够存储一个或多个元素。当桶中元素数量超过一个固定的阈值(默认为 8)时,HashSet 会自动把链表转化为红黑树,以减少链表的查找时间,提高性能。另外,当桶中元素数量过少时,HashSet 也会自动将红黑树转换为链表。
除了 HashSet,Java 中还有一些其他的 Set 集合实现,例如 TreeSet 和 LinkedHashSet。TreeSet 底层使用红黑树来实现,这种数据结构可以保证有序性;而 LinkedHashSet 底层则基于哈希表加上指向前一个和后一个元素的指针来实现,它可以维护元素插入的顺序,同时也保证元素唯一性。
综上所述,Set 集合底层的实现可以使用多种不同的数据结构,包括哈希表、红黑树等。在选择具体实现时需要考虑不同实现的性能、空间复杂度、有序性等因素,并进行合理的权衡。(具体情况,具体分析!)
2.set集合去重在企业中作用
Set 集合去重在企业中非常常见,并且具有重要的作用。下面列举几个企业应用场景:
1. 数据库处理:在对数据库进行操作时,经常需要去重来避免重复数据的出现。例如在从多个数据表中检索数据时,需要将查询结果进行去重操作,以避免出现重复记录。
2. 数据分析:在数据分析中,经常需要对获取的数据进行处理和筛选,去掉重复数据能够提高数据分析的效率和准确性。例如,在分析用户行为模式时,需要筛选出真实的独立用户数据,去重是不可或缺的一步。
3. 接口开发((导入外部接口))和数据交换:在接口开发和数据交换中,去重可以避免重复数据的传输和处理,节约网络和计算资源。例如,在数据接口开发中,需要对接口返回的数据进行去重操作,以确保接口返回的数据是正确、完整且无重复的。
4. 日志分析:在日志分析中,需要对日志信息进行去重,以避免重复的数据影响分析结果。例如,对于特定的异常日志信息,需要对异常发生的次数进行统计,去掉重复的异常信息可以保证统计结果的准确性。
综上所述,Set 集合去重在企业中具有广泛的应用,能够在数据库处理、数据分析、接口开发和数据交换、日志分析等方面发挥重要作用。通过合理地使用去重算法,可以保证数据的准确性和完整性,提升企业的竞争力和效率。
二.哈希冲突
1.什么是哈希冲突?
两个或多个不同的元素通过哈希函数计算得到的哈希值相同,而它们需要存储在哈希表的不同位置上。哈希冲突是哈希函数不可避免的副作用,因为哈希函数无法将所有元素都映射到唯一的哈希值上。
2.解决方法
最常见的解决哈希冲突的方法是开放寻址法和链表法。
- 开放寻址法:当发现哈希表中的某个位置已经被占用时,就从该位置开始,依次往后查找空闲的位置,直到找到一个空闲位置为止,然后把元素存储在该位置上。这种方法可以有效地避免链表法中因为大量链表产生而导致的额外空层结构的消耗,但是当大量哈希冲突发生时,会出现线性探测次数过多的情况,导致哈希表性能下降。
- 链表法:当发现哈希表中的某个位置已经被占用时,就把新元素插入到该位置所对应的链表中,这样每次产生哈希冲突时,都会在对应位置的链表上增加一个节点,从而避免了线性探测的问题。在链表法中,哈希表中的每个位置都可以存储多个元素,只要它们满足哈希值相同的条件。
一般来说,开放寻址法在哈希表空间较小,元素数目较少时使用性能比较好,但是当哈希表相对于元素数目较大时,容易出现冲突,从而导致性能下降。而链表法则可以应对各种情况,并且在哈希表性能方面比较稳定,但相对来说浪费的空间可能会较多。
要减少哈希冲突,可以通过以下方法来优化哈希函数:
- 选择更好的哈希函数:好的哈希函数应该尽可能均匀地将元素映射到不同的哈希值上,从而减少哈希冲突的发生。
- 扩大哈希表容量:为哈希表分配更多的空间能减少元素在桶中的碰撞率,从而减少哈希冲突的发生。
- 重新设计数据结构:对于特定类型的元素,可以重新设计数据结构来提高哈希性能,例如写更好的哈希运算方法等。
综上所述,哈希冲突是哈希表中的一种不可避免的问题,但可以通过优化哈希函数和哈希表容量等方法来减少哈希冲突的发生及其对性能的影响。
三.实例操作
1.底层原理
Set 集合是 Java 中的一个接口,它继承自 Collection 接口,但与 Collection 接口不同的是,Set 集合的元素是没有重复的,这是由 Set 集合底层的原理决定的。
在 Java 中,Set 接口有多个实现类,如 TreeSet、HashSet、LinkedHashSet、EnumSet 等。其中,HashSet 是使用最广泛的一种 Set 实现类,下面以 HashSet 为例来介绍 Set 集合去重的底层原理。
HashSet 底层是通过哈希表来实现的,例如创建一个 HashSet 集合时,会创建一个容量为16的初始数组,并根据元素的 hashCode 值将元素分配到对应的桶中。当向 HashSet 集合插入元素时,首先会计算元素的 hashCode 值,然后找到对应的哈希桶,在该桶中查找是否已有相同 hashCode 值的元素存在。如果不存在,就将元素添加到哈希桶中;如果已经存在相同 hashCode 值的元素,就会调用元素的 equals 方法来进行进一步比较,如果该方法返回 true,就说明该元素已经存在于 HashSet 集合中,不会再将其插入到 HashSet 中。
因此,HashSet 集合能够确保元素的唯一性,是由 hashCode 方法和 equals 方法共同判断的。在元素插入 HashSet 集合时,首先判断元素的 hashCode 值,如果已有相同 hashCode 值的元素不存在,就不需要再进行 equals 比较;如果已有相同 hashCode 值的元素存在,则需要进一步进行 equals 比较,只有当 hashCode 值和 equals 方法的比较结果都相等时,才说明两个元素是相同的,该元素才不会被插入到 HashSet 集合中。
除了 HashSet 外,还有一些其他的实现类是通过不同的数据结构来实现 Set 集合去重的,例如 TreeSet 底层是通过红黑树实现的,LinkedHashSet 底层是通过链表和哈希表相结合的方式实现的。但它们的去重原理都是相同的,即在插入元素时,先计算元素的 hashCode 值,然后找到对应的位置,如果该位置上已经存在相同 hashCode 值的元素,就需要使用 equals 方法进行进一步比较,以确保元素的唯一性。
2.注意点:
由于哈希表是通过哈希码来定位元素的位置,因此 hashCode 的计算要尽可能均匀,以减少元素在桶中的碰撞率,提高 HashSet 的性能。
另外,HashSet 采用了拉链法解决哈希冲突,即在同一个桶中存储相同哈希码的元素时,将它们放在一个链表中,这样就可以在链表中进行 equals 比较了。
综上所述,Set 集合去重是通过哈希表实现的,它利用 hashCode 方法和 equals 方法来判断元素是否相同,哈希码相同的元素会被存储在同一个桶中,如果同一个桶中已经存在相同的元素,就需要进一步使用 equals 方法进行比较,以确保元素的唯一性。HashSet 采用的是拉链法来解决哈希冲突。
3.操作
1.代码截屏:
2.修改截屏
3.源码
package com.lz.set; import java.util.HashSet; import java.util.Set; public class demo2 { public static void main(String[] args) { /** * * 2.contains 是否能够判断含有某个对象 * 企业使用: 在实现外部接口进行 数据导入为了解决数据重复的问题 */ Set set=new HashSet<>(); //对象 set.add(new stu(1, "阿达")); set.add(new stu(2, "圣诞节")); set.add(new stu(3, "斯蒂芬")); System.out.println(set.contains(new stu(3, "斯蒂芬"))); /** * HashSet去重 * 先执行 hashcode 比较hashcode值 * *(前提是hashcode值相同才调用)equals方法 在比较 equals返回值值 */ } } class stu{ private int id; private String name; /** * @return the id */ public int getId() { return id; } /** * @param id the id to set */ public void setId(int id) { this.id = id; } /** * @return the name */ public String getName() { return name; } /** * @param name the name to set */ public void setName(String name) { this.name = name; } public stu(int id, String name) { super(); this.id = id; this.name = name; } public stu() { super(); } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return "stu [id=" + id + ", name=" + name + "]"; } /* (non-Javadoc) * @see java.lang.Object#hashCode() */ @Override public int hashCode() { System.out.println("hashcode加载中"); final int prime = 31; int result = 1; result = prime * result + id; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; // return (int) (Math.random()*100000000); } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { System.out.println("equals加载中"); if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; stu other = (stu) obj; if (id != other.id) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } }
四.说明
这只是小编使用的最基本方法,至于更难的的靠自己,毕竟我的只是让你理解怎么用