Bootstrap

集合Collection和Map

目录

Collection

Collection继承关系图

 Collection常用方法

iterator迭代器

增强for循环-可以替代迭代器

List接口

List常用方法 

List接口练习1

List 3种遍历方式

List接口练习2

ArrayList细节

ArrayList底层结构和源码

Vector 与ArrayList比较

LinkedList

 LinkedList底层结构

ArrayList和LinkedList比较与选择 

Set接口

Set遍历

Set接口实现类-HashSet

经典面试题

 HashSet扩容机制

 HashSet练习1

 HashSet练习2

Set接口实现类-LinkedHashSet

LinkedHashSet练习

 TreeSet 

Map接口(很实用)

 Map继承关系图

Map接口特点 

Map接口常用方法

Map六大遍历方式

Map接口小练习

  HashMap底层机制

HashTable

Properties 

TreeMap

 开发中如何选择集合实现类?

Collections工具类

练习1

练习2

练习3

练习4 


Collection

可以保存任意多个数据(数组长度在定义时固定,不能改变)

集合主要有两组:Collection单列集合(List和Set接口继承Collection)和Map多列集合


Collection继承关系图


 Collection常用方法

package com.lili.collection_;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class CollectionMethod {
    @SuppressWarnings({"all"})
    public static void main(String[] args) {
        List list = new ArrayList();
        //add 添加单个元素
        list.add("hello");
        list.add(88);
        list.add("java");
        list.add(true);

        //remove 删除指定元素
        //list.remove("hello");   //返回boolean
        list.remove(0);    //返回被删除的对象
        System.out.println(list);

        //contains 查找元素是否存在,返回boolean
        System.out.println(list.contains(88));

        //size 获取元素个数
        System.out.println(list.size());

        //isEmpty 判断是否为空
        System.out.println(list.isEmpty());

        //clear 清空
        list.clear();
        System.out.println(list);

        //addAll 添加多个元素
        List list2 = new ArrayList();
        list2.add(8);
        list2.add(true);
        list.addAll(list2);
        System.out.println(list);

        //containAll 查找多个元素是否都存在
        System.out.println(list.containsAll(list2));

        //removeAll 一次删除多个元素
        list.add(888);
        list.removeAll(list2);
        System.out.println(list);
    }
}

iterator迭代器

1. 先得到集合的迭代器  Iterator iterator = col.iterator();

2. while循环遍历,iterator.hasNext()判断是否还有未遍历元素,next()返回下一个元素

3. 若需重新遍历,需要更新迭代器

package com.lili.collection_;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class CollectionIterator {
    @SuppressWarnings({"all"})
    public static void main(String[] args) {
        Collection col = new ArrayList();
        col.add(new Book("红楼梦", "曹雪芹", 65.5));
        col.add(new Book("三国演义", "罗贯中", 87.5));
        col.add(100);

        //遍历col集合
        //1. 得到col的迭代器
        Iterator iterator = col.iterator();
        //2. while循环遍历
        int count = 0;
        while (iterator.hasNext()) { //hasNext()判断是否还有未遍历元素
            Object obj = iterator.next(); //next()获取元素
            System.out.println("第" + (++count) + "个元素:" + obj);
        }

        //若需重新遍历,需要重置迭代器
        iterator = col.iterator();
        System.out.println(iterator.next());
    }
}

class Book {
    private String name;
    private String author;
    private double price;

    public Book(String name, String author, double price) {
        this.name = name;
        this.author = author;
        this.price = price;
    }

    @Override
    public String toString() {
        return name + ' ' + author + ' ' + price;
    }
}

增强for循环-可以替代迭代器

 只能用于遍历集合或数组

增强for遍历集合的本质其实也是迭代器

package com.lili.collection_;

import java.util.ArrayList;
import java.util.Collection;

public class CollectionFor {
    public static void main(String[] args) {
        Collection col = new ArrayList();
        col.add(new Book("红楼梦", "曹雪芹", 65.5));
        col.add(new Book("三国演义", "罗贯中", 87.5));
        col.add(100);

        //增强for遍历集合,快捷键I
        //增强for遍历集合的本质其实也是迭代器
        for (Object obj : col) {
            System.out.println(obj);
        }

        //增强for也可遍历数组
//        int[] nums = {10, 20, 30};
//        for (int i : nums) {
//            System.out.println(i);
//        }
    }
}

List接口

List常用方法 

  1.  List集合类中元素有序(先进先出),且可重复
  2. add(Object obj)直接添加对象
  3. add(int index, Object obj)在index处加入对象
  4. addAll(int index, Collection col)从index位置处开始多个添加对象
  5. get(int index)获取指定index位置的元素
  6. indexOf(Object obj)返回对象在集合中首次出现的位置
  7. lastIndexOf(Object obj)返回对象在集合中末次出现的位置
  8. remove(int index)移除指定位置的对象
  9. set(int index, Object obj)设置指定位置(index必须小于集合大小)的元素为obj,相当于替换
  10. subList(int fromIndex, int toIndex)返回[fromIndex, toIndex)的子集合
package com.lili.list_;

import java.util.ArrayList;
import java.util.List;

public class ListMethod {
    public static void main(String[] args) {
        List list = new ArrayList();
        //add(Object obj)直接添加对象
        list.add("hello");
        list.add("consistant");
        list.add(true);
        //add(int index, Object obj)在index处加入对象
        list.add(1, 99);
        //List集合类中元素有序(先进先出),且可重复
        list.add(2, '.');
        System.out.println(list);

        List list2 = new ArrayList();
        list2.add(1);
        list2.add('.');
        //addAll(int index, Collection col)从index位置处开始多个添加对象
        list.addAll(3, list2);

        //addAll(Collection col)无index默认加在最后
        //list.addAll(list2);
        System.out.println(list);

        //get(int index)获取指定index位置的元素
        System.out.println(list.get(0));

        //indexOf(Object obj)返回对象在集合中首次出现的位置
        System.out.println(list.indexOf('.'));

        //lastIndexOf(Object obj)返回对象在集合中末次出现的位置
        System.out.println(list.lastIndexOf('.'));

        //remove(int index)移除指定位置的对象
        list.remove(4);
        System.out.println(list);

        //set(int index, Object obj)设置指定位置(index必须小于集合大小)的元素为obj,相当于替换
        list.set(1, 88);
        System.out.println(list);

        //subList(int fromIndex, int toIndex)返回[fromIndex, toIndex)的子集合
        List returnList = list.subList(1, 5);
        System.out.println(returnList);
    }
}

List接口练习1

  1. 实例化List对象,并添加10个对象
  2. 在第2个位置插入元素
  3. 获取第5个元素
  4. 删除第6个元素
  5. 修改第7个元素
  6. 使用迭代器遍历集合
package com.lili.list_;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class ListExercise {
    public static void main(String[] args) {
        //实例化List对象,并添加10个对象
        List list = new ArrayList();
        list.add("pump");
        list.add(21);
        list.add("hi");
        list.add("lucky");
        list.add("stick");
        list.add("10");
        list.add('@');
        list.add("again");
        list.add("jennie");
        list.add("young");

        //在第2个位置插入元素
        list.add(1,"lili");
        System.out.println(list);

        //获得第5个元素
        System.out.println(list.get(4));

        //删除第6个元素
        list.remove(5);
        System.out.println(list);

        //修改第7个元素
        list.set(6, '.');
        System.out.println(list);

        //使用迭代器遍历集合
        Iterator iterator = list.iterator();
        while(iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

List 3种遍历方式

List接口的实现子类ArrayList, LinkedList, Vector都适用

1. 迭代器

2. 增强for循环

3. 普通for循环,List底层也是一个数组

package com.lili.list_;

import java.util.*;

public class ListFor {
    public static void main(String[] args) {
//        List list = new ArrayList();
//        List list = new LinkedList();
        List list = new Vector();
        list.add("你好");
        list.add("北京");
        list.add("!");
        list.add(21);

        System.out.println("====迭代器遍历====");
        //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);
        }

        System.out.println("====普通for遍历====");
        //3. 普通for遍历
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }
}

List接口练习2

  1.  使用List的实现类添加3本图书,并遍历
  2. 冒泡法按价格降序排序
  3. 使用ArrayList, LinkedList, Vector三种集合实现
package com.lili.list_;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;

public class ListExercise02 {
    public static void main(String[] args) {
        //使用List的实现类添加3本图书,并遍历
        //冒泡法按价格降序排序
        //使用ArrayList, LinkedList, Vector三种集合实现

        //List list = new ArrayList();
        List list = new LinkedList();
        //List list = new Vector();

        list.add(new Book("红楼梦", "曹雪芹", 86.5));
        list.add(new Book("三国演义", "罗贯中", 112));
        list.add(new Book("西游记", "吴承恩", 78));
        list.add(new Book("水浒传", "施耐庵", 89));

        //价格降序
        sort(list);

        //遍历集合
        for (Object o : list) {
            System.out.println(o);
        }
    }

    public static void sort(List list) {
        int listSize = list.size();
        for (int i = 0; i < listSize - 1; i++) {
            for (int j = 0; j < listSize - 1 - i; j++) {
                Book book1 = (Book) list.get(j);
                Book book2 = (Book) list.get(j + 1);
                if (book1.getPrice() < book2.getPrice()) {
                    list.set(j, book2);
                    list.set(j+1, book1);
                }
            }
        }
    }
}

ArrayList细节

  1. ArrayList可以加入任意元素,包括空null
  2. ArrayList是由数组来实现数据存储的
  3. ArrayList基本等同于Vector,但ArrayList是线程不安全的 (看源码)
package com.lili.collection_;

import java.util.ArrayList;

public class ArrayListDetail {
    public static void main(String[] args) {
        ArrayList arrayList = new ArrayList();
        arrayList.add(null);
        arrayList.add("lucy");
        arrayList.add(null);
        System.out.println(arrayList);
    }
}

ArrayList里的方法没有synchronized修饰,所以是线程不安全的


ArrayList底层结构和源码

  1. ArrayList中维护了一个Object类型的数组,transient Object[] elementData; transient表示瞬间的、短暂的,表示该属性不会被序列化
  2. 当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第一次添加扩容为10,如需再次扩容,就扩容为当前倍数的1.5倍
  3. 如果使用的是指定大小的构造器,则初始elementData容量为指定大小,再次扩容,就扩容为当前倍数的1.5倍

第一次容量为10;

第二次及以后扩容1.5倍;

 扩容底层用的是Arrays.copyOf(),会保留原数组元素并扩容。

注意,使用idea进行debug时,默认显示的数据是简化后的,想看到所有数据需要设置(取消勾选下图中的篮色小方框)


创建指定大小的ArrayList

初始容量为指定大小

添加对象,进行装箱并添加

 


Vector 与ArrayList比较

  1.  Vector底层也是一个对象数组,protected Object[] elementData;
  2.  Vector是线程安全的,Vector的方法都有synchronized修饰 

 调用Vector无参构造器创建Vector,初始容量为10,每次扩容2倍

若是指定大小,直接每次扩容2倍

package com.lili.list_;

import java.util.Vector;

public class Vector_ {
    public static void main(String[] args) {
        Vector vector = new Vector();
        /*
        Vector的无参构造器
        public Vector() {
            this(10);
        }
        */
    }
}

LinkedList

  1. LinkedList底层实现了双向链表和双端队列的特点
  2. 可以添加任意元素,包括null
  3. 线程不安全,没有实现同步

 LinkedList底层结构

  1. LinkedList底层维护了一个双向链表
  2. LinkedList维护了两个属性,first和last分别指向首节点和尾节点
  3. 每个节点里面又维护了prev, next, item 三个属性
  4. LinkedList添加和删除效率高(修改指针即可)

ArrayList和LinkedList比较与选择 

  1. 如果改查操作多,选择ArrayList
  2. 如果增删操作多,选择LinkedList
  3. 在程序中80%是查找操作,因此大多数情况使用ArrayList

Set接口

  1.  Set实现类的对象,不能存放重复元素
  2.  Set实现类的对象,可以添加任意元素,包括null
  3.  Set是无序的(添加时无序),但每次输出时顺序一样
public class SetMethod {
    public static void main(String []args) {
       	Set set = new HashSet();
		set.add("john");
		set.add("lucy");
		set.add("john");
		set.add("jack");
		set.add(null);
		set.add(null);
		System.out.println(set);
    }
}

Set遍历

1. 使用迭代器

Iterator iterator = set.iterator();
while(iterator.hasNext()) {
    Object obj = iterator.next();
    System.out.println(obj);
}

2. 增强for (增强for的底层就是迭代器)

for(Object o : set) {
    System.out.println(o);
}

注意:不能用普通for循环,因为Set接口对象不能通过索引获取,它是无序的


Set接口实现类-HashSet

  1.  HashSet实现了Set接口
  2.  HashSet底层实际上是HashMap(看源码,HashSet的构造器)

 小练习:看看下面添加操作能否成功?

public class SetMethod {
    public static void main(String []args) {
       	Set set = new HashSet();
		set.add("lucy");  //1
		set.add("lucy");  //2
        set.add(new Dog("大黄"));  //3
        set.add(new Dog("大黄"));  //4
		System.out.println(set);
    }
}

class Dog {
    private String name;
    public Dog(String name) {
        this.name = name;
    }
}

answer:

set集合不能有重复元素

1 添加成功

2  添加失败

3 添加成功

4 添加成功


经典面试题

 看看下面代码添加能否成功?

public class SetMethod {
    public static void main(String []args) {
       	Set set = new HashSet();
		set.add(new String("tom"));  //1
		set.add(new String("tom"));  //2
		System.out.println(set);
    }
}

answer:

查看Set的 add() 源码,弄清什么情况算同一个元素

1 添加成功

2 添加失败


 HashSet扩容机制

 HashSet底层是HashMap

  1. 添加1个元素时,先得到这个元素的hash值,将hash值转成索引值
  2. 找到存储数据表table,再看这个索引处是否已存储别的元素
  3. 如果没有,直接加入
  4. 如果有,调用equals比较,如果是同一个元素,放弃添加,不相同添加到该索引的链表最后
  5. 在java8中,如果一条链表的元素个数到达TREEIFY_THRESHOLD(默认是8),并且table的大小>=MIN_TREEIFY_CAPACITY(默认64),就会进行树化(红黑树)

 HashSet练习1

 定义1个Employee类,该类包含:private成员属性name, age

1. 创建3个Employee对象放入HashSet中

2. 当name和age相同时,认为是相同员工,不能添加到HashSet中 

public class HashSetExercise {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        hashSet.add(new Employee("judy", 21));
        hashSet.add(new Employee("kiki", 19));
        hashSet.add(new Employee("judy", 21));
    }
}

class Employee {
    private String name;
    private int age;
    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return this.age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    //Generate equals() 和 hashCode()
    //name 和 age 相同时,equals()返回true,且hashCode()返回相同的hashCode值
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

 HashSet练习2

 定义1个Employee类,该类包含:private成员属性name, sal, birthday,其中birthday为MyDate类型(属性包括:year, month, day)

1. 创建3个Employee对象放入HashSet中

2. 当name和birthday相同时,认为是相同员工,不能添加到HashSet中 

public class HashSetExercise {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        hashSet.add(new Employee("judy", 6500, 20000101));
        hashSet.add(new Employee("kiki", 10000, 20030605));
        hashSet.add(new Employee("judy", 8000, 20011112));
    }
}

class Employee {
    private String name;
    private int sal;
    private MyDate birthday;

    public Employee(String name, int sal, MyDate birthday) {
        this.name = name;
        this.sal = sal;
        this.birthday = birthday;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getSal() {
        return this.sal;
    }

    public void setSal(int sal) {
        this.sal = sal;
    }

    public MyDate getBirthday() {
        return this.birthday;
    }

    public void setBirthday(MyDate birthday) {
        this.birthday = birthday;
    }

    //Generate equals() 和 hashCode()
    //name 和 birthday 相同时,equals()返回true,且hashCode()返回相同的hashCode值
    @Override
    public int hashCode() {
        return Objects.hash(name, birthday);
    }
}

class MyDate {
    private int year;
    private int month;
    private int day;
}

Set接口实现类-LinkedHashSet

  1.  LinkedHashSet是HashSet的子类,实现了Set接口
  2.  LinkedHashSet底层是一个LinkedHashMap,底层维护了一个数组+双向链表(head和tail),加入和取出元素顺序一致
  3.  LinkedHashSet根据元素的hashCode值来决定元素的存储位置,同时使用链表维护元素的次序,使得元素看起来是以插入顺序保存的
  4. LinkedHashSet不允许添加重复元素

LinkedHashSet练习

Car类(属性:name, price),如果name和price一样,就认为是相同元素

public class LinkedHashSetExercise {
    LinkedHashSet linkedHashSet = new LinkedHashSet();
    linkedHashSet.add(new Car("迪奥", 1000));
    linkedHashSet.add(new Car("奥迪", 300000));
    linkedHashSet.add(new Car("法拉利", 1000000));
    linkedHashSet.add(new Car("奥迪", 300000));    //加入不了
    linkedHashSet.add(new Car("保时捷", 20000000));
    linkedHashSet.add(new Car("奥迪", 300000));    //加入不了
}


class Car {
    private String name;
    private double price;
    
    public Car(String name, double price) {
        this.name = name;
        this.price = price;
    }
    
    //Generate equals() 和 hashCode()
    //name 和 price 相同时,equals()返回true,且hashCode()返回相同的hashCode值
    @Override
    public int hashCode() {
        return Objects.hash(name, price);
    }
    
}

 TreeSet 

  1.  当使用TreeSet无参构造器创建TreeSet 时,集合仍是无序的
  2.  使用TreeSet 提供的一个带参构造器,可以传入一个比较器compare,指定排序规则
public class TreeSet_ {
    public static void main(String[] args) {
        TreeSet treeSet = new TreeSet(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return ((String) o2).compareTo((String) o1);
            }     
        });
        treeSet.add("tom");
        treeSet.add("rose");
        treeSet.add("kuku");
    }
}

Map接口(很实用)

 Map继承关系图

Map接口特点 

  1.  Map用于保存具有映射关系的数据key-value键值对
  2.  Map中可以是任何引用类型的数据,会封装到HashMap$Node对象中
  3.  Map中的key不能重复,若重复,会替换掉key对应的value
  4.  Map中的value可以重复
  5.  Map中的key可以为null(任意类型),但只能有一个key为null(不能重复),value为 null,可以有多个null的value
public class MapMethod {
    public static void main(String[] args) {
        Map map = new HashMap();
        map.put("no1", "kiki");
        map.put("no2", "kiki");  //value可以重复
        map.put("no1", "kuku");  //key不能重复,会覆盖掉原来的no1
        map.put(null, null);  //key为null
        map.put(null, "abc")  //替换
        map.put("noo3", null);  //value为null可以有多个

        //get方法,传入key,返回对应的value
        System.out.println(map.get("no1")); //kuku
    }
}

1. 一对k-v 是封装为一个HashMap$Node对象,HashMap$Node node = new Node(hash, key, value, null);

2. 为了方便遍历,会创建EntrySet集合,该集合存放的元素类型是Entry,而一个Entry对象就有 k, v,EntrySet<Entry<k, v>>

3. entrySet中,定义的类型是Map.Entry,但实际上存放的还是HashMap$Node(Node实现了Entry接口) 

4. Entry提供了2个方法便于遍历,getKey()和getValue()

Set set = map.entrySet();
for(Object obj: set) {
    Map.Entry entry = (Map.Entry)obj;
    System.out.println(entry.getKey() + " : " + entry.getValue);
}

Map接口常用方法

  1.  put 添加
  2. remove 根据key删除映射关系
  3. get 根据键获取值
  4. size 获取元素个数
  5. isEmpty 判断个数是否为0
  6. containsKey 查找键是否存在
  7. containsValue 查找值是否存在
  8. clear 清除
public class MapMethod {
    public static void main(String[] args) {
        Map map = new HashMap();
        //1 put 添加
        map.put(1, "jerry");
        map.put(2, "jimi");
        map.put(3, "lucy");
        
        //2 remove 根据key删除映射关系
        map.remove(3);;

        //3 get 根据键获取值
        System.out.println(map.get(1));
        
        //4 size 获取元素个数
        System.out.println(map.size());

        //5 isEmpty 判断个数是否为0
        System.out.println(map.isEmpty());

        //6 containsKey 查找键是否存在
        System.out.println(map.containsKey(2)));

        //7 containsValue 查找值是否存在
        System.out.println(map.containsValue("刘亦菲")));

        //8 clear 清除
        System.out.println(map.clear());
    }
}

Map六大遍历方式

public class MapFor {
    public static void main(String[] args) {
        Map map = new HashMap();
        map.put(1, "jerry");
        map.put(2, "jimi");
        map.put(3, "lucy");
        
        //把所有的key取出
        Set keyset = map.keySet();
        //1 增强for
        for(Object key : keyset) {
            System.out.println(key + " : " + map.get(key));
        }

        //2 迭代器
        Iterator iterator = keyset.iterator();
        while(iterator.hasNext()) {
            Object key = iterator.next();
            System.out.println(key + " : " + map.get(key));
        }
        
        //把所偶的value取出
        Collection values = map.values;
        //3 增强for
        for(Object value : values) {
            System.out.println(value);
        }

        //4 迭代器
        Iterator iterator2 = values.iterator();
        while(iterator2.hasNext()) {
            Object value = iterator2.next();
            System.out.println(value);
        }

        //通过EntrySet来获取k-v
        Set entrySet = map.entrySet();
        //5 增强for
        for(Object entry : entrySet) {
            Map.Entry m = (Map.Entry)entry;
            System.out.println(m.getKey() + " : " + m.getValue());
        }

        // 6 迭代器
        Iterator iterator3 = entrySet.iterator();
        while(iterator3.hasNext()) {
            Object entry = iterator3.next();
            Map.Entry m = (Map.Entry)entry;
            System.out.println(m.getKey() + " : " + m.getValue());
        }
    }
}

Map接口小练习

使用HashMap添加3个员工对象,要求:

1. 键-员工id,值-员工对象 

2. 遍历显示工资>18000的员工,员工类:姓名、工资、员工id

public class MapExercise {
    public static void main() {
        Map map = new HashMap();
        map.put(1, new Employee(1, "kidy", 18500));
        map.put(2, new Employee(2, "cuku", 19000));
        map.put(3, new Employee(3, "lucy", 10000));
        
        //1 使用keySet
        Set keySet = map.keySet();
        for(Object key : keySet) {
            Employee emp =  (Employee)map.get(key);
            if(emp.getSal() > 18000) {
                System.out.println(emp));
            }
        }

        //2 使用entrySet
        Set entrySet = map.entrySet();
        Iterator iterator = entrySet.iterator();
        while(iterator.hasNext()) {
            Map.Entry entry = (Map.Entry)iterator.next();
            Employee emp = (Employee)entry.getValue();
            if(emp.getSal() > 18000) {
                System.out.println(emp);
            }
        }
    }
}

class Employee {
    private int id;
    private String name;
    private double sal;

    public Employee(int id, String name, double sal) {
        this.id = id;
        this.name = name;
        this.sal = sal;
    }
}

  HashMap底层机制

  1. HashMap底层维护了Node类型的数组table,默认为null
  2. 当创建对象时,将加载因子(loadfactor)初始化为0.75
  3. 当添加k-v时,通过key的哈希值得到在table的索引,然后判断该索引处是否已存放元素,如果没有直接添加;如果有且为相同元素,则不添加,否则添加在链表末尾
  4. 第一次添加,需要扩容table容量为16,临界值(threshold)为12
  5. 以后再扩容,则需要扩容为原来的2倍
  6. 在java8中,如果一条链表的元素个数 >=  THREEIFY_THRESHOLD(默认是8),并且table的大小 >= MINI_THREEIFY_CAPACITY(默认是64),就会进行树化(红黑树)

HashTable

  1.  存放的元素是键值对
  2.  HashTable的键和值都不能为null
  3.  HashTable的键不能重复,否则会覆盖
  4.  HashTable使用方法和HashMap基本一样
  5.  HashTable是线程安全的,HashMap是线程不安全的
  6.  HashTable底层是一个数组HashTale$Entry类型的table,初始大小为11
  7.  临界值threshold 8 = 11 * 0.75(到了临界值就会扩容,,临界值=当前大小*加载因子)
  8.  到了临界值扩容为原来大小的2倍+1

Properties 

  1.  Properties 是HashTable的子类
  2.  Properties主要用于从xxx.properties文件中,加载数据到Properties类对象,并进行读取和修改(xxx.properties文件通常为配置文件)

TreeMap

  1.  当使用TreeMap无参构造器创建TreeMap时,集合仍是无序的
  2.  使用TreeMap提供的一个带参构造器,可以传入一个比较器compare,指定排序规则
public class TreeMap_ {
    public staic void main(String[] args) {
        TreeMap treeMap = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return ((String) o2).compareTo((String) o1);
            }     
        });
        treeMap.put("n1", "lily");
        treeMap.put("n2", "qiqi");
        treeMap.put("n3", "ferry");
    }
}

 开发中如何选择集合实现类?

1. 先判断存储的类型是单列还是双列

2.

若为一组对象(单列),就用Collection接口对象

    1) 允许重复:List

        增删多:LinkedList(底层维护一个双向链表)

        改查多:ArrayList(底层维护Object类型的可变数组)

    2) 不能重复:Set 

        无序:HashSet(底层是HashMap,维护了一个哈希表,即数组+链表+红黑树)

        排序:TreeSet

        插入和取出顺序一致:LinkedHashSet(维护数组+双向链表)

若为一组键值对(双列),就用Map接口对象

  键无序:HashMap(底层是:jdk8:数组+链表+红黑树,jdk7:数组+链表)

  键排序:TreeMap

  键插入和取出顺序一致:LinkedHashMap

  读取文件:Properties


Collections工具类

排序方法(全是静态的) 

public class Collections_ {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("tom");
        list.add("kidy");
        list.add("lary");

        //reverse 反转
        Collections.reverse(list);
        
        //shuffle 随机排序
        Collections.shuffle(list);

        //sort(List) 升序排序
        Collections.sort(list);

        //sort(List, Comparator) 自定义排序规则
        Collections.sort(list, new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return ((String) o2).length() - ((String) o1).length();
            }
        });

        //swap(List, int, int) 将指定两个下标交换
        Collections.swap(list, 1, 0);
    }
}

 查找、替换

public  class Collections2_ {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("tom");
        list.add("kidy");
        list.add("lary");

        //max(Collection) 根据元素的自然顺序,返回集合的最大元素
        Collections.max(list);
        
        //max(Collection, Comparator) 根据Comparator指定顺序,返回集合的最大元素
        Collections.max(list, new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return ((String) o1).length() - ((String) o2).length();
            }
        });

        //min(Collection) 根据元素的自然顺序,返回集合的最小元素
        Collections.min(list);
        
        //min(Collection, Comparator) 根据Comparator指定顺序,返回集合的最小元素
        Collections.min(list, Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return ((String) o1).length() - ((String) o2).length();
            }
        });

        //frequency(Collection, Object) 返回指定集合中元素的出现次数
        Collections.frequency(list);

        //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);
        
        //replaceAll(List list, Object oldVal, Object newVal) 使用新值替换list中的所有旧值
        Collections.replaceAll(list, "tom", "汤姆");
    }
}

练习1

1.封装1个新闻类,包含标题和内容属性,提供get和set方法,重写toString方法,只打印标题

2.只提供一个带参的构造器,实例化时,只初始化标题,并实例化2个对象

新闻一:博鳌亚洲论坛全球健康论坛第三届大会新闻发布会举行

新闻二:机票价格“跳水”后能否“买低退高” 调查:部分航司可操作

3.将新闻对象添加到ArrayList中,并进行倒序遍历

4.在遍历集合过程中,对新闻标题进行处理,超过15字的只保留15个,然后在后边加“...”

5.在控制台打印遍历出经过处理的新闻标题

public class Homework01 {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add(new News("博鳌亚洲论坛全球健康论坛第三届大会新闻发布会举行"));
        list.add(new News("机票价格\"跳水\"后能否\"买低退高\" 调查\:部分航司可操作"));
        
        //倒序遍历
        for(int i = list.size() - 1;  i>=0; i--) {
            News news = (News) list.get(i); 
            System.out.println(processTitle(news.getTitle()));
        }
    }

    public static String processTitle(String title) {
        if(title.length() > 15) {
            return title.subString(0,15) + "...";
        }
        return title;
    }
}

class News {
    private String title;
    private String content;
        
    public News(String title) {
        this.title = title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getTitle() {
        return this.title;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getContent() {
        return this.content;
    }

    @Override
    public int toString() {
        return "title = " + this.title;
    }
}

练习2

1.使用HashMap类实例化一个Map类型的对象,键(String)和 值(int)分别用于存储员工的姓名和工资,存入数据:jack-4000, tom-7500, smith-10000;

2.将jack的工资改为5000

3.为所有员工加薪500

4.遍历集合中所有员工

5.遍历集合中所有工资

public class Homework02 {
    pblic static void main(Strig[] args) {
        Map map = new HashMap();
        map.put("jack", 4000);
        map.put("tom", 7500);
        map.put("smith", 10000);

        //修改jack的工资
        map.put("jack", 5000);

        Set keySet = map.keySet();         
        for(Object key : keySet) {
            map.put(key,(Integer) m.get(key) + 100); 
        }

        Set entrySet = map.EntrySet();
        Iterator iterator = entrySet.iterator();
        while(iterator.hasNext()) {
            Map.Entry entry = (Map.Entry) iterator.next();
            System.out.println(entry.getKey() + " : " + entry.getValue());
        }
    }
}

练习3

分析HashSet和TreeSet是如何实现去重的?

1)  HashSet去重:hashCode() + equals(),先得到这个元素的hash值,将hash值转成索引值;找到存储数据表table,看这个索引处是否已存储别的元素;如果没有,直接添加;如果有,调用equals逐一比较,如果是同一个元素,放弃添加,全不相同则添加到该索引的链表最后

2) TreeSet去重:如果传入了Comparator匿名对象,使用实现的compare方法进行比较,如果返回0,就认为是相同元素,不添加,如果未传入Comparator匿名对象,就一添加的Compareable接口的compareTo去重


练习4 

 已知:Person类按照id和name重写了hashCode和equals方法,问下面代码输出什么?

public class Homework04 {
    public static void main(String[] args) {
        HashSet set = new HashSet();
        Person p1 = new Person(1001, "AA");
        Person p2 = new Person(1002, "BB");
        set.add(p1);
        set.add(p2);
        p1.name = "CC";
        set.remove(p1);  //删除的时候也会根据哈希值确定索引位置来删除,删除失败
        System.out.println(set);
        set.add(new Person(1001, "CC"));
        System.out.println(set);
        set.add(new Person(1001, "AA"));
        System.out.println(set);
    }
}

class Person {
    private String name;
    private int id;
    
    public Person(int id, String name) {
        this.name = name;
        this.id = id;
    }

    @Override
    public boolean equals(Object o) {
        if(this == o) return true;
        if(o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return id == person.id && Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, id);
    }
}

注意:删除的时候也会根据哈希值确定索引位置来删除,删除p1失败

;