Java 核心技术-集合-集合框架
说在前面的话:
关于Core Java 集合方面的博文网上已经写烂了,为啥我还要写呢?
答:他们写的都很好,我也学到不少东西,如果把我当做一个系统的话,学习别人、看书、读源码是输入,但是往往形不成一个回路,形不成回路就会与行业脱节,所以我也要输出一些东西,尽管这些东西大家耳熟能详。
本文适合的读者?
答:会简单用Java 集合类库,看过core java volume 1但是不知其所以然的同学。
废话不多说,大家找你们感兴趣的点吧,也可以多多提提建议。
没有目录的博文都是耍流氓。
集合框架
框架是一个类的集,它奠定了创建高级功能的基础。框架包含很多超类,这些超类拥有非常有用的功能、策略和机制。框架使用者创建的子类可以扩展超类的功能,而不必重新创建这些有用的机制。
Java集合类库中有很多有用的接口、抽象类和具体类,这些对使用者来说非常有帮助,你需要用的时候直接拿来用就可以了,需要扩展功能的实现相应的接口和扩展类即可。
集合类库的接口关系图这里就不画了,基本的接口有Collection和Map。还有Iterator接口。这些接口被一些更具体的接口继承,来提供某种具体集合的服务。除此之外,还有一个标记接口RandomAccess,这个接口没有任何方法,只是用来检测一个特定的集合是否支持高效的随机访问。
视图与包装器
使用视图可以获得其他的实现了集合接口和映射表接口的对象。
例如:映射表类的keySet方法,看起来好像它创建了一个新集(Set),并将映射表中的所有键都填进去,然后返回这个集。事实是该方法返回一个实现Set接口的类对象,这个类方法对原映射表进行操作。这种集合就称为视图。
视图技术的应用
轻量级集包装器
Arrays类的静态方法asList将返回一个包装了普通Java数组的List包装器,该方法可以将数组传递给一个希望得到列表或者集合变量的方法。
String[] values = new String[10];
List<String> aList = Arrays.asList(values);
返回的对象不是ArrayList,而是一个视图对象,带有访问底层数组的get和set方法。改变数组大小的所有方法都会抛出一个异常(不支持的操作)。
源码解析:
// Arrays asList方法源码
@SafeVarargs
public static <T> List<T> asList(T... a) {
// 返回构造的ArrayListd对象,注意构造方法的参数,并不是java.util.ArrayList支持的参数。
return new ArrayList<>(a);
}
这里使用的ArrayList类是Arrays的内部类
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable
{
private static final long serialVersionUID = -2764017481108945198L;
private final E[] a;
ArrayList(E[] array) { // 构造函数接收数组作为参数
if (array==null)
throw new NullPointerException();
a = array;
}
public Object[] toArray() { //默认的toArray方法返回Object[]
return a.clone();
}
public <T> T[] toArray(T[] a) { //返回传入参数类型的数组
int size = size();
if (a.length < size)
return Arrays.copyOf(this.a, size,
(Class<? extends T[]>) a.getClass());
System.arraycopy(this.a, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
// 没有覆盖add remove等改变数组大小的方法
// get set indexof contains 方法省略……
}
为什么改变数组的大小的方法会抛出异常呢? ArrayList<E> 继承自AbstractList类,AbstractList类中定义的改变数组大小的方法默认抛出异常。
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
public E remove(int index) {
throw new UnsupportedOperationException();
}
下一个操作,Collections.nCopies();
List<anObject> oList = Collections.nCopies(n,anObject);
将返回一个实现了List接口的不可修改的对象(不能修改引用,并没说对象本身不可以修改)。
该方法和Arrays.asList()方法类似,也是返回一个内部类(CopiesList)的对象,该内部类继承AbstractList类,没有覆盖修改对象的方法(所以不支持修改该对象)
public static <T> List<T> nCopies(int n, T o) {
if (n < 0)
throw new IllegalArgumentException("List length = " + n);
return new CopiesList<>(n, o);
}
private static class CopiesList<E>
extends AbstractList<E>
implements RandomAccess, Serializable
{
private static final long serialVersionUID = 2739099268398711800L;
final int n;
final E element; // 只有这一个元素,对外看起来就像是有n个元素一样
CopiesList(int n, E e) {
assert n >= 0;
this.n = n;
element = e;
}
public boolean contains(Object obj) {
return n != 0 && eq(obj, element);
}
public int indexOf(Object o) {
return contains(o) ? 0 : -1;
}
public int lastIndexOf(Object o) {
return contains(o) ? n - 1 : -1;
}
public E get(int index) {
if (index < 0 || index >= n)
throw new IndexOutOfBoundsException("Index: "+index+ ", Size: "+n);
return element;
}
public Object[] toArray() {
final Object[] a = new Object[n];
if (element != null)
Arrays.fill(a, 0, n, element);
return a;
}
public <T> T[] toArray(T[] a) {
final int n = this.n;
if (a.length < n) {
a = (T[])java.lang.reflect.Array
.newInstance(a.getClass().getComponentType(), n);
if (element != null)
Arrays.fill(a, 0, n, element);
} else {
Arrays.fill(a, 0, n, element);
if (a.length > n)
a[n] = null;
}
return a;
}
public List<E> subList(int fromIndex, int toIndex) {
if (fromIndex < 0)
throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
if (toIndex > n)
throw new IndexOutOfBoundsException("toIndex = " + toIndex);
if (fromIndex > toIndex)
throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")");
return new CopiesList<>(toIndex - fromIndex, element);
}
}
同理 Collections 类中还有很多方法都是按照类似的节奏来对外提供服务的,Collections.singleton(anObject); 返回一个视图对象,该对象实现了Set<E>接口,其实是继承了AbstractSet抽象类。返回的对象实现了一个不可修改的单元素集。
singletonList方法和singletonMap方法类似。具体请参见Collections源代码。
(源码面前,了无秘密)
子范围
很多集合都支持建立子范围(subrange)视图。对于列表,可以使用subList方法来获得一个列表的子范围视图。
List<String> group = aList.subList(10,20);
可以将任何操作应用于子范围(包括改变列表大小,也可以清空),并且能够自动反映整个列表的情况(因为子范围返回的对象是列表内部类的对象,也就是列表的视图,它们共用一份数组。)
对于有序集和映射表,可以使用排序顺序而不是元素位置建立子范围。
SortedSet<E> subSet(E from,E to)
SortedSet<E> headSet(E to)
SortedSet<E> tailSet(E from)
SortedMap<K,V> subMap(K from , K to)
SortedMap<K,V> headMap(K to)
SortedMap<K,V> tailMap(K from)
以上方法返回有序集和映射表的视图。
Java SE 6引入NavigableSet接口赋予子范围操作更多的控制能力。可以指定是否包含边界:
NavigableSet<E> subSet(E from,boolean fromInclusive, E to, boolean toInclusive)
NavigableSet<E> headSet(E to,boolean toInclusive)
NavigableSet<E> tailSet(E from,boolean fromInclusive)
不可修改视图
Collections 还有几个方法用于产生集合的不可修改视图,这些视图对现有集合增加一个运行时的检查。如果发现试图对集合进行修改,就抛出一个异常,同时这个集合将保持未修改的状态。
这些方法有:
Collections.unmodifiableCollection
Collections.unmodifiableList
Collections.unmodifiableSet
Collections.unmodifiableSortedSet
Collections.unmodifiableMap
Collections.unmodifiableSortedMap
不可修改视图并不是集合本身不可修改。仍然可以通过集合的原始引用进行修改。
同步视图
由于普通集合类不能保证被多线程访问的安全性。
类库的设计者提供了一种视图机制保证常规集合的线程安全,而不是实现线程安全的集合类。例如: Collections类的静态synchronizedMap方法可以将任何一个映射表转换成具有同步访问方法的Map。
Map<String,Employee> map =
Collections.synchronizedMap(new HashMap<String,Employee>());
通过源码我们可以了解到,集合框架Collections 给我们提供了很多有用的内部类,这些内部类担当常规集合的同步视图,个人感觉有点像装饰者模式。为了让大家有一个直观的认识,这些同步内部类的继承结构如下图所示(原谅我作图实在是。):
其实同步视图的原理是在访问常规集合的时候加了个锁,具体可以参见源码。很清晰,也很简单。
检查视图
Java SE 5 增加了一组“检查”视图,用来对泛型类型发生问题时提供调试支持。
错误举例:
ArrayList<String> strings = new ArrayList<>();
ArrayList rawList = strings;
rawList.add(new Date()); // add不会出错,另一部分代码调用get方法,并转换为String 就会抛出异常了。
检查视图可以探测到这类问题。
List<String> safeStrings = Collections.checkedList(strings,Sring.class);
原理其实很简单,在调用集合add方法之前检验下添加元素的类型和集合元素的类型是否匹配。
源码如下:
void typeCheck(Object o) {
if (o != null && !type.isInstance(o))
throw new ClassCastException(badElementMsg(o));
}
private String badElementMsg(Object o) {
return "Attempt to insert " + o.getClass() +
" element into collection with element type " + type;
}
CheckedCollection(Collection<E> c, Class<E> type) {
if (c==null || type == null)
throw new NullPointerException();
this.c = c;
this.type = type;
}
关于可选操作的说明
通常,视图有一些局限性,可能只可以读、无法改变大小、只支持删除而不支持插入,这些与映射表的键视图情况相同。如果试图进行恰当的操作,受限制视图就会抛出 UnsupportedOperationException。
这样很好的限制了接口数量的成倍增加。
批操作
使用类库中的批操作避免频繁的使用迭代器
集合批操作 |
批操作含义 |
retainAll(Collection<?> c):boolean |
保留参数中不存在的元素 |
removeAll(Collection<?> c):boolean |
删除参数中存在的元素 |
addAll(Collection<?> c):boolean |
添加参数中存在的元素 |
clear():void |
清空集合 |
集合与数组之间的转换
由于集合框架比Java平台的大部分API生的晚,所以有时候为了兼容,需要在传统的数组和现代的集合之间进行转换。
数组转换为集合,Arrays.asList的包装器可以实现:
String [] values = ...;
HashSet<String> staff = new HashSet<String>(Arrays,asList(values));
将集合转换为数组:
(1) 使用toArray方法
Object[] values = staff.toArray();
这样做的结果是产生一个对象数组。即使知道集合中包含一个特定类型的对象,也不能使用类型转换。
String[] values = (String[]) staff.toArray(); //error
toArray方法返回的数组是一个Object[]数组,无法改变其类型。
(2) 使用另外一种toArray方法,并将其设计为所希望的元素类型且长度为0的数组,随后所返回的数组将和所创建的数组一样:
String[] values = staff.toArray(new String[0]); // right
Java 核心技术-集合-集合框架的更多相关文章
- Java核心技术梳理-集合
一.前言 在日常开发中,我们经常会碰到需要在运行时才知道对象个数的情况,这种情况不能使用数组,因为数组是固定数量的,这个时候我们就会使用集合,因为集合可以存储数量不确定的对象. 集合类是特别有用的工具 ...
- Java核心技术点之集合框架
1. 概述 Java集合框架由Java类库的一系列接口.抽象类以及具体实现类组成.我们这里所说的集合就是把一组对象组织到一起,然后再根据不同的需求操纵这些数据.集合类型就是容纳这些对象的一个容 ...
- Java中的集合框架
概念与作用 集合概念 现实生活中:很多事物凑在一起 数学中的集合:具有共同属性的事物的总体 java中的集合类:是一种工具类,就像是容器,储存任意数量的具有共同属性的对象 在编程时,常常需要集中存放多 ...
- 菜鸟日记之 java中的集合框架
java中的集合框架图 如图所示:java中的集合分为两种Collection和Map两种接口 可分为Collection是单列集合和Map的双列集合 Collection单列集合:继承了Iterat ...
- Java基础--说集合框架
版权所有,转载注明出处. 1,Java中,集合是什么?为什么会出现? 根据数学的定义,集合是一个元素或多个元素的构成,即集合一个装有元素的容器. Java中已经有数组这一装有元素的容器,为什么还要新建 ...
- Java中的集合框架(上)
Java中的集合框架概述 集合的概念: Java中的集合类:是一种工具类,就像是容器,存储任意数量的具有共同属性的对象. 集合的作用: 1.在类的内部,对数据进行组织: 2.简单的快速的搜索大数据量的 ...
- Java核心技术卷一基础技术-第13章-集合-读书笔记
第13章 集合 本章内容: * 集合接口 * 具体的集合 * 集合框架 * 算法 * 遗留的集合 13.1 集合接口 Enumeration接口提供了一种用于访问任意容器中各个元素的抽象机制. 13. ...
- 第51节:Java当中的集合框架Map
简书作者:达叔小生 Java当中的集合框架Map 01 Map提供了三个集合视图: 键集 值集 键-值 映射集 public String getWeek(int num){ if(num<0 ...
- 第48节:Java当中的集合框架
Java当中的集合框架 01 在我们班里有50位同学,就有50位对象. // 简书作者:达叔小生 Student[] stus = new Student[20]; 结果来了一位插班生,该同学因为觉得 ...
随机推荐
- 在SpringMVC利用MockMvc进行单元测试
spring在线文档:https://docs.spring.io/spring/docs/current/javadoc-api/index.html?index-files/index-13.ht ...
- SQL 去除重复、获取最新记录
应用中常会有需要去除重复的记录,或者获取某些最新记录(如:每个用户可以答题多次,每次答题时间不同,现在要获取所有用户的最新答题记录,即每个用户取最新的一条) 使用group 和max 即可实现上述功能 ...
- uva 10034
计算所有点之间的权值 然后就是最小生成树 #include<cstring> #include<string> #include<cstdio> #includ ...
- uva 108
降维 枚举行累加 然后求单行上最大连续和 #include <iostream> #include <cstring> #include <cstdio> # ...
- Discuz云平台站点信息同步失败,An unknown error occurred. May be DNS Error.
站点信息同步失败 An unknown error occurred. May be DNS Error. (ERRCODE:1) 经过Discuz教程网(http://www.1314study.c ...
- 抽象工厂模式(python版)
http://blog.csdn.net/ponder008/article/details/6886039 抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类.优点:易 ...
- Android_Mars学习笔记_S01_001activity初步
一.activity初步 1.程序启动会先读配置文件AndroidManifest.xml找activity 2.activity会在onCreate方法中读取activity_main.xml文件, ...
- laravel的解决方案
对form表单批量去掉前后空格trim: $request->merge(array_map('trim', $request->all())); 或 Input::merge(array ...
- vimrc for windows
set nobackupsource $VIMRUNTIME/vimrc_example.vimsource $VIMRUNTIME/mswin.vimbehave mswin:color deser ...
- Sqlmap基础(二)
sqlmap.py -r req1.txt --dbms Oracle --risk