集合

集合

集合框架的概述

  1. 集合、数组都是对多个数据进行存储(内存层面的存储)操作的结构,简称java容器
  2. 数组在存储多个数据方面的特点
    • 一旦初始化以后,其长度确认了。
    • 数组一旦定义好,其元素的类型也就确定了,我们也就只能操作指定类型的数据了。
  3. 数组在存储多个数据方面的缺点
    • 一旦初始化以后,其长度不可修改了。
    • 数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便,同时效率不高。
    • 获取数组中实际元素的个数的需求,数组没有现成的属性或方法可用。
    • 数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足。

集合框架

Collection接口单列集合,用来存储一个一个的对象

  • List接口:存储有序的、可重复的数据。---->”动态“数组
    • ArrayList:作为List接口的主要实现类;线程不安全,效率高;底层使用Object[] elementData存储
    • LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储
    • Vector:作为List接口的古老实现类;线程安全的,效率低;底层使用Object[] elementData存储
  • Set接口:存储无序的、不可重复的数据 ----->数学意义上的集合。
    • HashSet:作为Set接口的主要实现类;线程不安全,可以存储null值
      • LinkedHashSet:作为HashSet的子类,遍历其内部数据时,可以按照添加的顺序遍历,对于频繁的遍历操作,LinkedHashSet效率高于HashSet
    • TreeSet:可以按照添加对象的指定属性进行排序。

Map接口:双列集合,用来存储一对(key - value)一对的数据

  • HashMap:作为Map的主要实现类:线程不安全,效率高;可以存储null的key和value(底层时数组+链表+红黑树)

    • LinkedHashMap:保证遍历map元素时,可以按照添加的顺序实现遍历。

      原因:在原有的HashMap底层结构的基础上,添加了一对指针,指向前一个和后一个元素,对于频繁的遍历操作,此类执行效率高于HashMap。

  • TreeMap:保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序。底层使用红黑树

  • Hashtable:作为古老实现类:线程安全,效率低

    • Properties:常用来处理配置文件。key和value都是String问题。

Collection接口中的方法

向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals().

package collectionTest;

import org.junit.jupiter.api.Test;

import java.util.*;

public class CollectionTest {
    @Test
    public void test(){
        Collection col1 = new ArrayList();

        //add(object e):将元素e添加到集合col1中
        col1.add("AA");
        col1.add("BB");
        col1.add(123);
        col1.add(new Date());

        //size():获取添加的元素的个数
        System.out.println(col1.size());//4

        //addAll():
        Collection col2 = new ArrayList();
        col2.add(456);
        col2.add("CC");
        col1.addAll(col2);
        System.out.println(col1.size());//6
        System.out.println(col1);

        //clear():清空元素
        col1.clear();

        //isEmpty():
        System.out.println(col1.isEmpty());//true

    }

    @Test
    public void test1(){
        Collection col1 = new ArrayList();
        col1.add(123);
        col1.add(456);
        col1.add(new String("Tom"));
        col1.add(false);
        col1.add(new Person("Jerry",20));

        //contains(Object obj):判断当前集合是否包含obj,在判断时会调用所在类的equals()。
        

        boolean contains = col1.contains(123);
        System.out.println(contains);//true
        System.out.println(col1.contains(new String("Tom")));//true
        System.out.println(col1.contains(new Person("Jerry",20)));//true

        //containsAll(Collection col2):判断形参col1中的所有元素是否都存在与当前集合中
        Collection col2 = Arrays.asList(123,456);
        System.out.println(col1.containsAll(col2));//true

    }

    @Test
    public void test2(){
        Collection col1 = new ArrayList();
        col1.add(123);
        col1.add(456);
        col1.add(new Person("Jerry",20));
        col1.add(new String("Tom"));
        col1.add(false);
        //remove(Object obj):从当前集合中移除obj元素
        col1.remove(123);
        System.out.println(col1);
        col1.remove(new Person("Jerry",20));
        System.out.println(col1);

        //removeAll(Collection col2):从当前集合中移除col2中所有的元素
        Collection col2 = Arrays.asList(123,456);
        col1.removeAll(col2);
        System.out.println(col1);
    }

    @Test
    public void test3(){
        Collection col1 = new ArrayList();
        col1.add(123);
        col1.add(456);
        col1.add(new Person("Jerry",20));
        col1.add(new String("Tom"));
        col1.add(false);

        Collection col2 = Arrays.asList(123,456,false,3333);

        //retainAll(Collection col2):交集:获取当前集合和col2的交集并返回给col1
        col1.retainAll(col2);
        System.out.println(col1);
        System.out.println("=================");


        //equals(Collection col3):如果是true,则当前集合和形参集合元素都相同
        Collection col3 = new ArrayList();
        col3.add(123);
        col3.add(456);
        col3.add(new Person("Jerry",20));
        col3.add(new String("Tom"));
        col3.add(false);

        Collection col4 = new ArrayList();
        col4.add(456);
        col4.add(123);
        col4.add(new Person("Jerry",20));
        col4.add(new String("Tom"));
        col4.add(false);
        System.out.println(col4.equals(col3));//false
    }
    @Test
    public void test4(){
        Collection col1 = new ArrayList();
        col1.add(123);
        col1.add(456);
        col1.add(new Person("Jerry",20));
        col1.add(new String("Tom"));
        col1.add(false);

        //hashCode():返回当前对象的hash值
        System.out.println(col1.hashCode());//-1200490100

        //toArray():集合--->数组
        Object[] arr = col1.toArray();
        for (Object i:arr) {
            System.out.println(i);
        }
        //调用Arrays类的静态方法asList();数组--->集合
        List<Object> list = Arrays.asList(arr);
        System.out.println(list);


        //注意
        List<int[]> ints = Arrays.asList(new int[]{123, 456});
        System.out.println(ints.size());//1

        List<Integer> integers = Arrays.asList(new Integer[]{123, 4565, 234});
        System.out.println(integers);


    }


}

迭代器Iterator的使用

主要用来遍历Collection,不遍历Map。

image-20201205222653706

  • 集合元素的遍历操作,使用迭代器Iterator接口
  • 内部的方法:hasNext()、next()
package collectionTest;

import org.junit.jupiter.api.Test;

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

public class IteratorTest {
    @Test
    public void test(){
        Collection col1 = new ArrayList();
        col1.add(123);
        col1.add(456);
        col1.add(new Person("Jerry",20));
        col1.add(new String("Tom"));
        col1.add(false);

        Iterator iterator = col1.iterator();//创建一个迭代器对象
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }

        //错误示范:每执行一次col1.iterator()就创建一个新的迭代器对象
        while (col1.iterator().hasNext()){
            System.out.println(col1.iterator().next());//死循环输出123
        }

    }
}

image-20201205221551640

image-20201205223740788

@Test
public void test2(){
    Collection col1 = new ArrayList();
    col1.add(123);
    col1.add(456);
    col1.add(new Person("Jerry",20));
    col1.add(new String("Tom"));
    col1.add(false);

    Iterator iterator = col1.iterator();
    while (iterator.hasNext()){
        Object obj = iterator.next();
        if ("Tom".equals(obj)){
            iterator.remove();
        }
    }
    Iterator iterator1 = col1.iterator();
    while (iterator1.hasNext()){
        System.out.println(iterator1.next());
    }

}

ArrayList源码分析

ArrayList arrayList = new ArrayList();//底层创建了一个elementData[] {}
        arrayList.add(123);//将element[]长度扩容到10
		...
        arrayList.add(11);//如果此次的添加导致底层elementData数组容量不够,则扩容
        /*
        默认情况下,扩容为原来容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。

        结论:建议开发中使用带参的构造器:ArrayList arrayList = new ArrayList(int capacity)
         */

小结:

jdk7中的ArrayList的对象的创建类似于单例模式的饿汉式,而jdk8中的ArrayList的对象的创建类似于单例模式的懒汉式,延迟了数组的创建,节省内存。

LinkedList源码分析

image-20201206000952005

实例化时:

LinkedList linkedList = new LinkedList();

声明Node类型的两个属性默认值为null:

transient Node<E> first;
transient Node<E> last;
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;
    }
}

add操作:

linkedList.add(123);
public boolean add(E e) {
    linkLast(e);
    return true;
}

当链表里没有元素时,newNode就是last也是first

当有一个元素时,newNode是last,first.next是newNode

当有多个元素时,先把last赋值给之前的last,然后new一个新Node,然后把新Node也就是newNode赋给last。

void linkLast(E e) {
    final Node<E> l = last;
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}

由于是链表所以不用扩容

Vector源码分析

Vector vector = new Vector();//底层创建一个elementData[]大小为10 

扩容是扩容到原来的两倍

List接口方法

image-20201206001039710

@Test
    public void test(){
        ArrayList arrayList = new ArrayList();
        arrayList.add(123);
        arrayList.add(456);
        arrayList.add(new Person("Jerry",20));
        arrayList.add(new String("Tom"));
        arrayList.add(false);
        arrayList.add(3,"ele");
        ArrayList arrayList1 = new ArrayList();
        arrayList1.add(123);
        arrayList1.add(13);
        arrayList1.add(3);
        arrayList.addAll(arrayList1);

        Iterator iterator = arrayList.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
        System.out.println("==================");
        System.out.println(arrayList.get(3));
        System.out.println(arrayList.indexOf("ele"));
        Object index_3 = arrayList.remove(3);
        System.out.println(index_3);
        System.out.println("==================");
        Iterator iterator1 = arrayList.iterator();
        while (iterator1.hasNext()){
            System.out.println(iterator1.next());
        }

        arrayList.set(3,"ele");
        System.out.println(arrayList);

        System.out.println(arrayList.subList(0, 2));

    }

Set接口

image-20201206004918747

HashSet

private static final Object PRESENT = new Object();
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

一、

  1. 无序性:不等同于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定位置。
  2. 不可重复性:保证添加的元素按照equals()判断时,不能返回true,即相同元素只有一个。

二、添加元素的过程:

向HashSet中添加元素a,首先调用元素a所在类的hashcode()方法,计算元素a的哈希值,此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置),判断数组此位置上是否已经有元素:

  • 如果没有,则添加成功。
  • 如果有其他元素或以链表形式存在的多个元素,则比较元素a与其他元素的哈希值:
    • 如果哈希值不相同,则元素a添加成功。
    • 如果哈希值相同,进而需要调用元素a所在类的equals()方法:
      • equals()返回true,则添加失败。
      • equals()返回false,则添加成功。

向Set中添加的数据,其所在类一定要重写hashCode()和equals()

  • 重写的hashCode()和equals()尽可能保持一致性:相等的对象必须具有相同的哈希值。
  • 重写两个方法的技巧:对象中用作equals()方法比较的值,都应该用来计算哈希值。
@Test
public void test(){
    Set set = new HashSet();
    set.add(456);
    set.add(123);
    set.add("AA");
    set.add("CC");
    set.add(new User("Tom",12));
    set.add(new User("Tom",12));
    set.add(129);

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

public class 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 boolean equals(Object o) {
        System.out.println("User equals");
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        User user = (User) o;

        if (age != user.age) return false;
        return name != null ? name.equals(user.name) : user.name == null;
    }

        @Override
        public int hashCode() {
            int result = name != null ? name.hashCode() : 0;
            result = 31 * result + age;
            return result;
    }
}

LinkedHashSet

在添加数据时,每个数据还维护了两个引用,记录此数据的前一个数据和后一个数据。

image-20201206141851642

TreeSet

@Test
public void test(){
    TreeSet set = new TreeSet();

    set.add(new User("Tom",12));
    set.add(new User("See",22));
    set.add(new User("Fsdfs",32));
    set.add(new User("Jsedr",2));
    set.add(new User("Tim",62));
    set.add(new User("Tim",42));


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

public class User implements Comparable{
    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 boolean equals(Object o) {
        System.out.println("User equals");
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        User user = (User) o;

        if (age != user.age) return false;
        return name != null ? name.equals(user.name) : user.name == null;
    }

        @Override
        public int hashCode() {
            int result = name != null ? name.hashCode() : 0;
            result = 31 * result + age;
            return result;
    }

    //按照姓名从小到大排列
    @Override
    public int compareTo(Object o) {
        if (o instanceof User) {
            User user = (User) o;
            //return this.name.compareTo(user.name);
            int i = this.name.compareTo(user.name);
            if (i!=0){
                return i;
            }else
                return Integer.compare(this.age,user.age);

        }else
            throw new ClassCastException("输入的类型不匹配");
    }
}

自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,不再是equals().image-20201206145452091

在定制排序中,比较两个对象是否相同的标准为:compare()返回0,不再是equals()。~

@Test
public void test1(){
    Comparator comparator = new Comparator() {
        @Override
        public int compare(Object o1, Object o2) {
            if (o1 instanceof User && o2 instanceof User){
                User user1 = (User)o1;
                User user2 = (User)o2;
                return Integer.compare(user1.getAge(),user2.getAge());

            }else {
                throw new ClassCastException("数据不对");
            }

        }
    };
    TreeSet set = new TreeSet(comparator);
    set.add(new User("Tom",12));
    set.add(new User("See",22));
    set.add(new User("Fsdfs",32));
    set.add(new User("Jsedr",2));
    set.add(new User("Tim",62));
    set.add(new User("Tim",42));

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

public class User implements Comparable{
    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 boolean equals(Object o) {
        System.out.println("User equals");
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        User user = (User) o;

        if (age != user.age) return false;
        return name != null ? name.equals(user.name) : user.name == null;
    }

        @Override
        public int hashCode() {
            int result = name != null ? name.hashCode() : 0;
            result = 31 * result + age;
            return result;
    }

    //按照姓名从小到大排列
    @Override
    public int compareTo(Object o) {
        if (o instanceof User) {
            User user = (User) o;
            //return this.name.compareTo(user.name);
            int i = this.name.compareTo(user.name);
            if (i!=0){
                return i;
            }else
                return Integer.compare(this.age,user.age);

        }else
            throw new ClassCastException("输入的类型不匹配");
    }
}

排序方式

自然排序:实现Comparable接口

定制排序:实现Comparator接口

Map

  • HashMap的底层实现原理?
  • HashMap和Hashtable的异同?
  • CurrentHashMap与Hashtable的异同?

Map结构:

Map中的key:无序的、不可重复的,使用set存储所有的key。-->(以HashMap为例)key所在的类要重写equals()和)Code()

Map中的value:无序的、可重复的,使用Collention存储所有的value。-->value所在的类要重写equals()

一个键值对:key-value构成了一个Entry对象。

Map中的Entry:无序的、不可重复的,使用set存储所有的entry。

image-20201210223648298

HashMap的底层实现原理?(jdk7)

HashMap map = new HashMap();

在实例化以后,底层创建了长度是16的一维数组Entry[] table。
...可能已经执行过很多次put...

map.put(key1,value1);

key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置。
如果此位置上的数据为空,此时的key1-value1添加成功。
如果此位置上的数据不为空,比较key1和已经存在的一个或多个数据的哈希值:
如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功。 如果key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)
如果equals()返回false:此时key1-value1添加成功。
如果equals()返回true:使用value替换value2。
扩容问题,当超出临界值(且要存放的位置非空)时,扩容。默认的扩容方式;扩容为原来容量的2倍,并将原有的数据复制过来。

jdk8与7的不同

  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);
    }
}

Map接口:常用方法

image-20201214182858081

TreeMap

package map;

import list.User;
import org.junit.jupiter.api.Test;

import java.util.*;

public class TreeMapTest {
    /**
     * 向TreeMap中添加key-value,要求key必须是由同一个类创建的对象
     * 因为要按照key进行排序:自然排序、定制排序
     *自然排序
     */
    @Test
    public void test() {
        TreeMap treeMap = new TreeMap();
        User user1 = new User("Tom", 24);
        User user2 = new User("Jerry", 33);
        User user3 = new User("Jack", 23);
        User user4 = new User("Marry", 24);
        User user5 = new User("Rose", 45);

        treeMap.put(user1,11);
        treeMap.put(user2,22);
        treeMap.put(user3,33);
        treeMap.put(user4,44);
        treeMap.put(user5,55);

        Set set = treeMap.entrySet();
        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            Object next = iterator.next();
            Map.Entry entry = (Map.Entry) next;
            System.out.println(entry.getKey()+"----->"+entry.getValue());
        }
    }

    /**
     * 定制排序
     */
    @Test
    public void test1() {
        TreeMap treeMap = 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 user1 = new User("Tom", 24);
        User user2 = new User("Jerry", 33);
        User user3 = new User("Jack", 23);
        User user4 = new User("Marry", 24);
        User user5 = new User("Rose", 45);

        treeMap.put(user1,11);
        treeMap.put(user2,22);
        treeMap.put(user3,33);
        treeMap.put(user4,44);
        treeMap.put(user5,55);

        Set set = treeMap.entrySet();
        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            Object next = iterator.next();
            Map.Entry entry = (Map.Entry) next;
            System.out.println(entry.getKey()+"----->"+entry.getValue());
        }
    }
}
package list;

public class User implements Comparable{
    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 boolean equals(Object o) {
        System.out.println("User equals");
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        User user = (User) o;

        if (age != user.age) return false;
        return name != null ? name.equals(user.name) : user.name == null;
    }

        @Override
        public int hashCode() {
            int result = name != null ? name.hashCode() : 0;
            result = 31 * result + age;
            return result;
    }

    //按照姓名从小到大排列
    @Override
    public int compareTo(Object o) {
        if (o instanceof User) {
            User user = (User) o;
            //return this.name.compareTo(user.name);
            int i = this.name.compareTo(user.name);
            if (i!=0){
                return i;
            }else
                return Integer.compare(this.age,user.age);

        }else
            throw new ClassCastException("输入的类型不匹配");
    }
}

Properties

Properties类是Hashtable的子类,该对象用于处理属性文件

由于属性文件里的key、value都是字符串类型,所以Properties里的key和value都是字符串类型

存取数据时,建议使用setProperty(String key,String value)方法和getProperty(String key)方法

package map;

import java.io.FileInputStream;
import java.util.Properties;

public class PropertiesTest {
    public static void main(String[] args) throws Exception {
        Properties properties = new Properties();
        FileInputStream fileInputStream = new FileInputStream("jdbc.properties");

        properties.load(fileInputStream);
        String pro1 = properties.getProperty("name");
        String pro2 = properties.getProperty("password");
        System.out.println("name=" + pro1 + " , "+"password="+pro2 );
    }
}
name=Tom
password=123

Collections工具类

image-20201214221410035

image-20201214221427860

image-20201214221437205

posted on 2020-12-15 01:32  kikikikikiku  阅读(95)  评论(0编辑  收藏  举报