整体上分为两大接口:Collection和Map,一个是元素集合,一个是键值对集合
List,Set,Map三者的区别:
- List(对付顺序的好帮⼿): List接⼝存储⼀组不唯⼀(可以有多个元素引⽤相同的对象),有序的对象
- Set(注重独⼀⽆⼆的性质): 不允许重复的集合。不会有多个元素引⽤相同的对
- Map(⽤Key来搜索的专家): 使⽤键值对存储。Map会维护与Key有关联的值。两个Key可以引⽤相同的对象,但Key不能重复,典型的Key是String类型,但也可以是任何对象。
一、Collection集合框架
1、通用方法
- boolean add(E e)确保此 collection 包含指定的元素(可选操作)。
- void clear()移除此 collection 中的所有元素(可选操作)。
- boolean remove(Object o)从此 collection 中移除指定元素的单个实例,如果存在的话(可选操作)。
- boolean contains(Object o)如果此 collection 包含指定的元素,则返回 true
- boolean isEmpty()如果此 collection 不包含元素,则返回 true。
- int size()返回此 collection 中的元素数。
- Object[] toArray()返回包含此 collection 中所有元素的数组。
public static void main(String[] args) {
Collection coll=new ArrayList();//多态
System.out.println(coll);//重写toString
coll.add(1);coll.add("hello...");coll.add("张三"); //自动装箱
System.out.println("isEmpty:"+coll.isEmpty()); //isEmpty:false
System.out.println("size:"+coll.size()); //size:3
System.out.println("remove 1:"+coll.remove(1)); //remove 1:true
System.out.println("remove 2:"+coll.remove(2)); //remove 2:false容器中不包含2,移除失败
System.out.println("size:"+coll.size()); //size:2
System.out.println("contains:"+coll.contains("张三")); //contains:true
Iterator it=coll.iterator();//定义迭代器
while(it.hasNext()) {
System.out.print(it.next());//遍历输出容器元素hello...张三
}
Object [] array=coll.toArray();
System.out.println("\n"+Arrays.toString(array));//[hello..., 张三]
coll.clear();
System.out.println("size:"+coll.size()); //size:0
}
2、分类
(1).List
一个单列集合
1.元素有序(存储的顺序和取出的顺序一致)
2.允许有重复的元素
3.有大量带索引的方法
常用方法:
void add(String item)向滚动列表的末尾添加指定的项。
E get(int index)返回此列表中指定位置上的元素。
void remove(int position)从此滚动列表中移除指定位置处的项。
E set(int index, E element)用指定的元素替代此列表中指定位置上的元素。
实现类:
ArrayList(没有特有方法)
- 底层是数组 object [] elementData,底层实现了对数组的动态扩容——动态数组
- 查询快——随机访问(根据下标);增删慢
public static void main(String[] args) {
ArrayList<String>array=new ArrayList();
array.add("A");array.add("B");array.add("C");//继承自collection的方法
System.out.println(array);//[A, B, C]
array.add(1, "F");//在指定位置添加元素
System.out.println(array.get(1));//F
array.remove(0); //在指定位置删除元素
System.out.println(array);//[F, B, C]
System.out.println(array.set(0, "A"));//修改元素,返回值为被修改前的元素值:F
System.out.println(array);//[A, B, C]
}
LinkedList
- 底层链表实现
- 增删快,查询慢
- 特有方法:(主要可以操作首尾来操作堆栈)
addFirst(E e)将指定元素插入此列表的开头;addLast(E e)将指定元素添加到此列表的结尾。
getFirst()返回此列表的第一个元素;getLast()返回此列表的最后一个元素。
removeFirst()移除并返回此列表的第一个元素;removeLast()移除并返回此列表的最后一个元素。
pop()从此列表所表示的堆栈处弹出一个元素。换句话说,移除并返回此列表的第一个元素。
push(E e)将元素推入此列表所表示的堆栈。换句话说,将该元素插入此列表的开头。
public static void main(String[] args) {
LinkedList<String>list=new LinkedList();
//模拟栈:后进先出
list.push("A");list.push("B");list.push("C");//压栈
System.out.println(list);//[C, B, A]
System.out.println(list.pop());//弹栈:删除栈顶元素并返回其值:C
list.clear();;
//模拟队列:先进先出
list.addLast("a");list.addLast("b");list.addLast("c");//入队
System.out.println(list);//[a, b, c]
System.out.println(list.removeFirst());//出队:删除队首元素并返回其值:a
}
Vector
线程安全,基本和ArrayList一致(可自动增长的对象数组,大部分都是同步方法,性能损耗,不推荐使用)
(2).Set
存储单列的数据
1.数据不允许重复
2.数据无序(存入和取出顺序不一致)
实现类:
HashSet
- HashCode:散列表(散列函数f(x)–给定一个元素会算出一串值,这个值和数组下标对应)
- JDK1.8之前java散列表通过数组和链表实现
- 1.任意一个对象通过hash函数计算出一个hashcode(和散列表中数组下标相对应)
2.如果一个元素计算出的hashcode位置没有元素,则直接存在该位置上,如果已经存在元素,则通过equals判断是否为同一个元素,如果不是则通过链表连接
(注意:通过Set存储自定义对象时,要根据自身需要来重写hashcode和equals方法(成套出现))
HashSet<String>set=new HashSet();
set.add("A");set.add("B");set.add("C");
System.out.println(set);//[A, B, C]
set.add("A");System.out.println(set);//[A, B, C]
LinkedHashSet
相对于Hashset会维护其顺序
TreeSet
会将元素进行排序
TreeSet<String>set=new TreeSet();
set.add("aac");set.add("aaa");set.add("aab");
System.out.println(set);//[aaa, aab, aac]
3、遍历
Collection coll=new ArrayList();
coll.add("hello");coll.add(3);coll.add("张三");
Iterator it=coll.iterator();//获取Iterator对象
while(it.hasNext()) {//判断是否有下一个元素
System.out.print(it.next());
}//hello3张三
System.out.println();
for(Object obj:coll) {//通过增强for循环进行集合遍历
System.out.print(obj);
}//hello3张三
二、Map集合
1、存储的是一个双列的数据模型(每一个元素都是两个对象)
2、所存储的两个对象称作键值对,同一个Map对象中键是唯一的,值可以重复
1、通用方法
V put(K key, V value) : 把指定的键与指定的值添加到Map集合中。
V remove(Object key) : 把指定的键K所对应的键值对元素在Map集合中删除,返回被删除元素的V值。
V get(Object key) 根据指定的键,在Map集合中获取对应的值V。
boolean containsKey(Object key)判断指定key在map中是否存在
public static void main(String[] args) {
Map<String,String> map=new HashMap();//通过泛型指定类型时需要同时指定key、Value的类型
//put:如果存在K-V,K重复则替换原有K-V并返回原有Value,否则返回null
System.out.println(map.put("ABC", "abc"));//null存值:一一对应关系
map.put("EDF", "edf");map.put("GHI", "ghi");
System.out.println(map.put("ABC", "123"));//abc:K重复返回原有V
System.out.println(map); //{ABC=123, EDF=edf, GHI=ghi}
//get:返回K对应V,否则返回null;
System.out.println(map.get("ABC")); //123
//remove:移除K-V,移除成功返回其V,否则返回null
System.out.println(map.remove("ABC")); //123
System.out.println(map.remove("ABC")); //null:上一步已经移除成功
System.out.println(map); //{EDF=edf, GHI=ghi}
//containsKey 判断指定key在map中是否存在
System.out.println(map.containsKey("EDF")); //true
}
2、遍历
1.通过Key找值的方式进行遍历(Set keySet()返回此映射中包含的键的 Set 视图。)
(1)通过keySet将一个map对象中所有的键转化为一个Set集合
(2)不断遍历Set集合通过get方法取出Value值
2.通过Entry类进行遍历
Set<Map.Entry<K,V>> entrySet()返回此映射中包含的映射关系的Set视图。
public static interface Map.Entry<K,V>映射项(键-值对)。
(1)将每一个K-V键值对称为一个entry
(2)entrySet将map中所有的entry转为一个Set集合
(3)Entry类中提供了2个方法 getKey获取所对应的entry键,getValue获取所对应entry的值
public static void main(String[] args) {
Map<String,String> map=new HashMap();
map.put("ABC", "abc");map.put("EDF", "edf");map.put("GHI", "ghi");
// 1.通过Key找值的方式进行遍历
Set<String> set1=map.keySet();
/*Iterator<String> it=set1.iterator();//通过迭代器方式
while(it.hasNext()) {
String key=it.next();
String value=map.get(key);
System.out.print(key+"="+value+" ");
}*/
for(String key:set1) {//通过增强for循环方式
System.out.print(key+"="+map.get(key)+" ");
}//ABC=abc EDF=edf GHI=ghi
//2.通过Entry类进行遍历
Set<Map.Entry<String, String>> set2=map.entrySet();
Iterator<Map.Entry<String, String>> it2= set2.iterator();
/*while(it2.hasNext()){
Map.Entry<String, String> entry = it2.next();
System.out.print(entry.getKey()+"="+entry.getValue()+" ");
}*/
for(Map.Entry<String, String> entry:set2){
System.out.print(entry.getKey()+"="+entry.getValue()+" ");
}
}
3、底层原理
Map底层通过hash表进行数据存储:
JDK1.8之前通过数组+链表的方式实现,提高查询效率。
之后通过数组+链表+红黑树:当链表长度大于8的时候由链表转为红黑树,进一步提高效率
1、存值
-
根据存K-V键值对中的K对象,通过hash函数计算出一个hashCode(是和hash表中数组的下标某个值对应)
-
如果对应下标上没有K-V(entry)则直接放上去
-
如果对应下标位置已经有K-V键值对,则通过equals方法进行比较是否为同一个对象
(3.1)如果不是同一个对象,则在链表后加上新的K-V键值对
(3.2)如果是同一个对象,则将原有键值对覆盖 -
一旦某个链表长度大于8的时候转为红黑树大大提高查找的速度
2、取值
(1)通过key计算出对应hashCode
(2)在对应hashCode位置上找元素,判断是否有链表(或者红黑树)存在
(2.1)如果不存在,则直接返回对应位置的Value对象
(2.2)如果存在,则根据equals进行比较,比较成功则返回对应Value
(Map存储自定义对象时,需要对其hashcode和equals方法重写)
4、实现类:
- HashMap:无序,线程不安全
- LinkedHashMap:有序
- TreeMap:
- HashTable:(线程安全,很少使用) HashMap 是⾮线程安全的,HashTable 是线程安全的;HashTable 内部的⽅法基本都经过 synchronized 修饰。(如果要保证线程安全的话推荐使⽤ ConcurrentHashMap);
三、总结
Collection
- List
Arraylist: Object数组
Vector: Object数组
LinkedList: 双向链表(JDK1.6之前为循环链表,JDK1.7取消了循环) - Set
HashSet(⽆序,唯⼀): 基于 HashMap 实现的,底层采⽤ HashMap 来保存元素
LinkedHashSet: LinkedHashSet 继承于 HashSet,并且其内部是通过 LinkedHashMap 来实现的。有点类似于我们之前说的LinkedHashMap 其内部是基于 HashMap 实现⼀样,不过还是有⼀点点区别的
TreeSet(有序,唯⼀): 红⿊树(⾃平衡的排序⼆叉树)
Map
- HashMap: JDK1.8之前HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突⽽存在的(“拉链法”解决冲突)。JDK1.8以后在解决哈希冲突时有了⼤的变化,当链表⻓度⼤于阈值(默认为8)时,将链表转化为红⿊树,以减少搜索时间
- LinkedHashMap: LinkedHashMap 继承⾃ HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红⿊树组成。另外,LinkedHashMap 在上⾯结构的基础上,增加了⼀条双向链表,使得上⾯的结构可以保持键值对的插⼊顺序。同时通过对链表进⾏相应的操作,实现了访问顺序相关逻辑
- Hashtable: 数组+链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突⽽存在的
- TreeMap: 红⿊树(⾃平衡的排序⼆叉树)