Java集合
数组和集合区别
长度区别
- 数组的长度固定
- 集合的长度可变
内容不同
- 数组存储的是同一种类型的元素
- 集合可以存储不同类型的元素
元素的数据类型
- 数组可以存储基本数据类型,也可以存储引用数据类型
- 集合只能存储引用数据类型
集合
Java集合框架包含的内容
Collection:接口存储一组不唯一,无序的对象
List:接口存储一组不唯一,有序的对象(存储顺序和取出顺序一致)
Set:接口存储一组唯一,无序的对象(存储顺序和取出顺序不一致)
Map:接口存储一组键值对象,提供key到value的映射(key唯一,value不唯一)
collection接口
添加方法
boolean add(E e)//添加一个元素
boolean addAll(Collection<? extends E> c)//添加一个集合元素
删除方法
void clear()//移除所有元素
boolean remove(Object o)//移除一个元素
boolean removeAll(Collection<?> c)//移除一个集合元素,只要有一个元素被移除,就返回true
判断方法
boolean isEmpty()//判断集合是否为空
boolean contains()//判断集合中是否包含指定元素
boolean containsAll()//判断集合中是否包含指定的集合元素,只有包含所有元素,才返回true
获取方法
Iterator<E> iterator()//迭代器,集合的专用遍历方式
Iterator i = 集合对象.iterator;
while(i.hasNext()){
i.next();
}
不要多次使用next()方法,因为每次使用都是访问一个对象。
迭代器是依赖于集合存在的,遍历集合后修改元素,会发生并发修改异常。、
int size()//元素的个数
boolean retainAll(Collection<?> c)//仅保留两个集合都有的元素,A与B做交集,最终的结果保存在A中,B不变,返回值表示的是A是否发生过改变。
Object[] toArray()//把集合转换为数组
List接口
boolean add(Object o)//在列表的末尾顺序添加元素,起始索引位置从0开始
void add(int index,Object o)//在指定的索引位置添加元素。索引位置必须介于0和列表中元素个数之间。
boolean contains(Object o)//判断列表中是否存在指定元素
Object get(int index)//返回指定索引位置处的元素。取出的元素是Object类型,使用前需要进行强制类型转换。
boolean remove(Object o)//从列表中删除元素
Object remove(int index)//从列表中删除指定位置元素,起始索引位置从0开始
E set(int index,E element)//用指定元素替换列表中指定位置的元素
int size()//返回列表中的元素个数
int indexOf(Object o)//返回此列表中第一次出现的指定元素的索引,如果此列表不包含该元素,则返回-1
int lastIndexOf(Object o)//返回此列表中最后出现的指定元素的索引,如果此列表不包含该元素,则返回-1
List<E> subList(int fromIndex,int toIndex)//返回列表中指定的fromIndex(包括)和toIndex(不包括)之间的部分视图
ListIterator listIterator()//逆向遍历,但是必须先正向遍历
ArrayList
ArrayList()构造一个初始容量为10的空列表。
底层数据结构是数组,查询快,增删慢。线程不安全,效率高。
LinkedList
底层数据结构是链表,查询慢,增删快。线程不安全,效率高。
void addFirst(Object o)//在列表的首部添加元素
void addLast(Object o)//在列表的末尾添加元素
Object getFirst()//返回列表中的第一个元素
Object getLast()//返回列表中的最后一个元素
Object removeFirst()//删除并返回列白哦中的第一个元素
Object removeLast()//删除并返回列表中的最后一个元素
挽救的子类Vector
底层数据结构是数组,查询快,增删慢。线程安全,效率低。
void addElement(E obj)//添加
E elementAt(int index)//获取
Set接口
散列存放HashSet
里面不能存放重复元素,而且是采用散列的存储方式,所以是没有顺序的。
不保证set的迭代顺序,特别是他不保证该顺序恒久不变。
HashSet如何保证元素唯一性
底层数据结构是哈希表(元素是链表的数组)。
哈希表依赖于哈希值存储。
添加功能底层依赖两个方法:hashCode()和equals()。
给hashset存放对象必须实现Comparator接口
LinkedHashSet
底层数据结构由哈希表和链表组成,元素有序唯一。
由链表表示元素有序,由哈希表表示元素唯一。
有序存放TreeSet
能够对元素按照某种规则进行排序。
排序有两种方式:
自然排序(元素具备比较性):让元素所属的类实现Comparable接口
比较器排序(集合具备比较性):让集合构造方法接收Comparator的实现类对象
集合特点:有序和唯一
一个普通的类对象是不能TreeSet集合中加入的,如果直接加入会出现以下异常:java.lang.ClassCaseException,如果想要使用TreeSet则对象所在的类必须实现Compable接口。从TreeSet类的定义中可以发现,TreeSet里实现了sortedSet接口,此接口主要是用于排序操作的。
针对Collection集合我们到底使用哪一个?
判断唯一:
- Y:Set
- 判断排序:
- Y:TreeSet
- N:HashSet
- 判断排序:
- N: List
- 判断线程安全:
-
-
- Y:Vector
- N:ArrayList(查询)或LinkedList(增删)
-
Map接口
将键映射到值的对象,一个映射不能包含重复的键,每个键最多只能映射到一个值。
Map接口和Collection接口的不同
Map是双列的,Collection是单列的
Map的键唯一,Collection的子体系Set是唯一的
Map集合的数据结构值针对键有效,跟值无关;Collection集合的数据结构是针对元素有效
方法
Object put(Object key,Object val)//以“键-值对”的方式进行存储
Object get(Object key)//根据键返回相关的值,如果不存在指定的键,返回null
Object remove(Object key)//删除由指定的键映射的“键-值对”
int size()//返回元素个数
Set keySet()//返回键的集合
Collection values()//返回值的集合
boolean containsKey(Object key)//如果存在由指定的键映射的“键-值对”,返回true
Map集合遍历
Java中的所有map都实现了Map接口,以下方法适用于任何map实现(HashMap,TreeMap,LinkedHashMap,Hashtable,等等)
- 方法一:在for-each循环中使用entries来遍历
这是最常见的并且在大多数情况下也是最可取的遍历方式,在键值都需要时使用。
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for(Map.Entry<Integer, Integer> entry : map.entrySet()) {
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}
注意:for-each循环在java5中被引入,所以该方法只能应用于java5或更高的版本中。如果你遍历的是一个空的map对象,for-each循环将抛出NullPointerException,因此在遍历前你总是应该检查空引用。
- 方法二:在for-each循环中遍历keys或values
如果只需要map中的键或者值,你可以通过keySet或values来实现遍历,而不是用entrySet。
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
//遍历map中的键
for (Integer key : map.keySet()) {
System.out.println("Key = " + key);
}
//遍历map中的值
for (Integer value : map.values()) {
System.out.println("Value = " + value);
}
该方法比entrySet遍历在性能上稍好(快了10%),而且代码更加干净。
- 方法三:使用Iterator遍历
使用泛型:
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry<Integer, Integer> entry = entries.next();
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}
不使用泛型:
Map map = new HashMap();
Iterator entries = map.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry = (Map.Entry) entries.next();
Integer key = (Integer)entry.getKey();
Integer value = (Integer)entry.getValue();
System.out.println("Key = " + key + ", Value = " + value);
}
你也可以在keySet和values上应用同样的方法。
该种方式看起来冗余却有其优点所在。首先,在老版本java中这是惟一遍历map的方式。另一个好处是,你可以在遍历时调用iterator.remove()来删除entries,另两个方法则不能。根据javadoc的说明,如果在for-each遍历中尝试使用此方法,结果是不可预测的。
从性能方面看,该方法类同于for-each遍历(即方法二)的性能。
- 方法四:通过键找值遍历(效率低)
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (Integer key : map.keySet()) {
Integer value = map.get(key);
System.out.println("Key = " + key + ", Value = " + value);
}
作为方法一的替代,这个代码看上去更加干净;但实际上它相当慢且无效率。因为从键取值是耗时的操作(与方法一相比,在不同的Map实现中该方法慢了20%~200%)。如果你安装了FindBugs,它会做出检查并警告你关于哪些是低效率的遍历。所以尽量避免使用。
- 总结
如果仅需要键(keys)或值(values)使用方法二。如果你使用的语言版本低于java 5,或是打算在遍历时删除entries,必须使用方法三。否则使用方法一(键值都要)。
HashMap
基于哈希表的Map接口实现,保证键的唯一性。
无序存放的,是新的操作类,key不允许重复。
Hashtable和HashMap的区别
Hashtable:线程安全,效率低,不允许null键和null值。
HashMap:线程不安全,效率高,允许null键和null值。
TreeMap
键是红黑树结构,可以保证键的排序和唯一性。
可以排序的Map集合,按集合中的key排序,key不允许重复。
collections
是针对集合进行操作的工具类,都是静态方法。