Java 基础篇之集合
List 集合
List 集合中元素有序、可重复,集合中每个元素都有其对应的索引顺序。
List 判断两个对象相等,只要通过 equals 方法比较返回 true 即可。
看个例子:
public class A {
public boolean equals(Object obj) {
return true;
}
}
import java.util.ArrayList;
import java.util.List;
public class ListTest2 {
public static void main(String[] args) {
List books = new ArrayList();
books.add(new String("a"));
books.add(new String("b"));
books.add(new String("c"));
System.out.println(books);
books.remove(new A());
System.out.println(books);
books.remove(new A());
System.out.println(books);
}
}
当试图删除一个 A 对象时,List 会调用 A 对象的 equals 方法依次与集合元素进行比较。如果 equals 方法以某个集合元素作为参数时返回 true,List 将会删除该元素。这里 A 重写了 equals 方法,总是返回 true,所以每次都会从 List 集合中删除一个元素。
ArrayList 类
ArrayList 类是基于数组实现的 List 类,完全支持前面介绍的 List 接口的全部功能。
ArrayList 封装了一个动态的、允许再分配的 Object[] 数组。
Set 集合
HashSet 类
元素没有顺序,集合元素的值可以是 null
HashSet 不是同步的,假设有多个线程同时修改了 HashSet 集合时,必须通过代码来保证其同步
HashSet 判断元素相等的标准是两个对象通过 equals() 比较相等,同时两个对象的 hashCode()返回值也相等。
hashCode 和 equals 符合这样一个约定:equals 返回 true, hashCode 必须相等。很多 Java 类库中的代码都是按照这种约定使用这两个方法的,比 如 HashSet。这也是为什么我们要求如果一个类覆盖了 hashCode 方法,就 一定要覆盖 equals 方法,并保证方法的实现符合上述约定
当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来获得该对象的 hashCode 值,然后根据该 hashCode 值决定该对象在 HashSet 中的存储位置。
HashSet 中每个能存储元素的槽位称为桶(bucket)。如果多个元素的 hashCode 值相同,但它们通过 equals 方法比较返回 false,就需要在一个桶里放多个元素,这会导致性能下降。所以,建议在需要把某个类的对象保存到 HashSet 集合时,重写该类的 equals 和 hashCode 方法,尽量保证两个对象通过 equals 方法比较返回 true 时,他们的 hashCode 方法返回值也相等。
当把可变对象添加到 HashSet 中后,需要特别小心,尽量不要去修改可变对象中参与计算 hashCode() 、equals() 方法的实例变量,否则会导致 HashSet 无法正确访问这些集合元素。
看个例子:
public class R {
int count;
public R(int count) {
this.count = count;
}
public String toString() {
return "R[count:" + count + "]";
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj != null && obj.getClass() == R.class) {
R r = (R)obj;
return this.count == r.count;
}
return false;
}
public int hashCode() {
return this.count;
}
}
import java.util.HashSet;
import java.util.Iterator;
public class HashSetTest2 {
public static void main(String[] args) {
HashSet hs = new HashSet();
hs.add(new R(5));
hs.add(new R(-3));
hs.add(new R(9));
hs.add(new R(-2));
System.out.println(hs);
Iterator it = hs.iterator();
R first = (R)it.next();
first.count = -3;
System.out.println(hs);
hs.remove(new R(-3));
System.out.println(hs);
System.out.println("hs 是否包含 count 为 -3 的 R 对象" + hs.contains(new R(-3)));
System.out.println("hs 是否包含 count 为 -2 的 R 对象" + hs.contains(new R(-2)));
}
}
/*
[R[count:-2], R[count:-3], R[count:5], R[count:9]]
[R[count:-3], R[count:-3], R[count:5], R[count:9]]
[R[count:-3], R[count:5], R[count:9]]
hs 是否包含 count 为 -3 的 R 对象false
hs 是否包含 count 为 -2 的 R 对象false
*/
LinkedHashSet 类
LinkedHashSet 是 HashSet 的子类,同样根据 hashCode 值来决定元素的存储位置。但是使用链表维护元素的次序,使得当遍历 LinkedHashSet 集合里的元素时,LinkedHashSet 会按元素的添加顺序访问集合里的元素。
LinkedHashSet 需要维护元素的插入顺序,因此性能略低于 HashSet,但在迭代访问 Set 里的全部元素时会有很好的性能,因为它以链表维护内部的顺序。
TreeSet 类
TreeSet 是 SortedSet 接口的实现类,顾名思义这是一种排序的 Set 集合。
TreeSet 底层使用 TreeMap 实现,采用红黑树的数据结构来存储集合元素。TreeSet 支持两种排序方法:自然排序和定制排序。默认情况下,使用自然排序。
自然排序
Java 提供了 Comparable 接口,接口定义了一个 compareTo(Object obj) 方法。实现该接口的类必须实现该抽象方法。
compareTo(Object obj) 比较规则如下:
- obj1.compareTo(obj2) 返回值为 0,表明相等
- obj1.compareTo(obj2) 返回值大于 0,表明 obj1 > obj2
- obj1.compareTo(obj2) 返回值小于 0,表明 obj1 < obj2
TreeSet 会调用集合元素的 compareTo(Object obj) 方法来比较元素大小关系,再将集合元素按升序排序,这就是自然排序。所以自然排序中的元素对象都必须实现了 Comparable 接口。
如果两个对象通过 compareTo(Object obj) 比较相等, 即返回值为0,TreeSet 认为它们相等,那么新对象将无法添加到 TreeSet 集合中。
如果希望 TreeSet 能正常工作,TreeSet 只能添加同一种类型的对象。
定制排序
如果需要实现定制排序,需要在创建 TreeSet 集合对象时,提供一个 Comparator 对象与该 TreeSet 集合关联。Comparator 是一个函数式接口,可以使用 Lambda 表达式代替。
通过定制排序方式时,依然不可以向 TreeSet 中添加不同类型的对象,否则引发 ClassCastException 异常。此时集合判断两个元素相等的标准是:通过 Comparator 比较两个元素返回了 0, 这样 TreeSet 也不会把第二个元素添加到集合中。
import java.util.TreeSet;
public class TreeSettest4 {
public static void main(String[] args) {
TreeSet ts = new TreeSet((o1, o2) -> {
M m1 = (M) o1;
M m2 = (M) o2;
return m1.age > m2.age ? -1 : m1.age < m2.age ? 1: 0;
});
ts.add(new M(5));
ts.add(new M(-3));
ts.add(new M(9));
System.out.println(ts);
}
}
上面使用目标类型为 Comparator 的 Lambda 表达式,它负责 ts 集合的排序。所有 M 类无需实现 Comparable 接口,而是由 TreeSet 关联的 Lambda 表达式负责元素的排序。
在实现 compareTo 方法时,强烈推荐与 equals 结果一致,否则可能会出现一些奇怪的错误。因为有些类是根据 equals 来判断重复性,有些是利用自然排序 x.compareTo(y) == 0 来判断。compareTo 是判断元素在排序中的位置是否相等,equals 是判断元素是否相等,既然一个决定排序位置,一个决定相等,所以我们非常有必要确保当排序位置相同时,其equals也应该相等。
EnumSet 类
EnumSet 是专为枚举类设计的集合类,EnumSet 中的所有元素都必须是指定枚举类型的枚举类,该枚举类型在创建 EnumSet 时显式或隐式的的指定。
EnumSet 的集合元素是有序的,以枚举值在 Enum 类内的定义顺序来决定集合元素的顺序。EnumSet 集合不允许插入 null 元素。
EnumSet 内部以位向量的形式存储,这种存储形式紧凑高效,占用内存很小,运行效率很高。尤其是在进行批量操作时,比如调用 containsAll 和 retainAll 方法时。
Map 集合
定义:Map 用于保存具有映射关系的数据,key 和 value 之间存在单向的一对一关系,key 不允许重复。
Set 与 Map 之间关系非常密切,如果把 key-value 对中的 value 当成 key 的附庸,key 在哪里,value 就在哪里。这样就可以像对待 Set 一样对待 Map 了。
实际上,Map 提供了一个 Entry 内部类来封装 key-value 对,而计算 Entry 存储时则只考虑 Entry 封装的 key。从源码来看,Java 是先实现了 Map,然后通过包装一个所有 value 都为 null 的 Map 就实现了 Set 集合。
HashMap 实现类
HashMap 中用作 key 的对象必须实现 hashCode() 方法和 equals() 方法。
HashMap 判断两个 key 相等的标准是:两个 key 通过 equals() 方法比较返回 true,两个 key 的 hashCode 值也相等。
HashMap 判断两个 value 相等的标准是:两个对象通过 equals() 方法返回 true 即可。
与 HashSet 类似,当使用自定义类作为 HashMap 的 key 时,如果重写该类的 equals() 方法 和 hashCode() 方法,则应该保证两个方法的判断标准一致,即当两个 key 通过 equals() 方法比较返回 true 时,两个 key 的 hashCode() 方法返回值也应该相同。
与 HashSet 类似,尽量不要使用可变对象作为 HashMap 的 key,如果使用了,则尽量不要在程序中修改作为 key 的可变对象。
LinkedHashMap 实现类
LinkedHashMap 也使用双向链表来维护 key-value 对的次序(其实只需要考虑 key 的次序),该链表负责维护 Map 的迭代顺序,迭代顺序与 key-value 对的插入顺序保持一致。
import java.util.LinkedHashMap;
public class LinkedHashMapTest {
public static void main(String[] args) {
LinkedHashMap scores = new LinkedHashMap();
scores.put("Chinses", 80);
scores.put("English", 82);
scores.put("Math", 76);
scores.forEach((key ,value) -> System.out.println(key + "--->" + value));
}
}
TreeMap 实现类
TreeMap 是一个红黑树数据结构,每个 key-value 对即作为红黑树的一个节点。TreeMap 存储 key-value 对节点时,需要根据 key 对节点进行排序。TreeMap 可以保证所有的 key-value 对处于有序状态。
两种排序方式:
- 自然排序:TreeMap 的所有 key 必须实现 Comparable 接口,而且所有的 key 应该是同一个类的对象,否则会抛出 ClassCastException 异常
- 定制排序:创建 TreeMap 时,传入一个 Comparator 对象,该对象负责对 TreeMap 中的所有 key 进行排序。采用定制排序时不要求 Map 的 key 实现 Comparable 接口
TreeMap 判断两个 key 相等的标准是:两个 key 通过 compareTo 方法返回 0。
类似于 TreeSet,如果使用自定义类作为 TreeMap 的 key,为了让 TreeMap 良好的工作,则重写该类的 equals() 方法和 compareTo() 方法时应该保持一致的结果:两个 key 通过 equals 方法比较返回 true 时,它们通过 compareTo 方法比较应该返回 0。
在实现 compareTo 方法时,强烈推荐与 equals 结果一致,否则可能会出现一些奇怪的错误。因为有些类是根据 equals 来判断重复性,有些是利用自然排序 x.compareTo(y) == 0 来判断。compareTo 是判断元素在排序中的位置是否相等,equals 是判断元素是否相等,既然一个决定排序位置,一个决定相等,所以我们非常有必要确保当排序位置相同时,其equals也应该相等。
官方文档的说明:
Virtually all Java core classes that implement Comparable have natural orderings that are consistent with equals.
EnumMap 实现类
EnumMap 的 key 必须是单个枚举类的枚举值。
EnumMap 具有以下特征:
EnumMap 在内部以数组形式保存
EnumMap 根据 key 的自然顺序(即枚举值在枚举类中的定义顺序)来维护 key-value 对的顺序
EnumMap 不能使用 null 作为 key 值
创建 EnumMap 时必须指定一个枚举类,从而将该 EnumMap 和指定枚举类相关联。
欢迎关注我的公众号
Java 基础篇之集合的更多相关文章
- 图学java基础篇之集合工具
两个工具类 java.utils下又两个集合相关_(准确来说其中一个是数组的)_的工具类:Arrays和Collections,其中提供了很多针对集合的操作,其中涵盖了一下几个方面: 拷贝.填充.反转 ...
- 图学java基础篇之集合
(本文部分图片引用自其他博客,最后有链接,侵删.由于笔记使用markdown记录,格式可能不是太好看,见谅) 集合结构 红字为java.util包下的,绿字为concurrent包下扩展的与并发相关的 ...
- java基础篇 之 集合概述(List)
list,有序集合,元素可重复 LinkedList:底层用链表实现,查找慢,增删快.为什么?? ArrayList:底层用数组实现,查找看,增删慢.为什么?? Vector:跟ArrayList一样 ...
- 金三银四跳槽季,BAT美团滴滴java面试大纲(带答案版)之一:Java基础篇
Java基础篇: 题记:本系列文章,会尽量模拟面试现场对话情景, 用口语而非书面语 ,采用问答形式来展现.另外每一个问题都附上“延伸”,这部分内容是帮助小伙伴们更深的理解一些底层细节的补充,在面试中可 ...
- 小白—职场之Java基础篇
java基础篇 java基础 目录 1.java是一种什么语言,jdk,jre,jvm三者的区别 2.java 1.5之后的三大版本 3.java跨平台及其原理 4.java 语言的特点 5.什么是字 ...
- Java基础篇(JVM)——类加载机制
这是Java基础篇(JVM)的第二篇文章,紧接着上一篇字节码详解,这篇我们来详解Java的类加载机制,也就是如何把字节码代表的类信息加载进入内存中. 我们知道,不管是根据类新建对象,还是直接使用类变量 ...
- java基础篇---I/O技术
java基础篇---I/O技术 对于任何程序设计语言而言,输入输出(I/O)系统都是比较复杂的而且还是比较核心的.在java.io.包中提供了相关的API. java中流的概念划分 流的方向: 输 ...
- java基础篇---HTTP协议
java基础篇---HTTP协议 HTTP协议一直是自己的薄弱点,也没抽太多时间去看这方面的内容,今天兴致来了就在网上搜了下关于http协议,发现有园友写了一篇非常好的博文,博文地址:(http: ...
- java基础篇---I/O技术(三)
接上一篇java基础篇---I/O技术(二) Java对象的序列化和反序列化 什么叫对象的序列化和反序列化 要想完成对象的输入或输出,还必须依靠对象输出流(ObjectOutputStream)和对象 ...
随机推荐
- 微信小程序那些令人眼泪汪汪的坑儿
前言 最近做了一个麻雀虽小,五脏俱全的微信小程序项目.一看就会,一用就废的小程序.有些坑真的坑的你两眼泪汪汪.我就爱干前人栽树后人乘凉的事儿,看到文章的你,也许是同道中人,相视一笑:亦或是小程序外围人 ...
- 安装python的第三方库pillow
参考:http://jingyan.baidu.com/article/ff42efa929e6c8c19f220254.html 用Python做图像处理时,需要用到PIL(图像处理库).但是PIL ...
- P3317 [SDOI2014]重建 变元矩阵树定理 高斯消元
传送门:https://www.luogu.org/problemnew/show/P3317 这道题的推导公式还是比较好理解的,但是由于这个矩阵是小数的,要注意高斯消元方法的使用: #include ...
- HDU 4280 Island Transport(无向图最大流)
HDU 4280:http://acm.hdu.edu.cn/showproblem.php?pid=4280 题意: 比较裸的最大流题目,就是这是个无向图,并且比较卡时间. 思路: 是这样的,由于是 ...
- hihocoder #1616 : 是二叉搜索树吗?(模拟题)
题目链接:http://hihocoder.com/problemset/problem/1616 题解:就是简单的模拟一下至于如何判断是不是二叉搜索树可以通过中序遍历将每个点存下来看是不是递增的如果 ...
- hdu 5974 A Simple Math Problem(数学题)
Problem Description Given two positive integers a and b,find suitable X and Y to meet the conditions ...
- 基于单细胞测序数据构建细胞状态转换轨迹(cell trajectory)方法总结
细胞状态转换轨迹构建示意图(Trapnell et al. Nature Biotechnology, 2014) 在各种生物系统中,细胞都会展现出一系列的不同状态(如基因表达的动态变化等),这些状态 ...
- springmvc使用JSR-303对复杂对象进行校验
JSR-303 是JAVA EE 6 中的一项子规范,叫做Bean Validation,官方参考实现是Hibernate Validator.此实现与Hibernate ORM 没有任何关系.JSR ...
- 【Offer】[18-2] 【删除链表中重复的节点】
题目描述 思路分析 测试用例 Java代码 代码链接 题目描述 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针. 例如,链表1->2->3- ...
- Toad客户端连接Oracle数据库报错 ORA-12170:TNS:连接超时
Oracle 客户端连接Oracle数据库报错 ORA-12170:TNS:连接超时 排错步骤: 1. 查看网络是否畅通: 打开cmd,ping数据库IP 2. 查看端口是否畅通: 打开cmd ...