文章目录
Set系列集合特点:
无序:存取数据的顺序是不一定的, 当数据存入后, 集合的顺序就固定下来了
不重复:可以去除重复
无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素。
Set集合实现类特点:
HashSet : 无序、不重复、无索引。
public static void main(String[] args) {
// 无序, 不重复, 无索引
Set<String> sets = new HashSet<>();
sets.add("MySQL");
sets.add("MySQL");
sets.add("JAVA");
sets.add("JAVA");
sets.add("HTML");
sets.add("HTML");
sets.add("Vue");
sets.add("Vue");
System.out.println(sets); // [JAVA, MySQL, Vue, HTML]
}
LinkedHashSet:有序、不重复、无索引。
public static void main(String[] args) {
// 有序、不重复、无索引
Set<String> sets = new LinkedHashSet<>();
sets.add("MySQL");
sets.add("MySQL");
sets.add("JAVA");
sets.add("JAVA");
sets.add("HTML");
sets.add("HTML");
sets.add("Vue");
sets.add("Vue");
System.out.println(sets); // [MySQL, JAVA, HTML, Vue]
}
TreeSet:排序: 默认升序、不重复、无索引。
public static void main(String[] args) {
// 排序、不重复、无索引
Set<Integer> sets = new TreeSet<>();
sets.add(10);
sets.add(10);
sets.add(20);
sets.add(20);
sets.add(30);
sets.add(30);
sets.add(40);
sets.add(40);
sets.add(50);
sets.add(50);
System.out.println(sets); // [10, 20, 30, 40, 50]
}
Set集合的功能上基本上与Collection的API一致, Set集合没有扩展额外的API。
HashSet集合底层采取哈希表存储的数据。
哈希表是一种对于增删改查数据性能都较好的结构。
哈希表的组成:
JDK8之前的,底层使用
数组+链表组成JDK8开始后,底层采用
数组+链表+红黑树组成。哈希表是一种对于增删改查数据性能都较好的结构。
在了解哈希表之前需要先理解哈希值的概念
哈希值:
是JDK根据对象的地址,按照某种规则算出来的int类型的数值。
获取哈希值: 通过Object类的API:
public int hashCode():返回对象的哈希值
对象的哈希值特点:
同一个对象多次调用hashCode()方法返回的哈希值是相同的
public static void main(String[] args) {
String address = "成都市";
System.out.println(address.hashCode()); // 25299637
System.out.println(address.hashCode()); // 25299637
System.out.println(address.hashCode()); // 25299637
}
默认情况下,不同对象的哈希值是不同的。
public static void main(String[] args) {
String address = "成都市";
System.out.println(address.hashCode()); // 25299637
System.out.println(address.hashCode()); // 25299637
System.out.println(address.hashCode()); // 25299637
String address2 = "重庆市";
System.out.println(address2.hashCode()); // 36643529
}
JDK8之前的版本HashSet原理解析:数组 + 链表 +(结合哈希算法), 详细流程如下:
- 底层会默认创建一个默认长度16的数组,数组名table

- 根据元素的哈希值跟数组的长度求余计算出应存入的位置(哈希算法)
例如数组长度是16, 哈希值与16取余, 得出的结果一定是0到15之间的数字
- 判断当前位置是否为null,如果是null直接存入
- 如果位置不为null,表示有元素,则调用equals方法比较
- 如果一样,则不存,如果不一样,则存入数组
在JDK 7中, 新元素占老元素位置,并且新元素会指向老元素
在JDK 8中, 新元素挂在老元素下面
JDK8之后的版本HashSet原理解析:
底层结构:哈希表(数组、链表、红黑树的结合体)
当挂在元素下面的数据过多时,查询性能降低,从JDK8开始后,当链表长度超过8的时候,自动转换为红黑树。

JDK8开始后,哈希表对于红黑树的引入进一步提高了操作数据的性能。
HashSet去重注意点:
Set集合在比较两个对象时, 默认比较的是两个对象的地址是否一致, 若地址不同则认为是两个不同的对象;
而如果希望Set集合认为2个内容一样的对象是重复的,则需要自己重写对象的hashCode()和equals()方法
我们来看下面这样一个案例:
需求: 创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合,要求:学生对象的成员变量值相同,我们就认为是同一个对象
分析:
- 定义学生类,创建Set集合对象, 创建学生对象
- 把学生添加到集合
- 在学生类中重写两个方法,hashCode()和equals(),自动生成即可
public class Student {
private String name;
private int age;
private int id;
// 构造器
public Student() {};
public Student(String name, int age, int id) {
this.name = name;
this.age = age;
this.id = id;
}
// getter和setter方法
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;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
// 重写toString方法
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", id=" + id +
'}';
}
}
public class Test {
public static void main(String[] args) {
// 创建集合存储学生对象
Set<Student> students = new HashSet<>();
// 创建学生对象
Student stu1 = new Student("小明", 18, 101);
Student stu2 = new Student("小明", 18, 101);
Student stu3 = new Student("小王", 20, 102);
// 将学生对象添加到集合中
students.add(stu1);
students.add(stu2);
students.add(stu3);
System.out.println(students);
// 打印结果如下:
// [Student{name='小明', age=18, id=101},
// Student{name='小明', age=18, id=101},
// Student{name='小王', age=20, id=102}]
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && id == student.id && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
// 该方法传入的参数相同, 就会返回相同的哈希值
return Objects.hash(name, age, id);
}
public class Test {
public static void main(String[] args) {
// 创建集合存储学生对象
Set<Student> students = new HashSet<>();
// 创建学生对象
Student stu1 = new Student("小明", 18, 101);
Student stu2 = new Student("小明", 18, 101);
Student stu3 = new Student("小王", 20, 102);
// 将学生对象添加到集合中
students.add(stu1);
students.add(stu2);
students.add(stu3);
System.out.println(students);
// [Student{name='小王', age=20, id=102}, Student{name='小明', age=18, id=101}]
}
}
LinkedHashSet集合概述和特点:
有序、不重复、无索引。
这里的有序指的是保证存储和取出的元素顺序一致
原理:
底层数据结构是依然哈希表,只是每个元素又额外的多了一个双链表的机制记录存储的顺序。

TreeSet集合特点:
不重复、无索引、可排序
可排序:按照元素的大小默认升序(有小到大)排序。
TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好。
注意:TreeSet集合是一定要排序的,可以将元素按照指定的规则进行排序。
TreeSet默认排序规则:
对于数值类型:Integer , Double,官方默认按照大小进行升序排序。
public static void main(String[] args) { Set<Integer> sets1 = new TreeSet<>(); sets1.add(50); sets1.add(10); sets1.add(30); sets1.add(20); System.out.println(sets1); // [10, 20, 30, 50] Set<Double> sets2 = new TreeSet<>(); sets2.add(10.11); sets2.add(20.22); sets2.add(43.22); sets2.add(8.22); System.out.println(sets2); // [8.22, 10.11, 20.22, 43.22] }对于字符串类型:默认按照首字符的编号升序排序。
public static void main(String[] args) { Set<String> sets = new TreeSet<>(); sets.add("bbb"); sets.add("eee"); sets.add("aaa"); sets.add("ccc"); System.out.println(sets); // [aaa, bbb, ccc, eee] }
对于自定义类型如Student对象,TreeSet无法直接排序, 需要制定排序规则; 例如下面代码中向集合中添加学生类, TreeSet是无法进行排序的, 会崩溃报错。
// 错误演示
public static void main(String[] args) {
// 创建学生对象
Student stu1 = new Student("小明", 18, 101);
Student stu2 = new Student("小赵", 18, 102);
Student stu3 = new Student("小王", 18, 103);
// 创建集合
Set<Student> students = new TreeSet<>();
students.add(stu1);
students.add(stu2);
students.add(stu3);
System.out.println(students);
}
自定义排序规则: TreeSet集合存储对象的的时候有2种方式可以设计自定义比较规则
方式一: 让自定义的类(如学生类)实现Comparable接口, 并重写compareTo方法来定制比较规则。
// 实现Comparable接口
public class Student implements Comparable<Student> {
// 其他代码...
// 重写compareTo方法
@Override
public int compareTo(Student o) {
// 例如按照id进行排序
return this.id - o.id;
}
}
public static void main(String[] args) {
// 创建学生对象
Student stu1 = new Student("小明", 18, 101);
Student stu2 = new Student("小赵", 18, 102);
Student stu3 = new Student("小王", 18, 103);
// 创建集合
Set<Student> students = new TreeSet<>();
students.add(stu1);
students.add(stu2);
students.add(stu3);
System.out.println(students);
// 打印结果: 按照id升序
// [Student{name='小明', age=18, id=101},
// Student{name='小赵', age=18, id=102},
// Student{name='小王', age=18, id=103}]
}
方式二: TreeSet集合有参数构造器自带比较器对象,来进行定制比较规则, 并且该方法如果和方式一同时出现, 会优先使用此方法的比较规则。
public class SetDemo {
public static void main(String[] args) {
// 创建学生对象
Student stu1 = new Student("小明", 18, 101);
Student stu2 = new Student("小赵", 18, 102);
Student stu3 = new Student("小王", 18, 103);
// 创建集合
// 方式二: 使用构造器自带的比较器对象
Set<Student> students = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o2.getId() - o1.getId();
}
});
students.add(stu1);
students.add(stu2);
students.add(stu3);
System.out.println(students);
// 打印结果: 按照id降序
// [Student{name='小王', age=18, id=103},
// Student{name='小赵', age=18, id=102},
// Student{name='小明', age=18, id=101}]
}
}
public class SetDemo {
public static void main(String[] args) {
// 创建学生对象
Student stu1 = new Student("小明", 18, 101);
Student stu2 = new Student("小赵", 18, 102);
Student stu3 = new Student("小王", 18, 103);
// 创建集合
// 方式二: 使用构造器自带的比较器对象
Set<Student> students = new TreeSet<>((Student o1, Student o2) -> o2.getId() - o1.getId());
students.add(stu1);
students.add(stu2);
students.add(stu3);
System.out.println(students);
// 打印结果: 按照id降序
// [Student{name='小王', age=18, id=103},
// Student{name='小赵', age=18, id=102},
// Student{name='小明', age=18, id=101}]
}
}
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po