集合框架
集合
数组和集合的区别?
A:长度区别
数组的长度固定
集合长度可变
B:内容不同
数组内存是同一种类型的元素
而集合可以储存不同类型的元素
C:元素的数据类型问题
数组可以储存基本类型,也可以储存引用类型
集合只能储存引用类型(原因是因为集合是泛型类,会有边界擦除)
集合体系
Collection和Collections的区别
- Collection:是单列集合的顶层接口,有子接口List和Set
- Collections:是针对集合操作的工具类,有对集合进行排序和二分查找的方法
集合小问题
(1) Map和Collection的区别?
A:Map 是由键值对组成的集合,Map的键(key)是唯一的,值(value)可以重复。
B:Collection 是有单列数据组成的集合,它的儿子List是可以重复的,Set是唯一的。
(2) HashMap和Hashtable的区别?
HashMap:线程不安全,效率高。允许null键和值。
Hashtable:线程安全,效率低。不允许null键和值。
(3)List,Set,map等接口是否都继承自Map接口
List,Set不是继承自Map接口,他们继承自Collection接口
Map接口本身就是一个顶层接口。
(4)在集合中的数据结构问题
ArrayXxx:底层数据结构是数组。查询快,增删慢。
LinkedXxx:底层数据结构是链表。查询慢,增删快。
HashXxx:底层数据结构是哈希表。跟两个有关。hashCode()和equals()
TreeXxx:底层数据结构是二叉树。两种排序方式。Comparable接口和Comparator接口
Map(双列集合)
- 概念:Map是一个键值对形式的集合。它的元素都是有键和值组成。
(双列 底层结构是针对键有效,跟值无关) - 特点:
键唯一,值可重复。
允许以键集、值集或键-值映射关系集的形式查看Map - 功能:
- 添加功能:
Object put(Object key, Object value)
当集合中不包含该key值时,添加,返回null
当集合中包含该key值时,修改,覆盖该key原来对应的值,返回被替换掉的value值 - 判断功能:
boolean isEmpty() ---判断集合是否为空
boolean containsKey(Object key) ---判断是否包含指定的键
boolean containsValue(Object vlaue) ---判断是否包含指定的值 - 删除功能:
void clear() ---清空
V remove(Object key) ---根据指定的键删除键值对,返回被删除的键值对元素的值(0如果没有指定的键,返回null) - 获取功能:
Set<K> keySet() ---获取Map集合中所有键的集合
Collection<V> values() ---获取Map集合中所有值的集合
Set<Map.Entry<K, V>> entrySet() ---获取Map集合中所有键-值映射关系的集合
V get(K key) ---根据指定的键获取该键对应的值 - 长度功能:
int size() ---返回Map集合长度,即键值对的对数
- 添加功能:
Map接口的三个子类的特点
HashMap
底层数据结构是哈希表。
线程不安全,效率高。允许null键和值
Hashtable
底层数据结构是哈希表。
线程安全,效率低。不允许null键和值
TreeMap
底层数据结构是二叉树。
线程不安全,效率高。
HashMap
怎么保证唯一性的呢?
它依赖两个方法:hashCode()和equals()
顺序:
首先判断hashCode()值是否相同。
同:继续走equals(),看返回值
如果true:就不添加到集合。
如果false:就添加到集合。
不同:就添加到集合。Hashtable
怎么保证唯一性的呢?
它依赖两个方法:hashCode()和equals()
顺序:
首先判断hashCode()值是否相同。
同:继续走equals(),看返回值
如果true:就不添加到集合。
如果false:就添加到集合。
不同:就添加到集合。TreeMap
怎么保证唯一性的呢?是根据返回是否是0。
怎么保证排序的呢?两种方式
自然排序(元素具备比较性)
实现Comparable接口
比较器排序(集合具备比较性)
你要排序的对象实现了comparable接口,自然会按自然顺序排序,如果你想要用比较器,得有个Comparator的实现类,要排序的时候将该实现类的对象传递过去,就按比较器规则排序。
Collection 单列集合体系:
List的特点:元素可重复,有序 (通过索引操作)
Set的特点:元素无序,唯一。(这里的顺序是指存储和取出顺序)
常用子类
-
List
- ArrayList
- LinkedList
- Vector
-
Set
- HashSet
- TreeSet
List接口的三个子类的特点
ArrayList
底层数据结构是数组,查询快,增删慢
线程不安全,效率高
Vector
底层数据结构是数组,查询快,增删慢
线程安全,效率低
LinkedList
底层数据结构是链表,查询慢,增删快
线程不安全,效率高
到底使用谁?根据需求看
- 是否要安全:
- 是:Vector
- 否:ArrayList,LinkedList
- 查询多:ArrayList
- 增删多:LinkedList
- (如果你什么都不知道,用ArrayList。)
- ArrayList:存储字符串和自定义对象
重写emphasized textequqls三个步骤
1. 判断是否为同一个对象(提高效率)
2. 判断是否为该类的对象(提高健壮性)
3. 向下转型,然后根据需求比较成员变量
- Vector:存储字符串和自定义对象
Vector 成员方法
public void addElement(Object obj) 添加
public Object elementAt(int index) 获取
public Enumeration elements() 获取
public int size() 长度
有自己的特殊功能。但是不用,被新功能给替代了。
A:添加功能。
void addElement( Object obj )
B:获取功能。
public Object elementAt( int index )
public Enumeration elements()
类Enumeration里边有两个方法。(类似于迭代器)
boolean hasMoreElements()
Object nextElement()
public int size()
JDK版本升级考虑的因素:A:安全。B:效率。C:简化书写。
- LinkedList:存储字符串和自定义对象
有自己的特殊功能。可以很方便的操作头和尾。
A:添加功能
void addFirst( Object o ):将指定元素插入此列表的开头。
void addLast( Object o ):将指定元素添加到此列表的结尾。
B:获取功能
Object getFirst()
Object getLast()
C: 删除功能
Object removeFirst()
Object removeLast()
Set接口子类的特点
常用子类
HashSet
底层数据结构是哈希表。
线程不安全,效率高。
TreeSet
底层数据结构是二叉树。
线程不安全,效率高
- HashSet类
- 哈希值概念:
- 哈希值:哈希值就是调用对象的hashCode()方法后返回的一个int型数字
- 哈希桶:简单点理解就是存储相同哈希值对象的一个容器
- HashSet的特点:不保证元素的迭代顺序。并且,不保证该顺序恒久不变。
- HashSet类中的add(obj)方法如何保证元素的唯一性
HashSet底层数据结构是哈希表。
它依赖两个方法:hashCode()和equals()
顺序:
首先,判断hashCode()值是否相同。
相同:
继续走equals()方法,根据其返回值:
true:说明元素重复,不添加到集合。
false:说明元素不重复,添加到集合。
不同:直接添加到集合。 - 怎么重写hashCode()和equals()方法呢?
- hashCode():
把对象的所有成员变量值相加即可。
如果是基本类型,就加值。如果是引用类型,就加哈希值。 - equals():
A:this==obj
B:!(obj instanceof Student)
C:所有成员变量的值比较。基本类型用==,引用类型用equals()。
如果不会,自动生成。
- hashCode():
- 哈希值概念:
- TreeSet
- TreeSet:根据构造方法的不同,选择使用自然排序或者比较器排序。
按照实际的需求,可以对元素进行排序。并且保证唯一。 - 怎么保证唯一性的呢?是根据返回是否是0。
怎么保证排序的呢?两种方式
自然排序(元素具备比较性)
实现Comparable接口
比较器排序(集合具备比较性)
实现Comparator接口
- TreeSet:根据构造方法的不同,选择使用自然排序或者比较器排序。
迭代器的使用
- 什么是迭代器
- 迭代器是集合的一种遍历(一个一个的获取元素)方式。
- 迭代器是一个获取元素的一个工具,类似数组中的遍历。
- Java为了方便对集合中的元素获取,为集合类中提供了一个迭代的方法iterator(),该方法返回值实现了Iterator接口,这个接口就获取集合元素的一个迭代器
- 使用步骤
- 通过集合对象获取迭代器对象。
- 通过迭代器对象判断。
- 通过迭代器对象获取。
List list = new ArrayList();
list.add(1);
list.add(3);
Iterator iterator = list.iterator();
while(iterator.hasNext()){
Integer next = (Integer)iterator.next();
System.out.println(next);
}
- 迭代器原理
- 迭代器是一个内部类,在集合的内部定义了一个类,这个类实现了Iterator接口(集合迭代器是以内部类的方式实现的)
- 由于多种集合的数据结构不同,所以存储方式不同,所以,取出方式也不同。这个时候,我们就把判断和获取功能定义在了一个接口中,将来,遍历哪种集合的时候,只要该集合内部实现这个接口即可。
Iterator迭代器
- Iterator<E>接口
- 概述:
对 collection 进行迭代的迭代器。(迭代是取出集合中元素的一种方式)
迭代器取代了Enumeration(枚举)。 - 方法:
boolean hasNext() ---如果仍有元素可以迭代,则返回 true。
E next() ---返回迭代的下一个元素。
void remove() ---从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)。
- 概述:
- 成员方法
- Iterator
boolean hasNext() ---若被迭代的集合元素还没有被遍历,返回 true
Object next() ---返回集合的下一个元素
void remove() ---删除集合上一次 next()方法返回的元素。 (若集合中有多个相同的元素, 都可以删掉) - ListIterator 继承自Iterator
void add(Object obj) ---将指定元素插入next()方法返回的元素后面
void set(Object obj) ---用指定元素替换next()或previouse()方法返回的元素
boolean hasPrevious() ---若被迭代的集合元素还没有被遍历,返回 true
Object previous() ---返回集合的上一个元素
- Iterator
- Iterator和ListIterator的区别
- Iterator是ListIterator的父接口
- Iterator是Collection集合的公共的取出容器中元素的方式,对于List,Set都通用。而ListIterator是List集合的特有的取出元素的方式
- Iterator中只具备hasNext(),next(),remove()方法,可以删除被遍历到的元素
ListIterator中具备对遍历到的元素进行增(add)删(remove)改(set)查(next)的方法,可以对元素逆向遍历previous相对于next,hasPrevious相对于hasNext(用的很少)
常见问题
并发修改异常
- 简单来讲,并发修改异常就是在对集合元素进行迭代的时候,集合本身发生了改变,所以抛出此异常。(或者说并发修改异常 是在迭代过程中,被迭代的集合发生了变化才引发的)
- 并发修改异常
A:出现的现象
迭代器遍历集合,集合修改集合元素
B:原因
迭代器是依赖于集合的,而集合的改变迭代器并不知道。
C:解决方案
a:迭代器遍历,迭代器修改(ListIterator)
元素添加在刚才迭代的位置
b:集合遍历,集合修改(size()和get())
元素添加在集合的末尾
拆箱与装箱
来源:基本类型的数值不是对象,不能调用对象的toString()、hashCode()、getClass()、equals()等方法。所以Java提供了针对每种基本类型的包装类型。
装箱就是 自动将基本数据类型转换为包装类型;
拆箱就是 自动将包装类型转换为基本数据类型。
下表是基本数据类型对应的包装器类型:
基本数据类型 | 包装类型 |
---|---|
int(4字节) | Integer |
byte(1字节) | Byte |
short(2字节) | Short |
long(8字节) | Long |
float(4字节) | Float |
double(8字节) | Double |
char(2字节) | Character |
boolean(未定) | Boolean |
包装类与基本类型的区别
- 包装类:
- 包装类是对象,拥有方法和字段.对象的调用都是通过引用对象的地址,
产生对象时直接用引用指向这个对象。 - 包装类是引用传递
- 表示一个值(不用于计算,只用于保存值时和int类型一样);
- 对象存储在堆里(堆栈更高效,这也是Java保留基本类型的原因)
- 包装类是对象,拥有方法和字段.对象的调用都是通过引用对象的地址,
- 基本类型:
- 基本类型是变量,变量的值存储在栈里;
- 基本类型是值传递