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)和对象 ...
随机推荐
- APPARENT DEADLOCK!!!c3p0数据库连接池死锁问题
项目进行压力测试的时候,运行大概1小时候,后台抛出以下异常: Nov 9, 2012 1:41:59 AM com.mchange.v2.async.ThreadPoolAsynchronousRun ...
- 网页播放摄像头视频一种新的实现方式(非ocx方式)
前言 出于安全性考虑,浏览器对网页调用本地资源做了诸多限制.单纯的js是不能调用本地摄像头的,最常用的解决方案是通过ocx来实现.ocx是IE浏览器的扩展插件,并不是通用标准,很多浏览器并不支持ocx ...
- Mybatis案例升级版——小案例大道理
纯Mybatis案例升级版——小案例大道理 前言: 这几天看了一本书<原则>,在上面看到了一句话叫“每个人都把自己眼界的局限当成世界的局限”,大学生是
- UVA-10004-Bicoloring二分图染色
题意:给一张图,判断是不是二分图: 自己一开始不知道是二分图染色,理解的是任意三点不能互相连接 可能以后遇到这样的模型,可以往二分图想: 首先怎么判定一个图是否为二分图 从其中一个定点开始,将跟它邻接 ...
- 淘淘购物系统 (Python)
#首页def tao_first(): t1 = '欢迎进入淘淘购物'.center(110) print(t1) print('~' * 130) t2 = '注册'.center(20) prin ...
- 记一次tomcat内存大涨到溢出的经历
前一段时间提交了一个产品版本给测试人员测试,测试结果简直出人意料! 测试一段时间后页面就卡死了,当时根据这个现象下意识的怀疑是卡到数据库这一层,然后查看数据库连接相关的参数,如意料之中的相似,连接数太 ...
- js关系运算符的用法和区别
var num = 1; var str = '1'; var test = 1; test == num //true 相同类型 相同值 test === num //true ...
- springcloud(四):应用配置中心config的安全设置
springcloud应用配置中心config的安全设置 在springcloud应用开发中,为了方便在线管理我们的配置文件,通常会配一个配置中心config-server,这里托管着应用的一些配置文 ...
- App 冷启动与热启动及启动白屏优化
介绍一下 app 冷启动和热启动方式来实现 app 秒开的效果.那么,先来看看什么叫冷启动和热启动. 冷启动:指 app 被后台杀死后,在这个状态打开 app,这种启动方式叫做冷启动. 热启动:指 a ...
- android中的后退键——onBackPressed()的使用
转自:http://blog.sina.com.cn/s/blog_5085156c0101725e.html 很多网友不明白如何在Android平台上捕获Back键的事件,Back键是手机上的后退键 ...