读后感"/>
“java.util.Collection” 读后感
“java.util.Collection” 读后感
- 什么是Collection接口
- 规定实现子类的特性
- 构造方法
- 默认方法
- 抽象方法
- 提问
文中没有对每个方法涉及到的其他外部接口内容进行细究,主要是针对Collection接口的一个读后感,若读者遇到不明所以之处,不必细细追究,只用知道大致干嘛的即可,即不妨碍理解接口即可
什么是Collection接口
- 是一个集合类的顶层接口,JDK规定了该接口不可以被直接实现,只能实现其子接口,即所有的集合类都会间接的实现该接口内的方法。
- 该接口继承了Iterable接口。其内给定了15个抽象方法,和四个默认方法(接口内准许使用default关键字对方法进行声明,从而使得接口内也能存在具体实现的方法)。使用自然语言对描述了每个抽象的方法的期望功能,即若你要实现这个接口,那这些个抽象方法分别要做的事情是什么。这种口头的约束,并没有强有力的监督、纠错能力,只能是期望你有一定的职业操守,自动遵守大家都默认遵守的规则。
规定实现子类的特性
特性的具体实现子类,若不清楚,可以暂且不去细细追究,目前先大致了解Collection接口对于集合类的一些潜规定,并且记住就可以了
- 有序无序。子类对存储的元素的顺序一般有两种处理方式:
- 依据元素放入的顺序进行存储。
- 依据子类内部的排序规则进行排序后存储。
- 是否重复。即子类存储的元素是否可以重复,关键在于待存储的元素在存储之前,是否需要用hash函数处理一下:
- 对于List一类来说,以底层是数组的ArrayList为例,存储元素直接放入数组内部,若对位置有要求,就先算一下要存放的位置。
到这里,其实思想本质和hash函数的处理是一样的,但是具体的实现不一样,而针对这个特点的划分也仅仅是基于实现上的区别。 - 对于Set、Map这一类来说,以底层是数组为例,与上述上述进行比较(上述的一类存储之前是否需要计算存储位置,这个是可选的),这一类存储之前必须要进行存储位置的计算,即必须经过hash函数的处理得到一个存储位置,而计算、处理的依据是元素本身的信息(一般都是通过元素本身在ASCII或者unicode上对应的数值作为计算条件)和某一个质数(一般是31)做运算,可能还会做一个加减个位数的调整。当数据重复的时候,要求hash函数的计算结果是一样的,当一个数据是待存储的,会先进行hash函数的计算,得到存储位置,依据该位置是否有人来判断数据是否已经存在这个集合中(这里还涉及到一个hash冲突,暂不做解释,本文只是叙述一下笔者对Collection的读后感)。
- 对于List一类来说,以底层是数组的ArrayList为例,存储元素直接放入数组内部,若对位置有要求,就先算一下要存放的位置。
笔者将collection接口中的方法分为三个大类,依据是构造方法(虽然接口没有构造方法,但是Collection接口对其实现子类的构造方法做了一些口头的约束)还是默认的实现方法亦或是抽象类型的方法,又针对抽象方法依据增删改查进行划分。(因为集合类如Collection接口、Set一类、List一类、Map一类都是用作存储数据的,其他的如迭代、排序功能,都尽量用工具类进行隔离、封装,如Collections、Arrays等工具类)
构造方法
接口没有构造方法,只是这里对其实现子类(包括直接和间接的实现子类)进行口头建议
Collection接口建议,所有它的子接口的具体实现类,在构造函数上,建议明文特别声明以下两种构造函数:
- 无参构造函数。(会基本java语法的,都懂)
- 类型是Collection泛型类型的形参。
默认方法
重申一下,接口内有一个default关键字,被该关键字申明的方法,可以在接口内具体实现
- 该方法的作用是移除 this 集合内,和 filter 集合的并集元素。
不用管Predicate是函数式接口,不用具体理解,只用知道是一层包装
default boolean removeIf(Predicate<? super E> filter) {Objects.requireNonNull(filter); //判断filter是否为空,为空会向调用该方法的地方抛出空指针异常(NullPointerException)boolean removed = false;final Iterator<E> each = iterator(); //获取调用方法的集合类的迭代器while (each.hasNext()) {//判断是否存在下一个值if (filter.test(each.next())) { //test() 方法做具体的比较each.remove(); //移除当前元素removed = true;}}return removed;
}
- 笔者暂时也没用过,目前已知功能是做分割,将集合元素均分给其他集合。(如有不对,欢迎指正,给出理由)
@Overridedefault Spliterator<E> spliterator() {return Spliterators.spliterator(this, 0); }
- (不懂之处,暂且保留,后续更新)
default Stream<E> stream() {return StreamSupport.stream(spliterator(), false);
}
- (不懂之处,暂且保留,后续更新)
default Stream<E> parallelStream() {return StreamSupport.stream(spliterator(), true);
}
抽象方法
- 增
- 添加指定的单个元素。
boolean add(E e);
- 调用该方法将集合 c 添加到自己内部。
boolean addAll(Collection<? extends E> c);
- 添加指定的单个元素。
- 删
- 移除集合中指定的元素 o,如果元素存在,则移除,返回true,若不存在,则返回false。
boolean remove(Object o);
- 清除集合内部全部内容。
void clear();
- 移除集合中指定的元素 o,如果元素存在,则移除,返回true,若不存在,则返回false。
- 改
- 移除与指定集合的交集元素,一般是删去部分元素,对集合做更改。若this集合是集合 c 的子集,则起到和 clean() 同样的效果。
boolean removeAll(Collection<?> c);
- 该方法应该要实现求 this 集合和 c 集合的并集,并用并集替换 this 集和原本的元素。
boolean retainAll(Collection<?> c);
- 移除与指定集合的交集元素,一般是删去部分元素,对集合做更改。若this集合是集合 c 的子集,则起到和 clean() 同样的效果。
- 查
- 查询集合中的元素个数。
int size();
- 检查集合是否为空。若为空返回 true,若不为空返回false。
boolean isEmpty();
- 检查集合是否包含指定的对象元素。若存在则返回true,不存在返回false。底层实现是循环遍历,比hash函数直接计算出位置慢多了,时间复杂度分别是 O(n) 和 O(1)。
boolean contains(Object o);
- 检查集合是否包含指定集合的元素对象,类似方法retainAll()求并集,但不修改自身或者传入集合,只是确认两者是否有交集。返回布尔类型的值。
boolean containsAll(Collection<?> c);
- 将集合内的元素转换成Object类型的数组,
Object[] toArray();
- 将集合内的元素转换成指定类型的数组。
<T> T[] toArray(T[] a);
- 迭代器。这是一个外部接口,被写入 Iterable 接口中,而Collection接口继承了 Iterable 接口,因此也可以使用 Iterator 接口。
Iterator<E> iterator();
- 该方法应该判断 this 集合和 o 对象是否相等。
为什么集合的equals() 方法的形参是Object类型的,不应该是 Collection 的吗?boolean equals(Object o);
笔者做个Debug,this集合是一个ArrayList类型集合,调用该方法分别传入ArrayList类型变量,String类型变量,HashSet类型变量。都存入相同的值 abc 。
传入ArrayList类型变量进行比较,结果是true,调用的是AbstractList类的equals() ,形参 o 是ArrayList类型的变量。
传入String类型变量进行比较,结果是false,调用的是AbstractList类的equals(),形参 o是一个char[ ] 类型的变量(String底层是一个final的char[ ])。
传入HashSet类型的变量进行比较,结果是false,调用的是AbstractList类的equals(),形参 o 是HashSet类型的变量。
用例代码如下:public class test1 {public static void main(String[] args) {ArrayList<String> arrayList1 = new ArrayList<>();ArrayList<String> arrayList2 = new ArrayList<>();Set<String> set = new HashSet<>();String str = "abc";arrayList1.add("abc");arrayList2.add("abc");set.add("abc");System.out.println(arrayList1.equals(arrayList2)); //trueSystem.out.println(arrayList1.equals(str)); //falseSystem.out.println(arrayList1.equals(set)); //false} }
- 该方法的顶层抽象是用来计算一个变量或者对象的hash值,默认调用的hash函数是一个非java程序的本地的计算函数方法,并且可以被重写。
该方法和equals() 都可以用作判断两个变量或者两个对象是否相等,但有一个优先级,即hashcode高于equals方法。判断逻辑如下:
两个变量或者对象通过该方法得到属于自己的hash(理论上每个变量或对象的hash值是唯一的),如果hash值相同,表明两个变量或者对象一定是相同,即使通过equals() 函数判断两者不同,但最终结果也是两者相同,因为两者的hash值是一样的。
如果两个不同对象计算出了相同的hash值,这种情况是hash冲突,是需要解决的问题,通常来自于hash函数不够完美。理论上hash函数能对一切进行计算,并且对每个变量或者对象计算出唯一的hash值,只有两者相同的情况下才能得到相同的hash值
若两个变量或者对象通过equals() 判断相同,但是通过hashCode() 函数得到了不同的hash值,那么也是说明两者是不同的。
不过一般来说,equals() 都会向 hashCode() 协同(或者叫匹配,即结果尽量是前者依赖后者)。即对equals() 进行重写的时候,会同时对配套的hashCode() 进行重写。int hashCode();
- 查询集合中的元素个数。
提问
- Collection接口实现子类的具体分类有哪些?(本文未作具体详细总结、解释,大致提到了一点内容,这里建议自己进行总结划分)
- Collection接口的特性中,是否重复这一特性里面,Set、Map这一类集合,为何一定要在存储之前使用hash函数进行处理 ?
- 理论上完美的hashCode() 是什么?
- 什么是hash冲突?如何避免hash冲突?
文中内容只是笔者自己的心得体会,供大家以作讨论,顶多是一个参考,谈不上解读,若有错误之处,敬请斧正。
更多推荐
“java.util.Collection” 读后感
发布评论