集合
1 容器:用来装数据。
变量也可以理解为容器,用来装一个数据,当再次给他赋值后,原来的值就被覆盖了,数组也是容器,用来装一组数据,长度也是固定。
我们需要更多种的容器:
(1)任意大小的容器,即容器能够不断的自动扩容
(2)有序的容器,就是把数据放到容器之后会自动排序
(3)不允许重复的容器,就是两个一样的元素放到容器中,只会留下一个
(4)存储的数据是<key,value>键值对
。。。
Java中提前设计好了一些容器,它们各有特点。
2 容器都有相同的特点
(1)都可以添加对象
(2)删除对象
(3)查找对象
(4)修改对象
(5)遍历/迭代所有对象
....
3 集合的分类
集合主要分为两大系列:Collection和Map,Collection表示一组对象,Map表示一组映射关系或键值对。
Collection
1 java.util包中的Collection<E>
代表未知类型,是集合中位置的元素的类型,E是element的首字母。!
2 特点
(1)是Collection层次结构中的根接口。
(2)Collection表示一组对象,这些对象也被称为collection的元素。
(3)一些collection允许有重复的元素,另一些则不允许。一些collection是有序的,而另一些是无序的。
(4)jdk中不提供Collection接口的任何直接的实现,它提供更具体的子接口(如 Set 和 List)实现。
3 collection的方法
(1)添加
boolean add(E e) :添加一个元素
boolean addAll(Collection<? extends E> c):添加多个元素
this.当前集合 = this.当前集合 ∪ c集合
(2)查找
boolean contains(Object o):是否包含某个元素
boolean containsAll<Collection<?> c>:查找c集合中的元素是否在当前集合中包含,类似于判断c集合是否是当前集合的“子集”。
int size() : 元素个数
boolean isEmpty():当前集合是否是空集合
查找时,是依赖于元素的equals方法比较对象, 默认情况西,继承Object父类,equals方法实现的是比较对象的地址。一般需要重写equals才能实现比较对象之间的属性。
3 删除
void clear() :清空所有元素
boolean remove(Object o) :删除一个元素
boolean removeAll(Collection<?> c):删除c中的所有元素,如果c中的元素在this.集合中没有,那么会自动跳过,不会发生异常。
this.集合 = this.集合 - (c集合 ∩ this.集合)
boolean retainAll(Collection<?> c):删除两个集合不共有的元素。留下的是共同元素。
this.集合 = this.集合 ∩ c集合
4 集合的遍历方式:增强for循环,又称为foreach
for(元素的类型 元素名 : 集合容器对象名){
}
5 Collection的迭代
(1)Object[] toArray()
(2)foreach可以遍历(快捷键iter)
foreach遍历也支持数组类型。
(3)Iterator<E> iterator():获取遍历当前集合的迭代器对象
java.util.Iterator<E>是一个迭代器的接口。
为什么迭代器要有一个接口?
因为集合的类型有很多,底层的实现也不同,例如:有的底层实现是数组,有的底层实现是链表...它们的底层实现的方式不同,那么遍历集合的方式也不同。如果说每一种集合因为自己的底层实现不同,都单独提供一种迭代方法的话,那么会导致程序员学习成本提高。所以它把所有的迭代器构成了统一的迭代器标准,定义了统一的Iterator<E>接口。
迭代器的方法:
① boolean hasNext():判断还有没有下一个元素可以迭代。
②E next():返回下一个元素。
③void remove():在遍历过程中删除元素。
6 集合元素的删除
(1)情况一:非常明确要删除的元素对象
集合对象.remove(元素);
(2)情况二:在遍历元素的过程中,判断元素是否满足xx条件,在进行删除,必须使用Iterator迭代器的remove()方法
注意:
使用foreach遍历集合的过程中,是不嫩那个调用集合的add,remove等影响元素个数的方法。否则容易出现并发修改合计的问题,可能会报java.util.ConcurrentModificationException
List集合
1 java.util.List是java.util.Collection的子接口,注意不要导错包
2 List系列集合特点
(1)线性,依次排列
(2)有序,位置依次排列
(3)可以根据索引/下标来或者对应的元素
(4)允许元素重复
3 List接口的实现类:
比较经典的有 ArrayList,Vector,LinkedList等
4 List的方法,除了Collection父接口的方法之外,还新增了如下方法
(1)添加
void add(int index, E element):添加一个元素到[index]位置
boolean addAll(int index, Collection<? extends E> c):添加一组元素到[index]位置
(2)查找
E get(int index):获取[index]位置的元素
int indexOf(Object o):从左往右找获取元素的下标
int lastIndexOf(Object o):从右往左找获取元素的下标
List subList(int fromIndex, int toIndex):截取当前集合[fromIndex, toIndex)范围的元素
(3)删除
E remove(int index) :删除[index]位置的元素
(4)修改
E set(int index, E element):把[index]位置的元素直接替换为element
5 List集合的遍历
除了Collection的遍历方式(toArray, foreach, Iterator)它也支持外,还支持ListIterator
(1)ListIterator<E> listIterator()
(2)ListIterator<E> listIterator(int index)
ListIterator<E>是Iterator<E>接口的子接口。它比Iterator多了一些方法:
(1)int nextIndex()
(2)boolean hasPrevious()
(3)E previous()
(4)int previousIndex()
(5) void set(E e)
(6)void add(E e)
Set接口
1.jav.util.Set<E>接口是java.util.collection接口的子接口
2 特点
(1)不包含重复元素
(2)null也是有效元素之一,但是只能有一个null。
3 Set本身没有扩展Collection接口的方法
4 Set的实现类
HashSet(无序的,不保证元素的添加顺序);
TreeSet(有序的,是按照元素的大小顺序排列,需要集合中的对象对应的类必须实现内/外比较器之一);
LinkedHashSet(有序的,可以保证元素的添加顺序);
这是因为LinkedHashSet中比HashSet多维护了一个”链表“,它可以记录元素的添加顺序。
如果既要求数据不重复,有要求保证添加的顺序,那么可以考虑使用LinkedHashSet
凡是和对象大小有关的一定和java.lang.Comparable或者java.util.Comparator两个接口之一有关
Set的集合都是不允许元素重复的,那么它们都是依赖什么来判断元素是否重复的呢?
HashSet和LinkedHashSet是依赖元素的hashCode和equals方法来判断元素是否重复的。
TreeSet是依赖于java.lang.Comparable接口的compareTo方法或者是java.util.Comparator接口的CompareTo方法来判断的。
返回值为0的两个对象,就认为它们是重复对象。
Iterator接口
JDK1.5之后,让Collection接口继承了一个java.lang.Iterable接口。实现Iterable这个接口就允许对象成为 "foreach" 语句的目标。
问题:为什么Collection系列的集合,可以使用foreach循环进行遍历?
是因为Collection集合继承了Iterable。
问题:java.lang.Iterable接口有什么?
抽象方法:Iterator<T> iterator()
问题3:foreach是如何帮助我们进行集合的遍历的?
foreach循环还是用的集合的Iterator迭代器。
map接口
1 java.util.Map<K,V>,它不是Collection<E>的子接口,它和Collection是同一级别的。
2 特点:
(1)Map系列的集合是用来存储键值对<key,vakue>
(2)其中的key是不能重复的
(3)其中的value是允许重复的
(4)正常情况下,任意类型的对象都可以作为key和value,但是一般key类型选择String和Integer比较多,value类型,如果表示一个对象,就普通类型就可以,如果表示多个对象,可以使用Collection集合或者数组。
3 Map接口的API
(1)添加
V put(K key, V value) :添加一对键值对
void putAll(Map<? extends K,? extends V> m) :添加多对键值对
(2)查询
boolean containsKey(Object key) :是否包含某个key
boolean containsValue(Object value) :是否包含某个value
V get(Object key) :根据key获取value
boolean isEmpty() :集合是否为空
int size() :获取键值对的个数
(3)修改
修改value:再次put,就是修改
修改key:只能先移除原来的(key,value),然后修改完key之后重新添加(新key,value)
(4) 删除
V remove(Object key) :根据key,删除一对(key,value)
4、Map接口的常见实现类:
HashMap,Hashtable,TreeMap,LinkedHashMap,Properties等
5、遍历
因为Map接口并没有继承Iterable接口,所以不支持foreach遍历。
(1) Set<K> keySet():返回map中所有的key
(2)Collection<V> values() :返回map中所有的value
(3)Set<Map.Entry<K,V>> entrySet()
思考:keySet返回值类型是Set,而values返回值类型是Collection。
因为所有的key是不能重复的,所以可以用Set集合表示。所有的value是可能重复的,肯定不是Set,具体是Collection哪个子类型,没有规定,用统一的父接口Collection。
6 Map接口的常用实现类和他们之间的关系
(1)分类
Map接口的常用实现类:HashMap、Hashtable、TreeMap、LinkedHashMap和Properties。其中HashMap是 Map 接口使用频率最高的实现类。
(2)相同点和不同点
共同点:它们的key都不能重复。都能存储(key,value)键值对,键值对的类型是Map.Entry类型
不同点: HashMap:哈希表。允许key和value是null。线程不安全的。
Hashtable:哈希表,比HashMap还要古老。不允许key和value是null。线程安全的。
LinkedHashMap:它是HashMap的子类,比HashMap多维护了key的顺序,添加顺序。而HashMap的put顺序与遍历顺序可能会不一致。
TreeMap:它是可以按照key的大小顺序排列的。要求元素实现java.lang.Comparable<T>接口,或者我们指定java.util.Comparator<T>
Properties:它是Hashtable的子类,它虽然是Map,但是从命名上看出来,弱化了它是Map的特性,强调了它是“属性”类型。
例如:系统属性 文件编码=UTF-8
JDK版本=1.8
在使用Properties时,也尽量不要用put,get等方法,而是用Object setProperty(String key, String value),String getProperty(String key) 。另外,Properties它已经不是泛型类,要求key和value都是String。
补充:java.text.Collator可以执行区分语言环境的 String 比较。使用此类可为自然语言文本构建搜索和排序例程。
问题:java.lang.Comparable<T>接口和java.util.Comparator<T>接口的选择问题?
A:只要涉及到对象的比较大小,排序问题,首先(优先)考虑java.lang.Comparable<T>接口。 如果选择java.lang.Comparable<T>接口,那么要搞清楚要比较/排序的对象的类型是什么。
例如:如果是两个String字符串比较大小,那么就要求String实现java.lang.Comparable<T>接口。 例如:如果是两个Circle比较大小,那么就要求Circle实现java.lang.Comparable<T>接口。
B:如果要比较大小的对象类型没有实现java.lang.Comparable<T>接口,而又无法让它实现(例如:它是第三方写好的类),那么就改用java.util.Comparator<T>接口来辅助,但是声明一个类,或者用匿名内部类来实现java.util.Comparator<T>接口。
C:如果要比较大小的对象类型也实现java.lang.Comparable<T>接口,但是compareTo方法中的比较大小的规则,和我新的需求不一致。
例如:Student类中实现java.lang.Comparable<T>接口,compareTo方法是按照学号排序的,但是我新的需求是想要按照成绩排序。此时不能直接修改compareTo方法,因为它可能在别的地方用了。此时就可以选择java.util.Comparator<T>接口来辅助
Map与Set的关系
Set的实现类:HashSet,LinkedHashSet,TreeSet
Map接口的常用实现类:HashMap、Hashtable、TreeMap、LinkedHashMap和Properties。
讨论:Set与Map的关系?
共同点:Set不允许元素重复, Map不允许key重复。
底层:就从Map中剥离出一些新的类型Set。
Set其实内部就是Map,只是把Map中key提取出来了。添加到Set中的元素是作为底层Map的key,而value值是 private static final Object PRESENT = new Object(); 每一个Set类型中的所有(key,value)都是同一个Object类型常量对象。
List接口的实现类
1 List接口的实现类:ArrayList,Vector,LinkedList,Stack等
2 List实现类之间的相同点和不同点
它们共同点:都允许元素重复。
不同点:
ArrayList:动态数组,线程不安全的。
内部数组的初始化长度为10,从JDK1.7之后改为0。
不够了扩容,默认扩容为1.5倍。
仅支持foreach和Iterator迭代
Vector:动态数组,最古老的动态数组,线程安全的。
内部数组的初始化长度为10.
不够了扩容,默认扩容为2倍。
支持foreach和Iterator迭代,还有一种已经不用的Enumeration迭代器。
Stack:它是Vector的子类,表示出来是栈的数据结构。(先进后出)
它是通过扩展几个特有的方法,来体现栈结构。
E peek() :获取/访问栈顶元素,但是不移走。
E pop() :拿走栈顶元素。
E push(E item) :把新元素压入栈,成为栈顶元素。
LinkedList:双向链表。它不仅实现了List接口,还实现Queue和Deque接口。
Queue:队列 先进先出
Deque:双端队列
LinkedList几乎可以实现所有线性数据结构。例如:可以作为链表、栈、队列、双端队列等 LinkedList增加了很多方法,来适应程序员的各种需求。
3 LinkedList和动态数组的区别?
动态数组:一开始需要创建一个数组,不够了再扩容。
扩容:要么扩太多,浪费空间,要么频繁扩容性能低下。
优势:如果按照下标操作元素的话,效率极高。
问题:在非末尾插入和删除元素需要移动其他元素。
LinkedList:一开始不用创建任何对象,来一个加一个就可以。
不用扩容,不涉及空间浪费问题。
优势:在插入和删除时不涉及移动元素,只要断开插入位置的前后元素,然后重新连接上新元素。