集合(最终章)

128 阅读6分钟

Map接口

image.png

Map的实现类的结构:
Map:双列数据,存储key-value对的数据   ---类似于高中函数:y=f(x)
            ----HashMap:作为Map的主要实现类:线程不安全的,效率高;存储null的key和value
                ----LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序进行遍历
                        原因:在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素
                        对于频繁的遍历操作,此类效率高于HashMap
            ----TreeMap:保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然或定制排序
                        底层使用红黑树
            ----HashTable:作为Map的古老实现类,线程安全的,效率低,不能存储null的key和value
                ---- 


          HashMap的底层:数组+链表(jdk7及以前)
                        数组+链表+红黑树(jdk8)

 面试题:
 1.HashMap的底层实现原理?
 2.HashMap和HashTable的异同?
3.CurrentHashMap与HashTable的异同

二.Map结构的理解:
    Map中的key:无序的,不可重复的,使用Set存储所有的key --->key所在的类要重写equals方法和hashCode方法(以HashMap为例)
    
    Map中的value:无序的,可重复的,使用Collection存储所有的value--->value所在的类要重写equals方法
    
    一个键值对:key-value构成了一个Entry对象
    Map中的entry:无序的,不可重复的,使用Set存储所有的entry

三.HashMap的底层实现原理?以jdk7为例说明
    HashMap map = new HashMap();
    在实例化以后,底层创建了长度是16的一维数组Entry[] table
    ...可能已经执行过多次put...
    map.put(key1,value1):
    首先,调用key1所在类的hashCode方法计算key1哈希值,此哈希值通过计算后,得到在Entry数组中的存放位置
    如果此位置上的数据为空,此时的key-value添加成功----情况1
    如果此位置上的数据不为空(意味着此位置上存在一个或多个数据(以链表形式存在)),则需要比较key1和
    已经存在的一个或多个数据的哈希值
        如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功----情况2
        如果key1的哈希值与已经存在的某一个数据(key2-value2)的哈希值相同,继续调用key1所在类的equals方法(key2),比较
            如果equals方法返回false,此时key1-value1添加成功----情况3
            如果equals方法返回true:使用value1替换value2:此时体现的是修改value值的功能

    补充:关于情况2和情况3:此时key1-value1和原来的数据以链表方式存储

    在不断的添加过程中,会涉及到扩容问题,当超出临界值时(且要存放的位置非空时),默认扩容为2倍,并将原有数据复制过来

    jdk8相较于jdk7在底层实现方面的不同:
    1.new HashMap():底层没有创建一个长度为16的数组
    2.jdk8底层的数组是:Node[],而非Entry数组
    3.首次调用put方法,底层创建一个长度为16的数组
    4.jdk7底层结构有:数组+链表 jdk8底层结构有:数组+链表+红黑树
        当数组的某一索引位置上的元素以链表方式存在的数据个数>8且当前数组长度>64。
        此时此索引位置上的所有数据改为红黑树存储
        
四.LinkedHashMap的底层实现原理(了解)
static class Entry<K,V> extends HashMap.Node<K,V> {
    Entry<K,V> before, after;//能够记录添加的元素的先后顺序
    Entry(int hash, K key, V value, Node<K,V> next) {
            super(hash, key, value, next);
        }
    }

image.png

源码分析十分重要,是程序员必须熟透的一条路线。

为什么HashMap要提前扩容?

因为想要数组中的链表结构不会那么多

Map中的常用方法

image.png

public void test(){//添加,删除,修改操作
        Map map = new HashMap();

        //1.put(Object key,Object value),添加
        map.put("AA",123);
        map.put(45,123);
        map.put("BB",56);

        //put(Object key,Object value),修改
        map.put("AA",87);
        System.out.println(map);

        //2.putAll(Map m),添加m中的所有数据
        Map map1 = new HashMap();
        map1.put("CC",123);
        map1.put("DD",123);
        map.putAll(map1);

        System.out.println(map);

        //3.remove(Object key),移除
        Object cc = map.remove("CC");
        System.out.println(cc);//"CC"的value值
        System.out.println(map);

        //4.clear(),清空当前Map中的所有数据
        map.clear();
        System.out.println(map);//{}
        System.out.println(map.size());//0

    }
}
@Test
    public void test1(){//元素查询的操作
        Map map = new HashMap();
        map.put("AA",123);
        map.put(45,123);
        map.put("BB",56);

        //1.Object get(Object key):获取指定key对应的value
        System.out.println(map.get(45));//123

        //2.containsKey(Object key),是否包含指定的key
        boolean isExist = map.containsKey("BB");
        System.out.println(isExist);//true

        //3.containsValue(Object value),是否包含指定的value
        boolean isExist1 = map.containsValue(123);
        System.out.println(isExist1);//true

        //4.int size:返回个数
        //5.boolean isEmpty():判断当前map是否为空
        //6.boolean equals(Object obj):判断当前map和参数对象obj是否相等
    }
}
public void test2() {//元视图操作的方法
        Map map = new HashMap();
        map.put("AA", 123);
        map.put(45, 123);
        map.put("BB", 56);

        //1.Set keySet():返回所有key构成的Set集合,可以用来遍历所有的key集
        Set set = map.keySet();
        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
        //2.Collection values():返回所有value构成的Collection集合,可以用来遍历所有的value集
        Collection values = map.values();
        Iterator iterator1 = values.iterator();
        while(iterator1.hasNext()){
            System.out.println(iterator1.next());
        }
        //3.Set entrySet():返回所有key-value对构成的Set集合,可以用来遍历所有的key-value集
        //方式1:entrySet
        Set set1 = map.entrySet();
        Iterator iterator2 = set1.iterator();
        while (iterator2.hasNext()){
            Object obj = iterator2.next();
            //entrySet集合中的元素都是entry
            Map.Entry entry = (Map.Entry) obj;
            System.out.println(entry.getKey()+"---->"+ entry.getValue());
        }
        //方式2:
        Set set2 = map.keySet();
        Iterator iterator3 = set2.iterator();
        while(iterator3.hasNext()){
            Object key = iterator3.next();
            Object value = map.get(key);
            System.out.println(key+"====="+value);
        }
        //方式3:
        Set set3 = map.entrySet();
        Iterator iterator4 = set3.iterator();
        while(iterator4.hasNext()){
            System.out.println(iterator4.next());
        }
    }
}

常用方法

增:put(Object key)

删:remove(Object key)

改:put(Object key)

查:get(Object key)

长度:size()

遍历:keySet()/values()/entrySet()

TreeMap

//向TreeMap中添加key-value,要求key必须是由同一个类创建的对象
//因为要按照key进行排序:自然排序,定制排序
@Test
public void test() {//自然排序
    TreeMap map = new TreeMap();
    User u1 = new User("Tom", 23);
    User u2 = new User("Jerry", 32);
    User u3 = new User("Jack", 20);
    User u4 = new User("Rose", 18);
    map.put(u1, 98);
    map.put(u2, 89);
    map.put(u3, 76);
    map.put(u4, 100);
    Set set1 = map.entrySet();
    Iterator iterator2 = set1.iterator();
    while (iterator2.hasNext()) {
        Object obj = iterator2.next();
        //entrySet集合中的元素都是entry
        Map.Entry entry = (Map.Entry) obj;
        System.out.println(entry.getKey() + "---->" + entry.getValue());
    }
}

@Test
public void test1() {//定制排序,按照年龄进行排序
    TreeMap map = new TreeMap(new Comparator() {
        @Override
        public int compare(Object o1, Object o2) {
            if(o1 instanceof User&& o2 instanceof User){
                User u1 = (User) o1;
                User u2 = (User) o2;
                return Integer.compare(u1.getAge(),u2.getAge());
            }
            throw new RuntimeException("输入的类型不匹配");
        }
    });
    User u1 = new User("Tom", 23);
    User u2 = new User("Jerry", 32);
    User u3 = new User("Jack", 20);
    User u4 = new User("Rose", 18);
    map.put(u1, 98);
    map.put(u2, 89);
    map.put(u3, 76);
    map.put(u4, 100);
    Set set1 = map.entrySet();
    Iterator iterator2 = set1.iterator();
    while (iterator2.hasNext()) {
        Object obj = iterator2.next();
        //entrySet集合中的元素都是entry
        Map.Entry entry = (Map.Entry) obj;
        System.out.println(entry.getKey() + "---->" + entry.getValue());
    }

Properties

image.png

public class PropertiesTest {
    public static void main(String[] args) throws Exception {
        Properties pros = new Properties();
        FileInputStream fis = new FileInputStream("jdbc.properties");
        pros.load(fis);//加载流对应的文件
        String name = pros.getProperty("name");
        String password = pros.getProperty("password");
        System.out.println(name);
        System.out.println(password);
    }

Collections工具类

image.png

image.png

public class CollectionsTest {
    @Test
    public void test(){
        List list = new ArrayList();
        list.add(123);
        list.add(43);
        list.add(765);
        list.add(-97);
        list.add(0);
        //void copy(List dest,List src)
//        List list1 = new ArrayList();
//        Collections.copy(list1,list);//这样会报异常
        List dest = Arrays.asList(new Object[list.size()]);
        Collections.copy(dest,list);
        System.out.println(dest);//这样才能重新复制

//        System.out.println(list);
//        //1.reverse(list),反转List中元素的顺序
//        Collections.reverse(list);
//        System.out.println(list);
//
//        //2.shuffle(list):随机排序,每次都不一样
//        Collections.shuffle(list);
//        System.out.println(list);
//
//        //3.sort(list):根据元素的自然顺序对指定list集合元素按升序排序
//        Collections.sort(list);
//        System.out.println(list);

image.png

可以将ArrayList,HashMap由线程不安全的转换为线程安全的

public void test1(){
        List list = new ArrayList();
        list.add(123);
        list.add(43);
        list.add(765);
        list.add(-97);
        list.add(0);
        List list1 = Collections.synchronizedList(list);//返回的list1即为线程安全的list
    }
}