集合的主要结构:
1. Collection 接口
单列集合
1.1 Collection 集合的方法
1)添加
public void testAdd(){
Collection coll = new ArrayList();
// 1.添加一个元素
coll.add("chengyu");
coll.add(true);
System.out.println(coll); // [chengyu, true]
// 2.添加集合(一组元素)
Collection coll1 = new ArrayList();
coll1.add("chenglong");
coll1.add(100);
coll.addAll(coll1);
System.out.println(coll); // [chengyu, true, chenglong, 100]
// 3.添加类对象
Person per = new Person("chengyu",30);
coll.add(per);
System.out.println(coll); // [chengyu, true, chenglong, 100, Person{name='chengyu', age=30}]
}
2)删除
public void testRemove(){
Collection coll = new ArrayList();
coll.add("chengyu");
coll.add(20);
System.out.println(coll); // [chengyu, 20]
// 1.remove
boolean boo = coll.remove("chengyu"); // 注意:移除类对象要重写equals
// 是否移除
System.out.println(boo); // true
System.out.println(coll); // [20]
// 2.removeAll
Collection coll1 = new ArrayList();
coll1.add("chengyu");
coll1.add(20);
coll1.add(40);
System.out.println(coll1); // [chengyu, 20, 40]
Collection coll2 = new ArrayList();
coll2.add("chengyu");
coll2.add(20);
coll1.remove(coll2); // 不起作用
System.out.println(coll1); // [chengyu, 20, 40]
coll1.removeAll(coll2);
System.out.println(coll1); // [40]
// 3.retainAll(移除不同部分,返回交集)
Collection coll3 = new ArrayList();
coll3.add("chengyu");
coll3.add(20);
coll3.add(40);
Collection coll4 = new ArrayList();
coll4.add(20);
coll4.add(40);
coll3.retainAll(coll4);
System.out.println(coll3); // [20, 40]
}
3)包含
public void testContains(){
Collection coll = new ArrayList();
coll.add("chengyu");
coll.add(20);
Person per = new Person("chengyu",30);
coll.add(per);
// 是否包含单个元素
System.out.println(coll.contains("chengyu")); // true
// 是否包含类对象(同一对象)
System.out.println(coll.contains(per)); // true
// 是否包含类对象(新建对象)
System.out.println(coll.contains(new Person("chengyu",30))); // 没有重写Person 中的equals 方法时为false,重写后为true
// 是否包含形参中所有内容
Collection coll1 = new ArrayList();
coll1.add("chengyu");
coll1.add(20);
System.out.println(coll.containsAll(coll1)); // true
}
4)相等
public void testEquals(){
Collection coll = new ArrayList();
coll.add("chengyu");
coll.add(20);
Collection col2 = new ArrayList();
col2.add("chengyu");
col2.add(20);
// 集合中每个元素是否相等
System.out.println(coll.equals(col2)); // true
}
5)转换
public void testTransfor(){
Collection coll = new ArrayList();
coll.add("chengyu");
coll.add(20);
// 集合转数组
Object[] objects = coll.toArray();
for (int i = 0; i < objects.length; i++) {
System.out.println(objects[i]); // chengyu // 20
}
// 数组转集合
List<String> list = Arrays.asList(new String[]{"a","b","c"});
System.out.println(list); // [a, b, c]
// 注意:基本类型数组转集合时,即使多个元素也会被看作一个元素
List list1 = Arrays.asList(new int[]{111,222});
System.out.println(list1); // [[I@32a1bec0]
}
6)遍历
public void testIterator(){
// 集合元素的遍历操作(迭代器)
Collection coll = new ArrayList();
coll.add("chengyu");
coll.add(20);
Iterator iterator = coll.iterator();
// 迭代
while (iterator.hasNext()){
Object obj = iterator.next();
System.out.println(obj);
// 移除数据
if("chengyu".equals(obj)){
iterator.remove();
}
}
// 重新遍历集合
Iterator iterator1 = coll.iterator();
while (iterator1.hasNext()){
System.out.println(iterator1.next());
}
// Java5.0 用于遍历结合和数组,内部还是迭代器
Collection coll2 = new ArrayList();
coll2.add("chengyu");
coll2.add(20);
for (Object obj : coll2) {
System.out.println(coll2);
}
}
7)其他
public void testOthers(){
Collection coll = new ArrayList();
coll.add("chengyu");
coll.add(20);
// 1.元素个数
System.out.println(coll.size());
// 2.是否为空
System.out.println(coll.isEmpty());
// 3.清空
coll.clear();
System.out.println(coll.isEmpty());
// 4.返回当前对象的hashcode,
System.out.println(coll.hashCode());
}
}
1.2 List 接口(Java 1.2)
有序,可重复。“动态数组”;
List 接口是Collection 的一个子接口,所以Collection 中的方法都可以使用,由于List 是有序的,相对与Collection 增加一些索引方法;(Set无序列,所以Collection 不包含索引部分操作)
常用方法:
public void test01(){
ArrayList list = new ArrayList();
list.add("a");
list.add("b");
list.add(1);
list.add(2);
System.out.println(list); // [a, b, 1, 2]
// 1.1 添加一个元素
list.add(2,"c");
System.out.println(list); // [a, b, c, 1, 2]
// 1.2 添加一组元素
ArrayList list1 = new ArrayList();
list1.add(3);
list1.add(4);
list.addAll(list);
System.out.println(list); // [a, b, c, 1, 2, a, b, c, 1, 2]
// 2.获取
System.out.println(list.get(0)); // a
// 3.1 查询 首次出现位置(不存在返回 -1)
System.out.println(list.indexOf("c")); // 2
// 3.2 查询 末次出现位置(不存在返回 -1)
System.out.println(list.lastIndexOf("c")); // 7
// 4.1 删除 按索引
Object remove = list.remove(0);
System.out.println(remove); // 返回被删除的对象 a
System.out.println(list); // [b, c, 1, 2, a, b, c, 1, 2]
// 4.1 删除 按对象(基本类型需要转包装类)
boolean remove1 = list.remove(new Integer(1));
System.out.println(remove1); // 对象是否删除 true
System.out.println(list); // [b, c, 2, a, b, c, 1, 2]
// 5. 修改
list.set(0,"a");
System.out.println(list); // [a, c, 2, a, b, c, 1, 2]
// 6. 获取子集合(左闭又开)
List list2 = list.subList(1, 5);
System.out.println(list2); // [c, 2, a, b]
}
1.2.1 List 实现类 ArrayList(重点 Java 1.2)
ArrayList:List 接口的主要实现类,线程不安全的,执行效率高;底层使用Object[]存储(顺序存储)
JDK 7:初始化时,创建长度为10 的Object 数组;扩容1.5倍;(饿汉式)
建议开发中使用带参的构造器;
JDK 8:初始化为{},赋值时设置长度,没定义长度,赋值时初始长度10;(懒汉式,延迟了数组创建,节省内存)
1.2.2 List 实现类 LinkedList(Java 1.2)
LinkedList:对于频繁的插入和删除操作,效率比ArrayList 效率高;底层使用双向链表(左地址 | 数值 | 右地址)存储
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
1.2.3 List 实现类 Vector(了解 Java 1.1)
Vector:List 接口的古老实现类,线程安全的,效率低;底层使用Object[]存储
1.3 Set 接口(了解)
无序,不可重复
与List 接口相同,Set 接口也是Connection 的一个子接口;
Set 接口中没有额外定义方法;
1.3.1 Set 实现类 HashSet
Set 接口的主要实现类,线程不安全的,可以存储null 值,实现的是HashMap(),底层为数组;
1)无序性:表现为向底层数组中存值时,不是按照索引顺序存储,而实根据hash 值进行存储;要注意的是,无序性并不表示遍历结果随机输出!
2)不可重复性:添加的元素按照equals() 判断时,不能返回true,即相同元素只能添加一个;
hash 值是根据对象属性计算出的值,根据这个值决定在数组中存放的位置,
存放的位置说明:
通过散列函数将hash 值映射到数组的索引上,从而将数据存储到指定的索引位置中,当出现索引值相同,但hash 值不同时,再使用链表存储方式进行存储,链表地址指向问题JDK版本不同略有不同(七上八下);若hash 值相同,再进行equals() 方式比较,若返回true 则表名值一样,若返回false 则按照链表存储。
总结:存储对象 ⇒ 计算hash 值 ⇒ 映射索引值 ⇒ 索引处是否为空 ⇒ hash 值是否相同 ⇒ equals() 比较
为什么要重写Set 中添加对象的hashCode() 和equals():
Object 中的hashCode() 是随机计算出的一个值作为内存存放地址,即使内容相同,hash 值却不同,所以要重写hashCode() ;
Object 中的equals() 方法比较的是内存地址,而不是对象内容,所以也要重写;
重写的 hashCode() 和equals() 尽可能保持一致性(自动生成即可),即相等的对象必须具有相等的散列码;
1.3.2 HashSet 的子类LinkedHashSet
在HashSet 基础上,添加数据的同时,在每个数据中添加前后地址(链式),看似有序,遍历时可以按照添加顺序进行遍历;
对于频繁的遍历操作效率高于HashSet;
1.3.3 TreeSet
可以按照添加对象的指定属性进行排序,二叉树存储;
1)TreeSet 中添加的数据要求是相同类的对象(才可能有相同属性);
2)两种排序方式:自然排序(Comparable)、定制排序(Comparator)
注意:若对象中指定的排序对象相同,其他内容不同,也认为两个对象相同,使用时需要注意!
public void test01(){
TreeSet treeSet = new TreeSet();
treeSet.add(5);
treeSet.add(100);
treeSet.add(-1);
Iterator iterator = treeSet.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
自动排序输出:
-1
5
100
排序对象时,需要对象内部使用比较器(Comparable、Comparator)
public void test02(){
TreeSet treeSet = new TreeSet();
treeSet.add(new User("chengyu",30));
treeSet.add(new User("zhaoyun",20));
treeSet.add(new User("liubei",55));
Iterator iterator = treeSet.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
class User implements Comparable<User>{
private String name;
private int age;
public User() {
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(User user) {
return this.name.compareTo(user.name);
// 可多级排序
}
}
2. Map 接口(Java 1.2)
双列集合,键值对(key - value)
Map 中的key:无序,不可重复,使用Set 存储;
Map 中的value:无序,可重复,使用Collection 存储;
一个键值对构成了一个Entry 对象,无序,不可重复,使用Set 存储;
2.1 Map 的常用方法
public void test01(){
Map map = new HashMap();
// 1.1 添加(单个元素)
map.put("A",123);
map.put("B",456);
System.out.println(map); // {A=123, B=456}
// 1.2 添加(Map)
Map map1 = new HashMap();
map1.put("C",789);
map1.put("D",135);
map.putAll(map1);
System.out.println(map); // {A=123, B=456, C=789, D=135}
// 2.修改
map.put("A",789);
System.out.println(map); // {A=789, B=456, C=789, D=135}
// 3.移除
Object removeValue = map.remove("D");
System.out.println(removeValue); // 135,移除方法返回的是移除对象的value
System.out.println(map); // {A=789, B=456, C=789}
// 4.长度
System.out.println(map.size()); // 3
// 5.是否包含指定的Key
System.out.println(map.containsKey("A")); // true
// 6.是否包含指定的Value
System.out.println(map.containsValue(789)); // true
// 7.清空
map.clear();
System.out.println(map); // {}
// 8.是否为空
System.out.println(map.isEmpty()); // true
}
public void test02(){
// 遍历
Map map = new HashMap();
map.put("A",123);
map.put("B",456);
map.put("C",1);
// 遍历Key
Set set = map.keySet();
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
// 遍历Vaule
Collection values = map.values();
for (Object obj:values) {
System.out.println(obj);
}
// 遍历所有的 Key - Value (方法一)
Set set1 = map.entrySet();
Iterator iterator1 = set1.iterator();
while (iterator1.hasNext()){
Object next = iterator1.next();
// Map集合中的元素都是 Entry
Map.Entry entry = (Map.Entry) next;
System.out.println(entry.getKey() + " ===> " + entry.getValue());
}
// 遍历所有的 Key - Value (方法二)
Set set2 = map.keySet();
Iterator iterator2 = set.iterator();
while (iterator2.hasNext()){
Object key = iterator2.next();
Object value = map.get(key);
System.out.println(key + " ===>>> " + value);
}
}
2.2 HashMap(重要 Java 1.2)
Map 的主要实现类,线程不安全的,效率高; 可以存储null 的key 和value;
底层:
数组 + 链表 (jdk1.7及之前)
数组 + 链表 + 红黑树(jdk1.8)
key:无序,不可重复,使用Set 存储,所以存储对象时,要求对象类必须重写equals() 和 hashcode();
value:无序,可重复,使用Collection 存储,所以存储对象时,要求重写
equals() ;
底层实现原理:
jdk 7:数组 + 链表
HashMap map = new HashMap();
实例化后底层创建长度为16 的数组,类型为Entry[] table;
map.put(key1,value1);
首先调用key1 所在类的hashCode(),计算hash 值,次hash 值经过某种计算以后,得到Entry[] 的存放位置,如果此位置有值,比较hash 值,如果hash 值存在相同,调用key1 所在类的equals 方法,返回true,使用value1 替换相同key的value值(覆盖);
注意:数组指定位置为空,线式存储,若有值,进行链式存储!
加载因子(loadFactor):0.75
临界值(threshold):16 * 0.75 = 12,超过此数值时(且要存放的位置不为空),进行2倍扩容;扩容时,进行复制操作,从新计算再存储,原本链式存储也有可能全部变为线式存储,比较消耗资源;
// TODO 看源码
jdk 8:数组 + 链表 + 红黑树
new HashMap(); 时,底层没有创建长度为16 的数组,底层数组为Node类型数组,而非Entry[]
首次调用put() 时,底层创建长度为16 的数组;
当数组某位置上以链表形式存储的数据个数 > 8 且 当前数组长度 > 64 时,次索引位置上的所有数据改为使用红黑树存储;(提升查找速度)
// TODO 看源码
2.3 HashMap 子类LinkedHashMap(了解 Java 1.4)
HashMap 基础上,添加前后地址存储,保证元素可以按照添加时的顺序实现遍历;
对于频繁的遍历操作,效率高于HashMap,除此之外都使用HashMap;
2.4 TreeMap(Java 1.2)
有序的键值对存储
保证按照添加的key-value 对进行排序,实现排序遍历,具体是按照key 进行排序(自然排序或定制排序),所以要求key 必须是由同一个类型创建的对象;底层使用红黑树;
自然排序:
public void test03(){
TreeMap treeMap = new TreeMap();
treeMap.put(new User("chengyu",30),100);
treeMap.put(new User("zhaoyun",20),50);
treeMap.put(new User("liubei",55),200);
Set set1 = treeMap.entrySet();
Iterator iterator1 = set1.iterator();
while (iterator1.hasNext()){
Object next = iterator1.next();
// Map集合中的元素都是 Entry
Map.Entry entry = (Map.Entry) next;
System.out.println(entry.getKey() + " ===> " + entry.getValue());
}
}
User{name=‘chengyu’, age=30} ===> 100
User{name=‘liubei’, age=55} ===> 200
User{name=‘zhaoyun’, age=20} ===> 50
定制排序:(根据年龄)
public void test03(){
TreeMap treeMap = new TreeMap(new Comparator<User>() {
@Override
public int compare(User user1, User user2) {
return Integer.compare(user1.getAge(),user2.getAge());
}
});
treeMap.put(new User("chengyu",30),100);
treeMap.put(new User("zhaoyun",20),50);
treeMap.put(new User("liubei",55),200);
Set set1 = treeMap.entrySet();
Iterator iterator1 = set1.iterator();
while (iterator1.hasNext()){
Object next = iterator1.next();
// Map集合中的元素都是 Entry
Map.Entry entry = (Map.Entry) next;
System.out.println(entry.getKey() + " ===> " + entry.getValue());
}
}
User{name=‘zhaoyun’, age=20} ===> 50
User{name=‘chengyu’, age=30} ===> 100
User{name=‘liubei’, age=55} ===> 200
User 类参照 1.3.3
2.5 Hashtable(Java 1.0)
古老实现类,线程安全,效率低;不可以存储null 的key 和value;
2.6 Hashtable 子类Properties
常用于处理配置文件,key 和value 都是String 类型
public static void main(String[] args) {
FileInputStream fls = null;
try {
Properties pros = new Properties();
fls = new FileInputStream("jdbc.properties");
// 加载文件
pros.load(fls);
String name = pros.getProperty("name");
String password = pros.getProperty("password");
System.out.println("name=" + name + ",password=" + password); // name=chengyu,password=123456
}catch (IOException e) {
e.printStackTrace();
}finally {
if(fls != null){
try {
fls.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3. Collections 类
操作集合的工具类Collection 、Map
常用方法:
public void test01(){
List list = new ArrayList();
list.add(10);
list.add(4);
list.add(5);
System.out.println(list); // [10, 4, 5]
// 反转
// Collections.reverse(list); // [5, 4, 10]
// 随机输出
// Collections.shuffle(list); // [4, 10, 5]
// 自然排序(定制排序需要传入Comparator)
// Collections.sort(list); // [4, 5, 10]
// 交换
// Collections.swap(list,1,2); // [10, 5, 4]
// 最大值、最小值
// 元素出现的频率
// int frequency = Collections.frequency(list, 4);
// System.out.println(frequency); // 1
// 复制:复制前需要定义一个与元list 等长的list,否则无法复制
List list1 = Arrays.asList(new Object[list.size()]);
Collections.copy(list1,list);
System.out.println(list1); // [10, 4, 5]
// 转线程安全:f返回的list2 即为线程安全
List list2 = Collections.synchronizedList(list);
}