一、什么是集合
在Java中,集合是用来替换定长数组的一种引用数据类型,在内存中申请一块空间用来存储数据。
集合与数组的区别:
- 长度区别:
- 数组长度固定,定义长了造成内存空间的浪费,定义短了不够用。
- 集合大小可以变,用多少空间拿多少空间。
- 内容区别
- 数组可以存储基本数据类型和引用数据类型,数组中只能存储同一种类型成员。
- 集合中能存储引用数据类型(存储的为对象的内存地址),集合中可以存储不同类型数据(一般情况下也只存储同一种类型的数据)。
集合主要是两种,单列集合与双列集合。
Map是实现的子类是双列集合,存放的是键值对:
Collection接口实现了两个重要的子接口List Set,他们的实现子类都是单列集合:
二、Collection
2.1Collection常用方法:
add方法:
//add增加单个元素
list.add("jack");
list.add(10);
list.add(10);
System.out.println(list);
remove方法:
//remove移除指定元素
list.remove((Integer)10);
System.out.println(list);
//remove移除下标对应的元素
list.remove(0);
System.out.println(list);
contains方法:
//contains:查找元素是否存在
System.out.println(list.contains("jack"));
size方法:
//contains:查找元素是否存在
System.out.println(list.contains("jack"));
isEmpty方法:
//isEmpty:判断元素是否为空
System.out.println(list.isEmpty());
clear方法:
//clear:清空
list.clear();
System.out.println(list.size());
addAll方法:
ArrayList list2 = new ArrayList();
list2.add("jack");
list2.add("Tom");
//传入一个集合
list.addAll(list2);
System.out.println(list);
removeAll方法:
list.removeAll(list2);
System.out.println(list);
2.2Collection接口遍历元素方式
迭代器:
- Iteartor对象被称为迭代器,主要用于遍历Collection集合中的元素。
- 所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个是实现了Iterator接口的对象,即可以返回一个迭代器。
- Iterator只用来遍历集合,本身不存在任何东西。
Collection col = new ArrayList();
col.add(new Book("三国演义","罗贯中",10.1));
col.add(new Book("小李飞刀","古龙",5.1));
col.add(new Book("红楼梦","曹雪芹",34.6));
//先得到col 对应的迭代器
Iterator iterator = col.iterator();
while (iterator.hasNext()) {//返回下一个元素类型是ObjectObject next = iterator.next();System.out.println(next);
}
//当退出while循环之后,iterator迭代器指向最后的元素
//如果希望再次遍历,需要重置迭代器
iterator = col.iterator();
增强for循环:
//增强for循环遍历Collection集合
//增强for底层是迭代器
//增强for可以理解成是简化版本的迭代器遍历
for(Object obj:col){System.out.println(obj);
}
2.4List
List接口是Collection的子接口。
List list = new ArrayList();
//List集合类中的所有元素有序,即添加顺序和取出顺序一致,且可重复
list.add(1);
list.add("Jack");
list.add(2);
list.add("Mary");
System.out.println(list);//List集合中的元素都有其对应的顺序索引,即支持索引
//索引是从0开始的
System.out.println(list.get(3));
2.4.1List的常用方法
add方法:
List list = new ArrayList();
list.add("张三丰");
list.add("贾宝玉");
//void add(int index,Object ele):在index位置插入ele元素
//在index = 1位置插入一个对象
list.add(1,"张无忌");
System.out.println(list);
//boolean addAll(int index,Collection eles):从index位置开始将eles中的所有元素加进来
List list2 = new ArrayList();
list2.add("Jack");
list2.add("Tom");
list.add(1,list2);
System.out.println(list);
get方法:
//Object get(int index):获取指定index位置的元素
System.out.println(list.get(1));
indexOf方法和lastIndexOf方法:
//int indexOf(Object obj):返回obj在集合中首次出现的位置
System.out.println(list.indexOf("贾宝玉"));
//int lastIndexOf(Object obj):返回obj在当前集合中最后一次出现的位置
System.out.println(list.lastIndexOf("张三丰"));
remove方法:
//Object remove(int index):移除指定index位置的元素,并返回此元素
System.out.println(list.remove(0));
System.out.println(list);
set方法:
//Object set(int index, Object ele):设置指定index位置的元素为ele,相当于替换,这个索引必须存在。
list.set(1,"玛丽");
System.out.println(list);
subList方法:
//List subList(int fromIndex,int toIndex):返回从fromIndex到toIndex位置的子集和
//返回的子集和是左闭右开的
List returnlist = list.subList(0, 1);
System.out.println(returnlist);
List的三种遍历方式:
//1.迭代器
Iterator iterator = list.iterator();
while(iterator.hasNext()) {System.out.println(iterator.next());
}
//2.增强for
for(Object object : list) {System.out.println(object);
}
//3.普通for循环
for (int i = 0; i < list.size(); i++) {System.out.println(list.get(i));
}
2.4.2ArrayList
- ArrayList中维护了一个Object类型的数组elementData。
- 当创建ArrayList对象时,如果使用无参构造器,最初的容量为0,第一次添加,则扩容elementData为10,如需要再次扩容,则扩容elementData为1.5倍。
- 如果使用的是指定大小的构造器,则初始elementData的容量为指定大小,如果需要扩容则直接扩容为elementData的1.5倍。
2.4.3Vector
- Vector的底层也是一个对象数组。
- Vector是线程安全的。
- 如果是无参构造,默认容量为10,如需再次扩容,按2倍扩容。
2.4.4LinkedList
- LinkedList底层维护了一个双向链表。
- LinkedList中维护了两个属性first和last分别指向首节点和尾节点。
- 每个节点,里面又维护了prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个节点。
- LinkedList中元素的插入和删除,不通过数组来实现,效率较高。
2.5Set
- Set是无序的,即添加和取出的顺序不一致,并且没有索引。
- 不允许重复元素,最多包含一个null。
2.5.1HashSet
HashSet是Set接口的一个常见实现类,它基于哈希表实现,可以提供快速的插入、删除和查找操作,HashSet本质是一个HashMap。
Set<String> fruits = new HashSet<>();
//add方法添加向HashSet中添加元素
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Orange");
fruits.add("Apple"); // 重复元素,不会被添加System.out.println("Fruits: " + fruits);//remove从HashSet中移除元素
fruits.remove("Banana");
System.out.println("Fruits after removal: " + fruits);//contains方法检查是否包含指定元素
boolean containsApple = fruits.contains("Apple");
System.out.println("Contains Apple: " + containsApple);
HashSet底层剖析:
- 添加一个元素时,根据哈希函数得到哈希值,也就是索引值。
- 找到哈希表,看这个索引值位置是否已经存放元素。
- 如果没有直接加入。
- 如果又,调用equals方法比较,如果相同就放弃添加,如果不相同就添加到该索引值对应的链表的最后。
- 在Java8中,如果一条链表的元素个数超过TREEIFY_THRESHOLD,并且table的大小>=MIN_TREEIFY_CAPACITY就会进行树化(红黑树)。
2.5.2LinkedHashSet
- LinkedHashSet是HashSet的子类。
- LinkedHashSet的底层是一个LinkedHashMap,底层维护了一个数组+双向链表。
- LinkedHashSet根据元素的hashCode值来决定元素的存储位置,同时使用链表来维护元素的次序,这使得元素看起来是以插入顺序保存的。
- LinkedHashSet不能插入重复元素。
三、Map接口
3.1Map
- Map与Collection并列存在,用于保存具有映射关系的数据Key-Value。
- Map中的Key和Value可以是任何引用类型的数据,会封装到HashMap$Node对象中。
- Map中的Key可以为null,但是不能重复。
- Map中的Value可以为null,允许重复。
- Key与Value存在单向一对一关系,通过指定的Key总能找到对应的Value,同一个Key对应的Value会进行覆盖。
3.2Map接口常用方法
- put方法:添加键值对。
- remove方法:根据键删除映射关系。
- get方法:根据键获取值。
- size方法:获取元素个数。
- isEmpty方法:判断元素个数是否为空。
- clear方法:清空Map。
- containsKey方法:查看键是否存在。
3.3Map接口遍历方式
使用KeySet方法:
//第一组:先取出所有的Key,通过Key取出对应的Value
Set keyset = map.keySet();//1.增强for
for (Object key : keyset) {System.out.println(key+":"+map.get(key));
}
//2.迭代器
Iterator it = keyset.iterator();
while (it.hasNext()) {Object key = it.next();System.out.println(key+":"+map.get(key));
}
使用values方法:
//第二组:取出所有的Value
Collection values = map.values();//增强for
for (Object value : values) {System.out.println(value+":"+map.get(value));
}Iterator iterator = values.iterator();
while (iterator.hasNext()) {Object value = iterator.next();System.out.println(value+":"+map.get(value));
}
使用EntrySet方法:
//通过EntrySet获取Key——Value
Set entryset = map.entrySet();
//增强forfor(Object entry:entryset){Map.Entry entry1 = (Map.Entry) entry;System.out.println(entry1.getKey()+":"+entry1.getValue());
}//迭代器
Iterator iterator1 = map.entrySet().iterator();
while (iterator1.hasNext()) {Map.Entry next = (Map.Entry)iterator1.next();System.out.println(next.getKey()+":"+next.getValue());
}
3.4HashMap
- HashMap是Map接口使用频率最高的实现类。
- HashMap是以Key-Value对的方式来存储数据的。
- Key不能重复,但是Value可以重复,允许使用null键和null值。
- 如果添加相同的Key,会覆盖原来的Key-Value,等同于修改。
- 与HashSet一样,不能保证映射的顺序,因为底层是以Hash表的方式存储的。
- HashMap没有实现同步,是线程不安全的,没有同步互斥操作。
底层机制:
- HashMap底层维护了Node类型的数组table,默认为null。
- 当创建对象时,将加载因子初始化为0.75。
- 当添加Key-Value时,通过Key的哈希值得到在table处的索引,然后判断该索引处是否有元素,如果没有元素直接添加,如果有元素,判断该元素的Key是否和准备加入的元素Key相等,如果相等则替换Value,如果不相等需要判断是链表结构还是树结构,做出相应处理,添加时如果容量不够,则需要扩容。
- 第一次添加后,需要扩容table容量为16,临界值为12。
- 以后再扩容,则扩容为原来的两倍,临界值为原来的两倍。
- 一条链表的元素超过TREEIFY_THRESHOLD,table的大小>=MIN_TREEIFY_CAPACITY,就会进行树化。
3.5Hashtable
- 存放的元素是键值对:Key-Value。
- Hashtable的键和值都不能为null。
- Hashtable使用方法和HashMap基本一样。
- Hashtable是线程安全的,HashMap线程不安全。
3.6Properties
- Properties继承Hashtable。
- 通过Key-Value存放数据,当然Key和Value不能为null。
- Properties可用于从xxx.properties文件中,加载数据到Properties类对象中。
四、集合选型规则
- 先判断存储的类型,是一组对象或者一组键值对。
- 一组对象:
- 允许重复:List
- 增删多:LinkedList,底层维护了一个双向链表。
- 改查多:ArrayList,底层维护了Object类型的可变数组。
- 不允许重复:Set
- 无序:HashSet,底层是一个HashMap,维护了一个哈希表。
- 排序:TreeSet。
- 插入和取出顺序一致:LinkedHashSet,底层维护了数组+双向链表。
- 允许重复:List
- 一组键值对:
- 键无序:HashMap,底层是哈希表。
- 键排序:TreeMap。
- 键插入顺序和取出顺序一致:LinkedHashMap.
- 读取文件:Properties。
五、Collection工具类
Collections中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作。
5.1顺序相关
reverse方法:
//reverse(List):反转List中元素的顺序
Collections.reverse(list);
System.out.println(list);
shuffle方法:
//shuffle(List):对List集合元素进行随机排序
Collections.shuffle(list);
System.out.println(list);
sort方法:
//sort(List):自然排序
Collections.sort(list);
System.out.println(list);//sort(List,Comparator):自定义排序
Collections.sort(list, new Comparator(){public int compare (Object o1, Object o2){return ((String)o2).length() - ((String)o1).length();}
});
System.out.println(list);
swap方法:
//swap(List,start,end):交换下标为start和end的内容
Collections.swap(list,1,3);
System.out.println(list);
5.2查找、替换
max方法:
//Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
System.out.println(Collections.max(list));//Object max(Collection,Comparator):根据Comparator给定的顺序,返回最大值
System.out.println(Collections.max(list,new Comparator() {public int compare(Object o1, Object o2) {return ((String)o1).length() - ((String)o2).length();}
}));
min方法:
//Object min(Collection):根据元素的自然顺序,返回给定集合中的最小元素
System.out.println(Collections.min(list));//Object min(Collection,Comparator):根据Comparator给定的顺序,返回最小值
System.out.println(Collections.min(list,new Comparator() {public int compare(Object o1, Object o2) {return ((String)o1).length() - ((String)o2).length();}
}));
frequcency方法:
//int frequency(Collection,Object):返回指定集合中指定元素出现的次数
System.out.println(Collections.frequency(list,"tom"));
copy方法:
//void copy(List dest, List src):将src中的内容复制到dest中
List dest = new ArrayList();
for (int i = 0; i < list.size(); i++) {dest.add("");
}
Collections.copy(dest,list);
System.out.println(dest);
replace方法:
//boolean reaplaceAll(List list, Object oldval, Object newval)
//使用新值替换List对象中的所有旧值
Collections.replaceAll(list,"tom","汤姆");
System.out.println(list);