Copyright©stonee
写在前面,本篇文章将对 集合框架自顶向下有一个大概 的描述,介绍常用集合的API函数以及某些重要API函数的实现过程。 适合新人上手 。具体集合的源码分析将会在以后的专栏中推出。
1. 集合的诞生原因以及特点
Java的最初版本只为最长用的数据结构提供了很少的一组类:
Vector, Stack, Hashtable, BitSet, Enumeration
。显然这些接口离全面的集合类库相差甚远。从JavaSE1.2开始,集合框架诞生。
- 私以为,集合和数组相比:
- 数组的长度是固定的,当添加的元素超过了数组的长度的时候,需要重新定义比较麻烦;Java内部给我们提供了集合类,可以储存任意长度的任意对象,长度随着元素的增加而增加,减少而减少。即数组长度固定,集合长度动态变化;
- 对于数组来说存储基本数据类型,又可以存储引用数据类型,基本数据类型存储的是值,引用数据类型存储的是地址值;集合只能存储引用数据类型。当然也可以存储基本数据类型,不过存储的时候会自动装箱。
- 同时, 集合类给我们提供了高效操纵数据的各种可能。我们不必再纠结于快排、堆排序等排序方式,也不用担心不会写红黑树,B+树等数据结构,集合框架完全可以为我们完成这些工作(PS:这些东西还是要学习的,对个人的提升帮助很大!)
- Java中集合的接口和实现是分离的,每一个实现都可以通过一个实现了接口的类表示
2. Java集合框架图表
3. 上述图片pdf版以及文本形式下载地址:
我真的尽力了,但是由于图上的内容太多,所以我把pdf格式放到了这里,点击转到。
当然,如果没有c币的话,可以通过在下面留言或者到我的博客中找到我的联系方式^ _ ^
4. 具体集合中的API和基础原理
-
基本的集合方法
-
Collection :对于单值的操作
boolean add(E e) //Ensures that this collection contains the specified element (optional operation). boolean addAll(Collection<? extends E> c) //Adds all of the elements in the specified collection to this collection (optional operation). void clear() //Removes all of the elements from this collection (optional operation). boolean contains(Object o) //Returns true if this collection contains the specified element. boolean containsAll(Collection<?> c) //Returns true if this collection contains all of the elements in the specified collection. boolean isEmpty() //Returns true if this collection contains no elements. abstract Iterator<E> iterator() //Returns an iterator over the elements contained in this collection. boolean remove(Object o) //Removes a single instance of the specified element from this collection, if it is present (optional operation). boolean removeAll(Collection<?> c) //Removes all of this collection's elements that are also contained in the specified collection (optional operation). boolean retainAll(Collection<?> c) //Retains only the elements in this collection that are contained in the specified collection (optional operation). Object[] toArray() //Returns an array containing all of the elements in this collection. <T> T[] toArray(T[] a) //Returns an array containing all of the elements in this collection; the runtime type of the returned array is that of the specified array. String toString() //Returns a string representation of this collection.
这里的每个方法都要手动敲一遍
注意:其中后缀有
All
的方法传的参数必须是集合!还有List中的
remove()
方法,如果要删除的是整型的话,只能通过索引,此时元素不会像其他类型一样自动装箱,否则会弄混 -
Map:对于两个映射之间的操作
void clear() //Removes all of the mappings from this map (optional operation). protected Object clone() //Returns a shallow copy of this AbstractMap instance: the keys and values themselves are not cloned. boolean containsKey(Object key) //Returns true if this map contains a mapping for the specified key. boolean containsValue(Object value) //Returns true if this map maps one or more keys to the specified value. boolean equals(Object o) //Compares the specified object with this map for equality. V get(Object key) //Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key. int hashCode() //Returns the hash code value for this map. boolean isEmpty() //Returns true if this map contains no key-value mappings. Set<K> keySet() //Returns a Set view of the keys contained in this map. V put(K key, V value) //Associates the specified value with the specified key in this map (optional operation). void putAll(Map<? extends K,? extends V> m) //Copies all of the mappings from the specified map to this map (optional operation). V remove(Object key) //Removes the mapping for a key from this map if it is present (optional operation). int size() //Returns the number of key-value mappings in this map. String toString() //Returns a string representation of this map. Collection<V> values() //Returns a Collection view of the values contained in this map.
同上,这些也必须要敲一遍
-
-
AbstractCollection重写了Object类的toString方法,这使得它的派生类都可打印出字符串
-
contains()底层用equals实现
-
linkedlist中会有额外的方法对首尾进行操作,它的get方法做了部分优化,即
index > size()/2 ? 尾部 : 首部
-
在链表中使用for循环的方式遍历列表的效率 超低 ,如下:
for(int i = 0; i < list.size(); i++) list.get(i);
-
hashMap中是无序的,当key相同时,会覆盖value
-
Set中判断重复是通过
equals()
和hashCode()
,所以把自定义类对象放到set中时要重写这两个方法 -
Tree主要是通过Comparator接口实现比较大小的,当排序不满足需求时需要重写
5. 转换
-
集合和数组的相互转换
String [] arr3 = list.toArray(new String[0]); //当集合转换为数组时,数组长度小于等于集合的size时,转换后的数组长度等于size;否则和自己创建的空间一样大 List<String> li = Arrays.asList(arr); //数组转换成集合
-
数组和字符串的相互转换
char [] arr = s.toCharArray(); //把字符串转换为char数组
6.迭代
不知道在看集合框架图的时候是否发现,有两个接口
iterable
和Iterator
,我查阅API后发现,发现了一些区别:
-
Iterable和Iterator之间的异同
- 前者在lang包,后者在util包不必多说
- 分析两个单词的语义,前者表示“可迭代的”,后者表示“迭代器”。结合API,实现前者的类中都存在一个方法
Iterator<T> iterator()
来获得迭代器,即Iterator
-
当通过前者获得到后者之后,后者的方法中有三种比较常用:
boolean hasNext() //Returns true if the iteration has more elements. E next() //Returns the next element in the iteration. default void remove() //Removes from the underlying collection the last element returned by this iterator (optional operation).
-
迭代器的主要功能是用来遍历的,基本操作为:
Set<T> se = new linkedSet<>(); Iterator<T> it = se.iterator(); //ITerable可以换为任何一个实现此接口的方法 while(it.hasNext()){ System.println(it.next()); }
-
foreach在底层就是调用的上述操作,换句话说,只要是实现了Iterable接口的类都可以用foreach语句:
for(T e : se){ do somthing with e; }
-
JDK 8 之后甚至不需要用foreach,可以用到迭代器四个方法中的另外一个方法:
default void forEachRemaining(Consumer<? super E> action) //为每个剩余元素执行给定的操作,直到处理完所有元素或操作引发异常为止。 //我们可以这样来实现循环: iterator.forEachRemaining(e -> do some shit with e);
-
因为Map接口没有继承迭代器,所以迭代Map时需要转换
-
-
map中有
keyset()
方法可以获得key的集合并返回set,因为set实现了Iterable,所以可以遍历,如下:Set<String> keySet = map.keySet(); //获取key的集合 Iterator<String> it = keySet.iterator(); //间接迭代 while (it.hasNext()){ String key = it.next(); System.out.println(key + "..." + map.get(key)); } //当然也可以通过foreach,此处不演示
-
因为map中是两个值之间的映射,如果把这两个值封装成一个对象,然后把对象封装在实现Iterable的集合中,再进行遍历,如下:
//map.Entry说明Entry是map的内部接口,将键和值封装成了Entry对象,存储在set集合中 Set<Map.Entry<String, Integer>> ent = map.entrySet(); //获取每个对象 //第一种用迭代器 Iterator<Map.Entry<String, Integer>> it = ent.iterator(); while(it.hasNext()){ Map.Entry<String, Integer> en = it.next(); System.out.println(en.getKey() + "..." + en.getValue()); } //第二种,用foreach for (Map.Entry<String, Integer> e : map.entrySet()) { System.out.println(e.getKey() + "..." + e.getKey()); }
-
-
7. 比较
主要观察Comparable和Comparator,因为treeset中二叉树判断大小中用到了这两个接口,所以也提一下
-
Comparable和Comparator两者之间的异同
-
前者在lang包,几乎所有类都实现了此接口;后者在util包,是一个函数式接口;
-
前者中只有一个方法,即
compareTo(t,t)
,后者有多个,包括前者的方法 -
因为第一条原因,所以自定义类通过实现前者的compareTo方法来比较,后者基本是用于构造方法。同时,后者的优先级大于前者。同时某些库类如String实现了前者,可以通过后者在构造方法中来覆盖。栗子如下:
//Comparator //1 //TreeSet<String> ts = new TreeSet<>(new compareByLen()); // 按照这种比较器来比较,比较器优先级高,常用于已经重写Comparable接口,但是排序方式又不满足需求时 //换种构造方法就可以使字符串安长度排序 class compareByLen implements Comparator<String> { @Override public int compare(String s1,String s2) { int num = s1.length() - s2.length(); return num == 0 ? s1.compareTo(s2) : num; } //因为此类继承了Object类,所以不用重写equals //2 //此处可以用匿名内部类 TreeSet<String> ts = new TreeSet<>(new Comparator<String>() { @Override public int compare(String o1, String o2) { int num = o1.compareTo(o2); return num == 0 ? 1 : num; //不让它返回0就可保证不删除重复元素 } }); //3 //可以把内部类换成lambda TreeSet<String> ts = new TreeSet<>((s1, s2) -> { int num = s1.compareTo(s2); return num == 0 ? 1 : num; //不让它返回0就可保证不删除重复元素 });
//Comparable public class Person implements Comparable<Person>{ private int age; private String name; @Override public int compareTo(Person o) { return (this.age - o.age) == 0 ? this.name.compareTo(o.name) : (this.age - o.age); } //String中的compareTo方法已经重写,按照字典顺序进行排序 } TreeSet<Person> p = new TreeSet<>(); //这就可以完成对person类的排序
-
8. Collections
-
属于util包下,该类完全由操作集合或返回集合的静态方法组成。它包含对集合进行操作的多态算法,“包装器”,它返回一个由指定集合支持的新集合,以及一些其他零碎内容。
-
没有构造方法和子类,主要通过这个类的静态方法对集合进行操作。所以一般叫它“集合工具类”
-
Collection和Collections没有直接关系
-
其中一些方法包括:
Collections.sort(list); //通过string中的compareTo方法进行排序 System.out.println(Collections.binarySearch(list, "b")); //返回-插入点-1 System.out.println(Collections.binarySearch(list, "c")); //二分查找 System.out.println(Collections.max(list)); //获得最大值,即按照排序后的最大值 Collections.reverse(list); //翻转 Collections.shuffle(list); //对集合中所有的元素分配随机位置
-
其余的方法需要大家查阅API