集合
1.Collection接口
-
集合主要是两组(单列集合 , 双列集合)
-
Collection 接口有两个重要的子接口 List Set , 他们的实现子类都是单列集合
-
Map 接口的实现子类 是双列集合,存放的 K-V
collection接口的特点
-
colletion实现子类可以存放多个元素,每个元素可以是Object
-
有些Collection的实现类,可以存放重复的元素,有些不可以
-
有些Collection的实现类,有些是有序的(List),有些不是有序(Set)
-
Collection接口没有直接的实现子类,是通过它的子接口Set 和 List 来实现的
List list = new ArrayList();
// add:添加单个元素
list.add("jack");
list.add(10);//list.add(new Integer(10))
list.add(true);
System.out.println("list=" + list);
// remove:删除指定元素
list.remove(0);//删除第一个元素
list.remove(true);//指定删除某个元素
System.out.println("list=" + list);
System.out.println(list.contains("jack"));//T // contains:查找元素是否存在
System.out.println(list.size());//2 // size:获取元素个数
System.out.println(list.isEmpty());//F // isEmpty:判断是否为空
list.clear();// clear:清空
System.out.println("list=" + list);
// addAll:添加多个元素
ArrayList list2 = new ArrayList();
list2.add("红楼梦");
list2.add("三国演义");
list.addAll(list2);
System.out.println("list=" + list);
// containsAll:查找多个元素是否都存在
System.out.println(list.containsAll(list2));//T
list.add("聊斋");
list.removeAll(list2); // removeAll:删除多个元素
使用迭代器遍历
-
lterator对象称为迭代器,主要用于遍历 Collection 集合中的元素。
-
所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象,即可以返回一个迭代器。
-
lterator 仅用于遍历集合,Iterator 本身并不存放对象。
Collection col = new ArrayList();
col.add(new Book("三国演义", "罗贯中", 10.1));
col.add(new Book("小李飞刀", "古龙", 5.1));
col.add(new Book("红楼梦", "曹雪芹", 34.6));
//1. 先得到 col 对应的迭代器
Iterator iterator= col.iterator();
//使用while循环来遍历
while(iterator.hasNext()){
//返回下一个元素,类型是 Obj
Object next = iterator.next();
System.out.println(next.toString());
}
使用增强for遍历
for (Object book:col) {
System.out.println(book.toString());
}
2.List 接口
List接口是 Collection 接口的子接口
-
List集合类中元素有序(即添加顺序和取出顺序一致)、且可重复
-
List集合中的每个元素都有其对应的顺序索引,即支持索引
List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
常用api
/**
* Object get(int index)//获取指定 index 位置的元素
* int indexOf(Object obj):返回 obj 在集合中首次出现的位置
* int lastIndexOf(Object obj):返回 obj 在当前集合中末次出现的位置
* Object remove(int index):移除指定 index 位置的元素,并返回此元素
* Object set(int index, Object ele):设置指定 index 位置的元素为 ele , 相当替换
* List subList(int fromIndex, int toIndex):返回从 fromIndex 到 toIndex 位置的子集合
*
*/
List 的三种遍历方式
List list = new ArrayList();
list.add(new Book("三国演义", "罗贯中", 10.1));
list.add(new Book("小李飞刀", "古龙", 5.1));
list.add(new Book("红楼梦", "曹雪芹", 34.6));
//1. 迭代器
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
System.out.println(obj);
}
System.out.println("=====增强 for=====");
//2. 增强 for
for (Object o : list) {
System.out.println("o=" + o);
}
System.out.println("=====普通 for====");
//3. 使用普通 for
for (int i = 0; i < list.size(); i++) {
System.out.println("对象=" + list.get(i));
}
ArrayList
ArrayList 的注意事项
-
permits all elements, including null,ArrayList 可以加入null,并且多个
-
ArrayList 是由数组来实现数据存储的[后面老师解读源码]
-
ArrayList 基本等同于Vector,除了ArrayList是线程不安全(执行效率高)看源码.在多线程情况下,不建议使用ArrayList
ArrayList的底层操作机制源码分析)
-
ArrayList中维护了一个Object类型的数组elementData.
transient Objectll elementData;//transient 表示瞬间,短暂的,表示该属性不会被序列号
-
当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第1次添加,则扩容elementData为10,
如需要再次扩容,则扩容elementData为1.5倍。
-
如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍。
Vector集合
-
Vector底层也是一个对象数组,protected Object[] elementData;
-
Vector 是线程同步的,即线程安全,Vector类的操作方法带有synchronized
public synchronized void copyInto(Object[] anArray) {
System.arraycopy(elementData, 0, anArray, 0, elementCount);
}
Vector和ArrayList比较
ArrayList | 可变数组 | jdk1.2 | 线程不安全,效率高 | 如果有参构造1.5倍,如果是无参第一次10,从第二次开始安1.5扩 |
---|---|---|---|---|
Vector | 可变数组 | jdk1.0 | 安全,效率不高 | 如果是无参,默认10,满后,就按2倍扩容,如果指定大小,则每次直接按2倍扩容. |
LinkedList
LinkedList的全面说明
-
LinkedList底层实现了双向链表和双端队列特点
-
可以添加任意元素(元素可以重复),包括null
-
线程不安全,没有实现同步
LinkedList的底层操作机制
-
LinkedList底层维护了一个双向链表.
-
LinkedList中维护了两个属性first和last分别指向 首节点和尾节点
-
每个节点(Node对象),里面又维护了prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个节点。最终实现双向链表.
-
所以LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高。
ArrayList 和 LinkedList 的比较
底层结构 | 增删的效率 | 改查的效率 | |
---|---|---|---|
Arra | 可变数组 | 较低数组扩容 | 较高 |
LinkedList | 双向链表 | 较高,通过链表追加 | 较低 |
如何选择
-
如果我们改查的操作多,选择ArrayList
-
如果我们增删的操作多,选择LinkedList
-
一般来说,在程序中,80%-90%都是查询,因此大部分情况下会选择ArrayList,但是,在一个项目中,根据业务灵活选择,
3.Set 接口
特点:
-
无序,没有索引。
-
不允许重复元素,所以最多包含一个null。
-
和 List 接口一样, Set 接口也是 Collection 的子接口,因此,常用方法和 Collection 接口一样
遍历方式
Set set = new HashSet();
set.add("john");
set.add("lucy");
set.add("john");//重复
set.add("jack");
set.add("hsp");
Iterator iterator=set.iterator();
//迭代器遍历
while (iterator.hasNext()){
Object obj=iterator.next();
System.out.println(obj);
}
//增强for
for (Object objs:set) {
System.out.println(objs);
}
HashSet
-
HashSet实现了Set接口
-
HashSet实际上是HashMap.
public HashSet() { map = new HashMap<>(); }
-
可以存放null值,但是只能有一个null
-
HashSet不保证元素是有序的,取决于hash后,再确定索引的结果.(即,不保证存放元素的顺序 和取出顺序一致)
-
不能有重复元素/对象.在前面Set接口使用已经讲过
HashSet 的底层逻辑
-
HashSet 底层是HashMap
-
添加一个元素时,先得到hash值-会转成->索引值
-
找到存储数据表table,看这个索引位置是否已经存放的有元素
-
如果没有,直接加入
-
如果有,调用equals比较,如果相同,就放弃添加,如果不相同,则添加到最后
-
在Java8中,如果一条链表的元素个数到达TREEIFYTHRESHOLD(默认是8),并且table的大小>=MIN TREEIFY_CAPACITY(默认64),就会进行树化(红黑树)
LinkedHashSet
-
LinkedHashSet 是 HashSet 的子类
-
LinkedHashSet 底层是一个 LinkedHashMap,底层维护了一个 数组+双向链表
-
LinkedHashSet 根据元素的hashCode 值来决定元素的存储位置,同时使用链表维护元素的次序,这使得元素看起来是以插入顺序保存的。
-
LinkedHashSet不允许添重复元素
4.Map 接口
Map 接口实现类的特点
-
Map与Collection并列存在。用于保存具有映射关系的数据:Key-Value
-
Map 中的key 和 value 可以是任何引用类型的数据,会封装到HashMap$Node对象中
-
Map 中的 key 不允许重复
-
Map中的value 可以重复
-
Map 的key 可以为 null,value 也可以为null,注意 key 为null,只能有一个,value 为null,可以多个.
-
常用String类作为Map的key
-
key 和 value 之间存在单向一对一关系,即通过指定的key 总能找到对应的 value
Map相关的Api
Map map = new HashMap();
map.put("李四", new Book("三国演义", "罗贯中", 10.1));
map.put("张三","100");
map.put("李四","120");//此处会替换
System.out.println(map.get("李四"));
// remove:根据键删除映射关系
map.remove(null);
System.out.println("map=" + map);
// get:根据键获取值
Object val = map.get("鹿晗");
System.out.println("val=" + val);
// size:获取元素个数
System.out.println("k-v=" + map.size());
// isEmpty:判断个数是否为 0
System.out.println(map.isEmpty());//F
// clear:清除 k-v
map.clear();
System.out.println("map=" + map);
// containsKey:查找键是否存在
System.out.println("结果=" + map.containsKey("hsp"));//T
Map 接口遍历方法
方式一:
先取出所有的key,然后再获得value值
Map map = new HashMap();
map.put("张三","100");
map.put("李四","120");
//第一组: 先取出 所有的 Key , 通过 Key 取出对应
Set keys=map.keySet();
//第一种遍历方式,增强for
for (Object key:keys) {
System.out.println("key="+key+":"+"value="+map.get(key));
}
System.out.println("----第二种方式迭代器--------");
Iterator iterator = keys.iterator();
while (iterator.hasNext()) {
Object key = iterator.next();
System.out.println(key + "-" + map.get(key));
}
方式二:
把所有的 values 取出来
Collection collections=map.values();
//foreach
for (Object value:collections) {
System.out.println(value);
}
//迭代器
Iterator iterator=collections.iterator();
while (iterator.hasNext()){
Object objs=iterator.next();
System.out.println(objs);
}
方式三:
通过 EntrySet 来获取 k-v
Set entrySet = map.entrySet();// EntrySet<Map.Entry<K,V>>
//(1) 增强 for
for (Object entry : entrySet) {
//将 entry 转成 Map.Entry
Map.Entry m = (Map.Entry) entry;
System.out.println(m.getKey() + "-" + m.getValue());
}
//(2) 迭代器
Iterator iterator = entrySet.iterator();
while (iterator.hasNext()) {
Object entry = iterator.next();
//System.out.println(next.getClass());//HashMap$Node -实现-> Map.Entry (getKey,getValue)
//向下转型 Map.Entry
Map.Entry m = (Map.Entry) entry;
System.out.println(m.getKey() + "-" + m.getValue());
}
HashMap总结
-
Map接口的常用实现类:HashMap、Hashtable和Properties。
-
HashMap是 Map接口使用频率最高的实现类。
-
HashMap 是以key-val 对的方式来存储数据(HashMapSNode类型)
-
key 不能重复,但是值可以重复,允许使用null键和null值。
-
如果添加相同的key,则会覆盖原来的key-val,等同于修改.(key不会替换,val会替换)6)与HashSet一样,不保证映射的顺序,因为底层是以hash表的方式来存储的.(jdk8的hashMap 底层 数组+链表+红黑树)
-
HashMap没有实现同步,因此是线程不安全的,方法没有做同步互斥的操作,没有synchronized
Hashtable
-
存放的元素是键值对:即K-V
-
hashtable的键和值都不能为null,否则会抛出NuPointerException
-
hashTable 使用方法基本上和HashMap一样
-
hashTable 是线程安全的(synchronized),hashMap 是线程不安全的
Properties
-
Properties类继承自Hashtable类并且实现了Map接口,也是使用一种健值对的形式来保存数据。
-
Properties 还可以用于从xcxc.properties 文件中,加载数据到Properties类对象