Java内置的数据结构详解


集合概述

  1. 什么是集合?集合有什么用?

    数组其实就是一个集合。集合实际上就是一个容器。可以容纳其他类型的数据。

  2. 集合为什么说在开发中使用较多?

    集合是一个容器,是一个载体,可以一次容纳多个对象。
    在实际开发中,假设连接数据库,数据库当中有10条记录,那么假设把这10条记录查询出来,在java程序中会将10条数据封装成10个java对象,然后将10个java对象放到某一个集合当中,将集合传到前端,然后遍历集合,将一个数据一个
    数据展现出来。

  3. 集合不能直接存储基本数据类型,另外集合也不能直接存储java对象,集合当中存储的都是java对象的内存地址。(或者说集合中存储的是引用。)

    list.add(100) ; // 自动装箱Integer
    

    集合在java中本身是一个容器,是一个对象。
    集合中任何时候存储的都是"引用”。
    在这里插入图片描述

  4. 在java中每一个不同的集合,底层会对应不同的数据结构。往不同的集合中存储元素,等于将数据放到了不同的数据结构当中。什么是数据结构?数据存储的结构就是数据结构。不同的数据结构,数据存储方式不同。

    例如:数组、二叉树、链表、哈希表…
    以上这些都是常见的数据结构。
    使用不同的集合等同于使用了不同的数据结构。
    在java集合这一章节,需要掌握的不是精通数据结构。java中已经将数据结构实现了,已经写好了这些常用的集合类,你只需要掌握怎么用?在什么情况下选择哪一种合适的集合去使用即可。

    new ArrayList();//创建一个集合,底层是数组。
    new LinkedList();//创建一个集合对象,底层是链表。
    new TreeSet();//创建一个集合对象,底层是二叉树。
    
  5. 集合在java JDK中哪个包下?

    java.util.*;
    

    所有的集合类和集合接口都在java.util包下。

  6. 为了掌握集合这块内容,最好能将集合的继承结构图背会。
    集合整个这个体系是怎样的一个结构,需要有印象。

  7. 在java中集合分为两大类:

    • 一类是单个方式存储元素:
      单个方式存储元素,这一类集合中超级父接口:java.util.Collection;

    • 一类是以键值对的方式存储元素:
      以键值对的方式存储元素,这一类集合中超级父接口:java.util.Map;

      Iterator iterator = "Collection对象".iterator() ;//iterator是迭代器对象
      

集合的继承结构图

Collection


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

Map

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述HashSet源码:

/* @param e element to be added to this set
* @return {@code true} if this set did not already contain the specified 
* element
*/
pub1ic boolean add(E e) {
    return map.put(e, PRESENT)==nu11;
}

总结(所有的实现类):

  • List集合存储元素的特点:有序可重复
    有序:存进去的顺序和取出的顺序相同,每一个元素都有下标。
    可重复:存进去1,可以再存储一个1。

  • Set (Map)集合存储元素的特点:无序不可重复
    无序:存进去的顺序和取出的顺序不一定相同。另外Set集合中元素没有下标。
    不可重复:存进去1,不能再存储1了。

  • SortedSet (SortedMap)集合存储元素特点:
    首先是无序不可重复的,但是SortedSet集合中的元素是可排序的。
    无序:存进去的顺序和取出的顺序不一定相同。另外Set集合中元素没有下标。
    不可重复:存进去1,不能再存储1了。
    可排序:可以按照大小顺序排列。

  • ArrayList:底层是数组

  • LinkedList:底层是双向链表

  • Vector:底层是数组,线程安全的,效率较低,使用较少

  • HashSet:底层是HashMap,放到HashSet集合中的元素等同于放到HashMap集合key部分了

  • TreeSet:底层是TreeMap,放到TreeSet 集合中的元素等同于放到TreeMap集合key部分了

  • HashMap:底层是哈希表

  • Hashtable:底层也是哈希表,只不过线程安全的,效率较低,使用较少

  • Properties:是线程安全的,并且key和value只能存储字符串String

  • TreeMap:底层是二叉树,TreeMap集合的key可以自动按照大小顺序排序

Collection

Collection接口中常用的方法

关于java.util.Collection接口中常用的方法。
1、Collection 中能存放什么元素?
没有使用”泛型”之前, Collection中可以存储object的所有子类型。使用了“泛型之后, Collection中只能存储某个具体的类型。

Collection中什么都能存,只要是object的子类型就行。( 集合中不能直接存储基本数据类型,也不能存java对象,只是存储java对象的内存地址。)

2、Collection 中的常用方法

boolean add(Object e) 向集合中添加元素
int size() 获取集合中元素的个数
void clear() 清空集合
boolean contains(Object o) 判断当前集合中是否包含元素,包含返回true,不包含返回false
boolean remove(object o) 删除集合中的某个元素。
boolean isEmpty() 判断该集合中元素的个数是否为0
object[] toArray()调用这个方法可以把集合转换成数组
迭代器
public class CollectionDemo {
    public static void main(String[] args) {
        //遍历/迭代方式,是所有Collection通用的一种方式
        //创建集合对象
        Collection collection = new HashSet();//后面创建任何一种集合子类型
        collection.add("anonymous");
        collection.add("joker");
        collection.add(3.1415926);
        collection.add(2021);
        //对集合Collection进行遍历/迭代
        //第一步:获取集合对象的迭代器对象Iterator
        Iterator iterator = collection.iterator();
        //第二步:通过以上获取的迭代器对象开始迭代/遍历集合
        while (iterator.hasNext()){
            Object obj = iterator.next();
            System.out.println(obj);
        }
    }
}
boolean hasNext() 如果仍有元素可以迭代,则返回true
Object next() 返回迭代的下一个元素
while (true){//一直取,不判断,会出现异常:java.util.NoSuchElementException
     Object obj = iterator.next();
     System.out.println(obj);
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZJQjp4M0-1616557572508)(E:\Typora\images\image-20210302210635166.png)]
contains()方法

深入Collection集合的contains方法:

boolean contains(object o)

判断集合中是否包含某个对象o,如果包含返回true,如果 不包含返回false。
contains方法是用来判断集合中是否包含某个元素的方法,那么它在底层是怎么判断集合中是否包含某个元素的呢?
调用equals方法进行比对,equals方法返回true,就表示包含这个元素。

public class CollectionContains {
    public static void main(String[] args) {
        Collection collection = new ArrayList();
        String str1 = new String("abc");
        String str2 = new String("def");
        collection.add(str1);
        collection.add(str2);
        String str3 = new String("abc");
        System.out.println(collection.size());//2
        System.out.println(collection.contains(str3));//true
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ex6Mj6gH-1616557572510)(E:\Typora\images\BE1RRQ91F8DHSBVQ6OEE_G.png)]
Object中的equals方法:

public boolean equals(Object obj) {
    return (this == obj);
}
public class User {
    private String name;

    public User(String name) {
        this.name = name;
    }
}

public class CollectionContains {
    public static void main(String[] args) {
        Collection collection = new ArrayList();
        User u1 = new User("anonymous");
        User u2 = new User("anonymous");
        collection.add(u1);
        //没有重写equals之前:这个结果是false
        System.out.println(collection.contains(u2));//false
    }
}
public class User {
    private String name;

    public User(String name) {
        this.name = name;
    }

    //重equals方法
	//将来调用equals方法的时候,一定是调用这个重写的equals方法。
	//这个equals方法的比较原理是:只要姓名一样就表示同一个用户。
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        //如果名字一样表示同一个人。 (不再比较对象的内存地址了。比较内容。)
        return Objects.equals(name, user.name);
    }
}
//重写equals之后:这个结果是true
//System.out.println(collection.contains(u2));//true

结论:存放在集合中的类型,一定要重写equals方法。

remove()方法

public class CollectionRemove {
    public static void main(String[] args) {
        Collection c = new ArrayList();
        User u1 = new User("anonymous");
        User u2 = new User("anonymous");
        c.add(u1);
        //重写User类的equals方法前 false
        System.out.println(c.contains(u2));//重写User类的equals方法后 true
        c.remove(u2);
        //重写User类的equals方法前 1
        System.out.println(c.size());//重写User类的equals方法后 0
    }
}

关于集合元素的remove
重点:当集合的结构发生改变时,迭代器必须重新获取,如果还是用以前老的迭代器,会出现java.util.ConcurrentModificationException异常
重点:在迭代集合元素的过程中,不能调用集合对象的remove方法删除元素:c.remove(o);迭代过程中不能这样。会出现java.util.ConcurrentModificationException异常
重点:在迭代元素的过程当中,一定要使用迭代器Iterator的remove方法,删除元素,不要使用集合自带的remove方法删除元素。

分析(结合源码):在集合实例中有一个附加的数据域(modCount),用来帮助迭代器检测集合中的变化。modCount代表自从构造以来对集合实例所做修改的次数。每次对addremove调用都将更新modCount。其想法在于,当一个迭代器被创建时,他将存储集合的modCount。每次对一个迭代器方法(nextremove)的调用都将用该集合实例内的当前modCount检测在迭代器内存储的modCount,并且当两个计数不匹配时抛出一个ConcurrentModificationException异常。

为了检测在迭代期间集合被修改的情况,迭代器被构造时的集合实例的modCount存储在数据域excpectedModCount中。

详见源码

public class CollectionDemo {
    public static void main(String[] args) {
        //创建集合
        Collection c = new ArrayList();
        //注意:此时获取的迭代器,指向的是集合中没有元素状态下的迭代器。
        //一定要注意:集合结构只要发生改变,迭代器必须重新获取
        Iterator iterator = c.iterator();//java.util.ConcurrentModificationException
        //添加元素
        c.add("anonymous");
        c.add("joker");
        //重新获取迭代器
        iterator = c.iterator();
        while (iterator.hasNext()){
            //编写代码时next()方法返回值类型必须是Object
            Object obj = iterator.next();
            /*
            * 删除元素
            * 删除元素之后,集合的结构发生了变化,应该重新去获取迭代器。
            * 但是循环下一次的时候并没有重新获取迭代器,所以会出现异常:java.util.ConcurrentModificationException
            * 出现异常的根本原因是:集合中元素删除了,但是没有更新迭代器(迭代器不知道集合变化了)
            * c.remove(o);//直接通过集合去删除元素,没有通知迭代器。(导致迭代器的快照和原集合状态不同)
            * 使用迭代器来删除可以吗?
            * 迭代器去删除时,会自动更新迭代器,并且更新集合(删除集合中的元素)。
            * */
            iterator.remove();//删除的一定是迭代器指向的当前元素
            System.out.println(obj);
        }
        System.out.println(c.size());//0
    }
}

总结重点:

  • 第一个重点:把集合继承结构图背会。
  • 第二个重点:把collection接口中常用方法测试几遍。
  • 第三个重点:把迭代器弄明白。
  • 第四个重点:Collection接 口中的remove方法和contains方法底层都会调用equals,这个弄明白。
List

测试 List 接口中常用方法

  1. List集合存储元素特点:有序可重复
    有序:List集合中的元素有下标。
    从0开始,以1递增。
    可重复:存储一个1 ,还可以再存储1。

  2. List 既然是collection接口的子接口,那么肯定List接口有自己“特色”的方法:
    以下只列出List接口特有的常用的方法:

    void add(int index, E element )
    E get(int index)
    int index0f(Object o)
    int lastIndexOf(Object o)
    E remove(int index)
    E set(int index, E element )
    

ArrayList
ArrayList集合:
在这里插入图片描述

  1. 默认初始化容量10 (底层先创建了一个长度为0的数组,当添加第一个元素的时候,初始化容量10。)

  2. 集合底层是一个0bject[]数组。

  3. 构造方法:

    new ArrayList();
    new ArrayList(20);
    
  4. ArrayList集合的扩容:增长到原容量的1.5倍。
    Arraylist集合底层是数组,怎么优化?
    尽可能少的扩容。因为数组扩容效率比较低,建议在使用Arraylist集合的时候预估计元素的个数,给定一个初始化容量。

  5. 数组优点:检索效率比较高。( 每个元素占用空间大小相同,内存地址是连续的,知道首元素内存地址,然后知道下标,通过数学表达式计算出元素的内存地址,所以检索效率最高。)

  6. 数组缺点:随机增删元素效率比较低。另外数组无法存储大数据量。( 很难找到一块非常巨大的连续的内存空间)

  7. 向数组末尾添加元素,效率很高,不受影响。

  8. 面试官经常问的一个问题?
    这么多的集合中,你用哪个集合最多?
    答:ArrayList集合。因为往数组末尾添加元素,效率不受影响。另外,我们检索/查找某个元素的操作比较多。

  9. ArrayList集合是非线程安全的。(不是线程安全的集合。)

LinkedList

在这里插入图片描述在这里插入图片描述
单链表中的节点:节点是单向链表中基本的单元。
每一个节点Node都有两个属性:一个属性是存储的数据,另一个属性是下一个节点的内存地址。

链表的优点:由于链表上的元素在空间存储上内存地址不连续。所以随机增删元素的时候不会有大量元素位移,团此随机增删效率较高。
在以后的开发中,如果遇到随机增删集合中元素的业务比较多时,建议使用LinkedList。
链表的缺点:不能通过数学表达式计算被查找元素的内存地址,每一次查找都是从头节点开始遍历,直到找到为止。所以Linkedlist集合检索/查找的效率较低。
ArrayList:把检索发挥到极致。( 末尾添加元素效率还是很高的。)
L inkedList:把随机增删发挥到极致。

加元素都是往末尾添加,所以ArrayList用的比LinkedList多。

Linkedlist 集合有初始化容量吗?没有。
最初这个链表中没有任何元素。first和last引用都是null。
不管是LinkedList还是ArrayList ,以后写代码时不需要关心具体是哪个集合。因为我们要面向接口编程,调用的方法都是接口中的方法。

List list2 = new ArrayList(); //这样写表示底层你用了数组。
List list2 = new LinkedList(); //这样写表示底层你用了双向链表。
Vector
  • 底层也是一个数组。
  • 初始化容量:10
  • 怎么扩容的?
    扩容之后是原容量的2倍。 10–> 20 --> 40 --> 80
  • ArrayList集合扩容特点:ArrayList集合扩容是原容量1.5倍。
  • Vector 中所有的方法都是线程同步的,都带有synchronized关键字,是线程安全的。效率比较低,使用较少了。

怎么将一个线程不安全的ArrayList集合转换成线程安全的呢?
使用集合工具类:
java.util.Collections;

//这个可能以后要使用
List arrayList = new ArrayList(); //非线程安全的。
//变成线程安全的
Collections.synchronizedlist(myList);

java.util.Collection:集合接口
java.util.Collections:集合工具类

TreeSet

TreeSet集合存储元素特点:无序不可重复的,但是存储的元素可以自动按照大小顺序排序,称为可排序集合。
无序:这里的无序指的是存进去的顺序和取出来的顺序不同,并且没有下标。

Map

Map和Collection没有继承关系。

  • Map集合以key和value的方式存储数据:键值对

    key和value都是引用数据类型,都是存储对象的内存地址。
    key起到主导的地位, value是key的一个附属品。

java.util.Map接口中常用的方法:

V put(K key, V value) //向Map集合中添加键值对
V get(object key) //通过key获取value
boolean containsKey(Object key) //判断Map中是否包含某个key
boolean containsValue(Object value) //判断Map中是否包含某个value
void clear() //清空Map集合
boolean isEmpty() //判断Map集合中元素个数是否为0
int size() //获取Map集合中键值对的个数
V remove(Object key) //通过key删除键值对
Set<K> keySet() //获取Map集合所有的key(所有的键是一个set集合)
Collection<V> values() //获取Map集合中所有的value,返回一MCollection
Set<Map.Entry<K,V>> entrySet() //将Map集合转换成set集合

Set<Map. Entry<K, V>> entrySet():将Map集合转换成set集合
假设现在有一个Map集合,如下所示:

mapDemo集合对象
key			value
-----------------------------
1			zhangsan
2			lisi
3			wangwu
4			zhaoliu
Set set = mapDemo.entrySet();
set集合对象
1=zhangsan
2=lisi
3=wangwu
4=zhaoliu

注意 : Map集合通过entrySet()方法转换成的这个Set集合,Set集合中元素的类型是Map.Entry<K,V>

Map.Entry和String一样,都是一种类型的名字,只不过: Map.Entry是静态内部类。

遍历Map集合
  • 获取所有的Key,通过遍历所有的Key,来遍历Value
  • 将Map集合直接全部转换成Set集合Set<Map.Entery<K,V>> entrySet()
public class TraversalHashMap {
    public static void main(String[] args) {
        HashMap<Integer,String> hashMap = new HashMap<>();
        hashMap.put(0,"雷军");
        hashMap.put(1,"扎克伯格");
        hashMap.put(2,"张一鸣");
        hashMap.put(3,"丁磊");
//  第一种方式:获取所有的Key,通过遍历所有的Key,来遍历Value
        //获取所有的Key,所有的Key是一个set集合
        Set<Integer> keys = hashMap.keySet();
        //遍历Key,通过Key获取value
        /*Iterator<Integer> iterator = keys.iterator(); //通过迭代器遍历
        while (iterator.hasNext()){
            Integer key = iterator.next();
            System.out.println(key+" : "+hashMap.get(key));
        }*/
        for (Integer key:keys) {//通过foreach遍历
            System.out.println(key+" : "+hashMap.get(key));
        }
//  第二种方式:Set<Map.Entery<K,V>> entrySet()
        //将Map集合直接全部转换成Set集合
        //Set集合中元素的类型是:Map.Entery
        Set<Map.Entry<Integer,String>> set = hashMap.entrySet();
        //比那里Set集合,每一次取出一个Node
        /*Iterator<Map.Entry<Integer,String>> iterator = set.iterator();
        while (iterator.hasNext()){//通过迭代器遍历
            Map.Entry<Integer,String> node = iterator.next();
            Integer key = node.getKey();
            String value = node.getValue();
            System.out.println(key+" : "+value);
        }*/
        //这种方式效率比较高,因为获取key和value是直接从node对象中获取的属性
        //这种方式比较适合大数据量
        for (Map.Entry<Integer,String> node:set) {//通过foreach遍历
            System.out.println(node.getKey() + " : " + node.getValue());
        }
    }
}

Map.Entry<K,V>源码

/**
* Basic hash bin node, used for most entries. (See below for
* TreeNode subclass, and in L inkedHashMap for its Entry subclass. )
*/
static class Node<K,V> imp1ements Map.Entry<K,V> {
    final int hash;
    fina1 K key;
    V value;
    Node<K,V> next;
    
    Node(int hash, K key, V value, Node<K,V> next) {
        this.hash = hash;
        this.key = key;
        this.value = value;
        this.next = next;
    }
}
HashMap

在这里插入图片描述
HashMap集合底层是哈希表/散列表的数据结构。
哈希表是一个怎祥的数据结构呢?
哈希表是一个数组和单向链表的结合体。

  • 数组:在查询方面效率很高,随机增删方面效率很低。
  • 单向链表:在随机增删方面效率较高,在查询方面效率很低。

哈希表将以上的两种数据结构融合在一起,充分发挥它们各自的优点。

HashMap集合底层的源代码:

public class HashMap{
    // HashMap底层实际上就是一个数组。(一维数组 )
    Node<K, V>[] table;
    //静态的内部类HashMap.Node
    static class Node<K,V> {
        final int hash; //哈希值(哈希值是key的hashCode()方法的执行结果。hash 值通过哈希函数/算法,可以转换存储成数组的下标。)
        final K key; //存储到Map集合中的那个key
        V value; //存储到Map集合中的那个value
        Node<K,V> next; //下一个节点的内存地址。
}

哈希表/散列表:一维数组,这个数组中每一个元素是一个单向链表。( 数组和链表的结合体。)

最主要掌握的是:

map.put(k,v)
V = map.get(k)

以上这两个方法的实现原理,是必须掌握的。

HashMap集合的key部分特点:
无序,不可重复。
为什么无序?因为不一定挂到哪个单向链表上。
不可重复是怎么保证的?equals 方法来保证HashMap集合的key不可重复。如果key重复了, value会覆盖。
**放在HashMap集合key部分的元素其实就是放到HashSet集合中了。**所以HashSet集合中的元素也需要同时重写hashCode()、equals()方法。

哈希表HashMap 使用不当时无法发挥性能:
假设将所有的hashCode()方法返回值固定为某个值,那么会导致底层哈希表变成了纯单向链表。这种情况我们成为:散列分布不均匀。
什么是散列分布均勻?
假设有100个元素, 10个单向链表,那么每个单向链表上有10个节点,这是最好的,是散列分布均匀的。
假设将所有的hashCode()方法返回值都设定为不一样的值,可以吗,有什么问题?
不行,因为这祥的话导致底层哈希表就成为一维数组了,没有链表的概念了。也是散列分布不均匀。
散列分布均匀需要你重写hashCode()方法时有一定的技巧。
重点:放在HashMap集合key部分的元素,以及放在HashSet集合中的元素,需要同时重写hashCode() equals()方法。

HashMap集合的默认初始化容量16,默认加载因子0.75
这个默认加载因子是当HashMap集合底层数组的容量达到75%的时候,数组开始扩容。
重点,记住:HashMap集合初始化容量必须是2的倍数,这也是官方推荐的,这是因为达到散列均匀,为了提高HashMap集合的存取效率,所必须的。
在这里插入图片描述
重写hashCode()和equals()方法

向Map集合中存,以及从Map集合中取,都是先调用key的hashCode方法,然后再调用equals方法。equals方法有可能调用,也有可能不调用。
拿put(k,v)举例,什么时候equals不会调用?
k. hashCode()方法返回哈希值,哈希值经过哈希算法转换成数组下标。数组下标位置上如果是null , equals不需要执行。
拿get(k)举例,什么时候equals不会调用?
k. hashCode()方法返回哈希值,哈希值经过哈希算法转换成数组下标。数组下标位置上如果是null , equals不需要执行。

注意:如果一个类的equals方法重写了,那么hashCode()方法必须重写。
equals()方法返回如果是true,hashCode()方法返回的值必须一样。equals方法返回true表示两个对象相同,在同一个单向链表上比较。那么对于同一个单向链表上的节点来说,他们的哈希值都是相同的。所以hashCode()方法的返回值也应该相同。

hashCode()方法和equals()方法不用研究了,直接使用IDEA工具生成,但是这两个方法需要同时生成。
终极结论:放在HashMap集合key部分的,以及放在HashSet集合中的元素,需要同时重写hashCode()方法和equals()方法。

对于哈希表数据结构来说:如果o1和o2的hash值相同,一定是放到同一个单向链表上。当然如果o1和o2的hash值不同,但由于哈希算法执行结束之后转换的数组下标可能相同,此时会发生**“哈希碰撞”**。

**哈希碰撞:**不同的哈希值经过哈希算法得到的数组下标相同。

Hashtable

在这里插入图片描述

Hashtable的key可以为null吗?

  • Hashtable的key和value都是不能为null的。
  • HashMap集合的key和value都是可以为null的。

Hashtable方法都带有synchronized——线程安全的。线程安全有其它的方案,这个Hashtable对线程的处理导致效率较低,使用较少了。

Hashtable和HashMap一样,底层都是哈希表数据结构。
Hashtable的初始化容量11,默认加载因子是:0.75f
Hashtable的扩容是:原容量*2+1

/**
* Constructs a new, empty hashtable with a default initial capacity (11)
* and load factor (0.75).
*/
public Hashtable() {
	this( initialCapacity: 11, loadFactor: 0. 75f) ;
}
Properties

Properties是一个Map集合,继承Hashtable,Properties的key和value都是String类型。
Properties被称为属性类对象
Properties是线程安全的。

/*@param key	the key to be placed into this property list.
* @param value	the value corresponding to {@code key}.
* @return 	the previous value of the specified key in this property
*			list, or {@code null} if it did not have one.
* @see #getProperty
* @since 1.2
*/
public synchronized object setProperty(String key, String value) {
    return put(key, value) ;
}
public class PropertiesTest {
	public static void main(String[] args) {
        //创建一个Properties对象
        Properties pro = new Properties();
        //需要掌握roperties的两个方法,一个存,一个取。
        pro.setProperty("ur1", "jdbc :mysq1://localhost: 3306/bjpowernode");
        pro.setProperty( "driver" , "com. mysq1. jdbc .Driver");
        pro.setProperty("username", "root");
        pro.setProperty("password", "123");
        //通过key获取value
        String url = pro.getProperty("ur1");
        String driver = pro.getProperty("driver");
        String username = pro.getProperty( "username" );
        String password = pro.getProperty("password");
        System.out.println(url);
        System.out.println(driver);
        System.out.println(username);
        System.out.println(password);
	}
}
TreeSet(HashSet)

在这里插入图片描述

数据库中有很多数据:
userid		name		birth
1			 ZS		 1980-11-11
2			 ls		 1980-10-11
3			 WW		 1981-11- 11
4			 zl		 1979-11-1I

编写程序从数据库当中取出数据,在页面展示用户信息的时候按照生日升序或者降序。这个时候可以使用TreeSet集合,因为TreeSet集合放进去,拿出来就是有顺序的。

TreeSet 集合底层实际上是一个TreeMap,TreeMap 集合底层是一个二叉树
放到TreeSet集合中的元素,等同于放到TreeMap集合key部分了。
TreeSet 集合中的元素:无序不可重复,但是可以按照元素的大小顺序自动排序。称为:可排序集合

TreeSet无法对自定义类排序

对自定义的类型来说,TreeSet可以排序吗?
以下程序中对于Person类型来说,无法排序。因为没有指定Person对象之间的比较规则。谁大谁小并没有说明。
以下程序运行的时候出现了这个异常:

java.lang.ClassCastException:
class com.mycompany.myproject.mypackage.Person
cannot be cast to class java.lang.Comparable

出现这个异常的原因是:Person类没有实现java.lang.Comparable接口。

compareTo方法的返回值很重要:

  • 返回0表示相同,value会覆盖。
  • 返回>θ,会继续在右子树上找。
  • 返回<θ,会继续在左子树上找。
    在这里插入图片描述

Set集合中元素不能通过下标取出(不存在下标)。

实现比较器Comparator接口

TreeSet集合中元素可排序的第二种方式:使用比较器的方式。

放到TreeSet或者TreeMap集合key部分的元素要想做到排序,包括两种方式:

  • 第一种:放在集合中的元素实现java. lang.Comparable接口。
  • 第二种:在构造TreeSet或者TreeMap集合的时候给它传一个比较器对象。

Comparable和Comparator怎么选择呢?
当比较规则不会发生改变的时候,或者说当比较规则只有1个的时候,建议实现comparable接口。如果比较规则有多个,并且需要多个比较规则之间频繁切换,建议使用Comparator接口。
Comparator接口的设计符合OCP原则。

编写比较器的方式:

  • 单独编写一个比较器:比较器需要实现java.util.Comparator接口。

    class XXXComparator implements Comparator<T>{
        @override
        public int compare(T t1, T t2){
            //...
            return 
        }
    }
    TreeSet<T> treeSet = new TreeSet<>(new XXXComparator());
    

    Comparablejava.lang包下的。Comparator是`java.util包下的。

  • 使用匿名内部类的方式直接new接口并重写接口内的方法。

    TreeSet<T> treeSet = new TreeSet<>(new Comparator<T>{
        @override
        public int compare(T t1, T t2){
            //...
            return 
        }
    })
    

Collections工具类

java.util.Collection//集合接口
java.util.Collections//集合工具类,方便集合的操作
ArrayList<String> list = new ArrayList<>();
Collections.synchronizedList(list);//将线程不安全的ArrayList集合变成线程安全的集合
Collections.sort(list);//对实现Comparable接口的集合排序。
Collections.sort(list,比较器对象)//也可以使用这种方式实现排序
  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值