Java集合基础知识学习笔记

 B站视频指路:尚硅谷Java入门视频教程(在线答疑+Java面试真题)_哔哩哔哩_bilibili

写在前面:马上秋招,打算从0开始再学一遍Java,开个知识点记录贴,就当做课堂笔记吧.

Java结合框架概述:

        1.一方面,面向对象语言对事物的体现都是以对象的形式,为了方便对多个对象的操作,就要对对象进行存储.另一方面,使用Array存储对象方面具有一些弊端,而Java集合就像一种容器,可以动态的把多个对象的引用放入容器中
               ·数组在内存存储方面的特点:
                        ①数组初始化后,长度就确定了
                        ②数组声明的类型,就决定了进行元素初始化时的类型
                ·数组在存储数据方面的弊端:
                        ①数组在初始化以后,长度就不可变了,不便于扩展
                        ②数组中提供的属性和方法少,不便于进行添加、删除、插入等操作,且效率不高,同
                时无法直接获取存储元素的个数.
                        ③数组存储的数据是有序的、可以重复的---->存储数据的特点比较单一
                        ④数组存储数据的特点:有序、可重复.对于无序、不可重复的需求,不能满足

        2.Java集合类可以用于存储数量不等的多个对象,还可以用于保存具有映射关系的关联数组

        3.集合、数组都是对多个数据进行存储操作的结构,简称Java容器
                        tips:此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储(.txt,.jpg.avi,数据库中)

 

Java集合分为Collection和Map两种体系
        ·Collection接口:单例数据、定义了存取一组对象的方法的集合,用来存储一个一个的对象
                ①List:元素有序、可重复的集合--->"动态数组"
                        ArrayList、LinkedList、Vector
       
 List接口中ArrayList、LinkedList都不是线程安全,Vector是线程安全
                ②Set:元素无序、不可重复的集合--->高中的"集合"概念
                        HashSet、LinkedHashSet、TreeSet
        ·Map接口:双列数据,保存具有映射关系"Key-value对"的集合 y=f(x)
                        HashMap、LinkedHashMap、TreeMap、Hashtable、Properties
        hashmap treemap 都是非线程安全的,currenthashmap, hashTable 是线程安全的

       

 Collection接口中的方法的使用

import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;

public class CollectionTest {
    @Test
    public void test(){
        Collection coll = new ArrayList();
        Collection coll1 = new ArrayList();
        //add():将元素e添加到集合coll中
        coll.add("ZK");
        coll.add("ZK");
        coll.add(234234);//自动装箱
        coll.add(new Date());
        //size():获取添加的元素的个数
        System.out.println(coll.size());//4
        //addAll(Collection coll1):将coll1集合中的元素添加到当前的集合中
        coll1.add(123456);
        coll1.add('s');
        coll.addAll(coll1);
        System.out.println(coll.size());//6
        System.out.println(coll);
        //isEmpty():判断当前集合是否为空
        System.out.println(coll.isEmpty());
    }
}

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

import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;

public class CollectionTest {
    @Test
    public void test(){
        Collection coll = new ArrayList();
        coll.add(123455);
        coll.add(1);
        coll.add(new String("ZAHNGKE"));
        coll.add(new person("tjw",18));
        coll.add(true);
        //contains(Object obj):判断当前集合中是否包含obj  调用的是obj对象所在类的equals方法
//        equals没有重写 用的是object里的 ==
        System.out.println(coll.contains(new String("ZAHNGKE")));//true
//        这个new String("ZAHNGKE")和上面的new String("ZAHNGKE") 是两个对象
//            所以可以得出结论 contains判断的是内容 不是地址  调用的是equals 不是 == 因为String重写了equals
        System.out.println(coll.contains(new person("tjw",18)));//false 这里equals没有重写 用的是object里的 ==
        //containsAll(Collecetion coll1):判断形参coll1中所有元素是否都存在于当前集合中
        Collection coll1 = Arrays.asList(123455);
        System.out.println(coll.containsAll(coll1));
    }
}

import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;

public class CollectionTest {
    @Test
    public void test(){
        Collection coll = new ArrayList();
        coll.add(123455);
        coll.add(1);
        coll.add(new String("ZAHNGKE"));
        coll.add(new person("tjw",18));
        coll.add(true);
        //remove(Object obj):从当前集合中移除obj  也要重写equals 因为首先要判断 集合内有没有
        //所以要从第一个元素开始挨个equals
        coll.remove(new person("tjw",18));
        System.out.println(coll);
        //removeAll(Collection coll1):从当前集合中移除coll1中所有的元素 即 移除两个集合的交集
        Collection coll1 = Arrays.asList(1,123455);
        coll.removeAll(coll1);
        System.out.println(coll);
    }
}

import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;

public class CollectionTest {
    @Test
    public void test(){
        Collection coll = new ArrayList();
        coll.add(123455);
        coll.add(1);
        coll.add(new String("ZAHNGKE"));
        coll.add(new person("tjw",18));
        coll.add(true);
        //retainAll(Collection coll1):交集: 获取当前集合和coll1集合的交集 并返回给当前集合
//        Collection coll1 = Arrays.asList(123,123455,true);
//        coll.retainAll(coll1);
//        System.out.println(coll);
        //equals(Object obj):若要返回true,需要当前集合和形参集合元素都相同
        Collection coll1 = new ArrayList();
        coll1.add(123455);
        coll1.add(1);
        coll1.add(new String("ZAHNGKE"));
        coll1.add(new person("tjw",18));
        coll1.add(true);
        System.out.println(coll.equals(coll1));
    }
}
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

public class CollectionTest {
    @Test
    public void test(){
        Collection coll = new ArrayList();
        coll.add(123455);
        coll.add(1);
        coll.add(new String("ZAHNGKE"));
        coll.add(new person("tjw",18));
        coll.add(true);
        //hashCode():返回当前对象的hash值
        System.out.println(coll.hashCode());
        //集合-->数组:toArray()
        Object[] objects = coll.toArray();
        for (int i = 0; i < objects.length; i++) {
            System.out.println(objects[i]);
        }
        //拓展:数组-->集合:调用Arrays类的静态方法asList()
        List<String> strings = Arrays.asList(new String[]{"AA", "BB", "CC"});
        System.out.println(strings);
       List ints = Arrays.asList(new int[]{123, 456});//基本数据类型不行 要用包装类
        System.out.println(ints+","+ints.size());
        List integers = Arrays.asList(new Integer[]{123, 456});
        System.out.println(integers+","+integers.size());
     
    }
}

import org.junit.Test;

import java.util.*;

public class CollectionTest {
    @Test
    public void test(){
        Collection coll = new ArrayList();
        coll.add(123455);
        coll.add(1);
        coll.add(new String("ZAHNGKE"));
        coll.add(new person("tjw",18));
        coll.add(true);
        //iterator():Iterator迭代器接口,返回Iterator接口的实例,用于遍历集合元素.放在IteratorTest.java中测试
        Iterator iterator = coll.iterator();
        //方式一:
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
        //方式二不推荐:
//        for (int i = 0; i < coll.size(); i++) {
//            System.out.println(iterator.next());
//        }
        //方式三:推荐
        //next():指针下移 将下移以后集合位置上的元素返回
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}

错误方式2对应了

        Iterator iterator = coll.iterator();  即 每次coll.iterator()都会生成一个新的迭代器对象,而我们要做的是使用同一个 然后不断下移

 

import org.junit.Test;
import java.util.*;
public class CollectionTest {
    @Test
    public void test(){
        Collection coll = new ArrayList();
        coll.add(123455);
        coll.add(1);
        coll.add(new String("ZAHNGKE"));
        coll.add(new person("tjw",18));
        coll.add(true);
        //iterator():Iterator迭代器接口,返回Iterator接口的实例,用于遍历集合元素.放在IteratorTest.java中测试
        Iterator iterator = coll.iterator();
        while (iterator.hasNext()){
            Object o = iterator.next();
            if("ZAHNGKE".equals(o)){
                iterator.remove();
            }
        }
         iterator = coll.iterator();
        while (iterator.hasNext()){//需要重新从头开始
            System.out.println(iterator.next());
        }

    }
}

 使用foreeach循环遍历集合元素
        ·Java5.0提供了foreach循环迭代访问Collection和数组
        ·遍历操作不需要获取Collection或数组的长度,无需使用索引访问元素
        ·遍历集合的底层调用Iterator完成操作
        ·foreach还可以用来遍历数组


 

import org.junit.Test;

import java.util.*;

public class ForTest {
    @Test
    public void test(){
        Collection coll = new ArrayList();
        coll.add(123455);
        coll.add(1);
        coll.add(new String("ZAHNGKE"));
        coll.add(true);
        //for(集合中元素的类型 局部变量  :  集合对象)
        //内部仍然用了迭代器
        for(Object obj : coll){
            System.out.println(obj);
        }
    }
    @Test
    public void test2(){
       int []arr = new int[]{1,2,3,4,5,6};
        //for(数组元素的类型 局部变量  :数组对象)
        for(int obj : arr){
            System.out.println(obj);
        }
    }
    @Test
    public void test3(){
        String[] arr = new String[]{"MM","MM","MM"};
//        //方式1:普通的for循环赋值
//        for (int i = 0; i < arr.length; i++) {
//            arr[i] = "GG";  /// GG GG GG 
//        }
        //方式2:增强for循环
        for (String s:arr //这是把arr赋值给s arr本身不变
             ) {
              s = "GG";
        }
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]); MM MM MM
        }
    }
}

Colletion子接口之一:List接口 
          ·鉴于Java中数组用来存储数据的局限性,我们通常使用List替代数组
          ·List集合类中元素有序、且可重复,集合中的每个元素都有其对应的顺序索引.
          ·List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素
          ·JDK API中List接口的实现类常用的就有:ArrayList、LinkedList和Vector

面试题:ArrayList、LinkedList、Vector的异同
        相同点:三个类都实现类List接口,存储数据的特点相同:存储有序的、可重复的数据
        不同点:
                 ①ArrayList: 作为List接口的主要实现类.线程不安全的,效率高.底层使用Object[ ] elementData存储
                 ②LinkedList: 对于频繁的插入、删除操作,使用此类效率比ArrarList高,底层使用双层链表存储
                 ③Vector: 作为List接口的古老实现类.线程安全的,效率低.  底层使用Object[ ]elementData存储.
                      

ArrayList的源码分析:
    1.jdk7情况下
        ArrayList list = new ArrayList();//底层创建了长度是10的Object[ ]数组elementData
        lisd.add(123);//elementData[0]=new Integer(123);
        若某次添加导致底层elementData数组容量不够,则扩容. 默认情况下扩容为原来容量的1.5倍,同时需要将原有数组中的数据赋值到新的数组中
        tips:建议开发中使用带参的构造器ArrayList list = new ArrayList(int capacity);

 

    2.jdk8中ArrayList的变化:
        ArrayList list = new ArrayList();//底层Object[ ]elementData初始化为{},并没有创建长度为10的数组
         lisd.add(123);//第一次调用add()时,底层才创建了长度为10的数组,并将123添加到elementData[0]
        若某次添加导致底层elementData数组容量不够,则扩容. 默认情况下扩容为原来容量的1.5倍,同时需要将原有数组中的数据赋值到新的数组中  即:后序的和7一样

 

     3.小结
               7中ArrayList的对象的创建有点类似于单例的饿汉式 而8有点像懒汉式 延迟的数组的创建,节省内存

LinkedList的源码分析:
         LinkedList list = new LinkedList();//内部声明了Node类型的first和last属性,用于记录首末元素,默认值为null,同时定义内部类Node,作为LinkedList中保存数据的基本结构,Node除了保存数据还定义了两个变量:①prev变量记录前一个元素的位置  ②next变量记录下一个元素的位置
         list.add(123);//将123封装到Node中,创建了Node对象
         其中,Node定义为:体现了LinkedList的双向链表的说法

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

Vector的源码分析:
        在jdk7和8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组,在扩容方面,默认扩容为原来数组长度的2倍

List接口常用方法

import org.junit.Test;

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

public class ListTest {
    @Test
    public void test(){
        ArrayList list = new ArrayList();
        list.add(123455);
        list.add(1);
        list.add(new String("ZAHNGKE"));
        list.add(true);
        System.out.println(list);
        //void add(int index,Object ele):在index位置插入ele元素
        list.add(1,"zhangtjw");
        System.out.println(list);
        //boolean addAll(int index,Collection eles):从index位置开始将eles中的所有元素添加进来
        Collection coll = new ArrayList();
        coll.add(1);
        ArrayList list1 = new ArrayList();
        list1.add(12323232);
        list.addAll(0,coll);
        list.addAll(0,list1);
        System.out.println(list);
        //Object get(int index):获取指定index位置的元素
        System.out.println(list.get(0));
        //int indexOf(Object obj):返回obj在集合中首次出现的位置 没有的话返回-1
        System.out.println(list.indexOf(1));
        //int LastIndexOf(Object obj):返回obj在集合中末次出现的位置 没有的话返回-1
        System.out.println(list.lastIndexOf(1));
        //Object remove(int index):移除指定index位置的元素,并返回此元素
        System.out.println(list.remove(0));
        System.out.println(list);
        //Object set(int index,Object obj):设置指定index位置的元素为ele
        list.set(0,'k');
        System.out.println(list);
        //List sublist(int fromIndex,int toIndex):返回从fromIndex到toIndex位置的子集合 左闭右开
        System.out.println(list.subList(0, 4));

    }
}


         
    

总结常用方法:
增: add(Object obj)
删: remove(int index) / remove(Object obj) -->Collection

改: set(int index,Object obj)
查: get(int index)
插: add(int index,Object ele)

长度: size()
遍历: ①Iterator迭代器 ②增强for循环 ③普通for循环

import org.junit.Test;

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


public class ListTest {
    @Test
    public void test(){
        ArrayList list = new ArrayList();
        list.add(123455);
        list.add(1);
        list.add(new String("ZAHNGKE"));
        list.add(true);
        //①Iterator迭代器
        Iterator iterator = list.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
        System.out.println("**********************");
        //②增强for循环
        for (Object obj:list) {
            System.out.println(obj);
        }
        System.out.println("**********************");
        //③普通for循环
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }
}

 

面试题:

ans:1 2 

        如果改成list.remove(new Integer(2)) 那么ans:1 3

Colletion子接口之二:Set接口 
        Set接口概述:
           ·Set接口是Collection的子接口,set接口没有提供额外的方法,使用的都是Collection的方法
           ·Set集合不允许包含相同的元素,如果试把两个相同的元素加入同一个Set集合中,则添加失败
           ·Set判断两个对象是否相同使用的是equals()方法 不是==运算符
       
    一.以HashSet为例说明:
   ①无序性:不等于随机性 存储的数据在底层数组中并非按照数组的索引顺序添加.二十根据数据的哈希值决定的
   ②不可重复性: 保证添加的元素按照equals()判断时,不能返回true.即:相同的元素只能添加一个.

   二.添加元素的过程:以HashSet为例说明
           我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置),判断数组此位置上是否已经有元素:
        若此位置上没有元素,则添加成功-->情况1
        若此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:
                若hash值不同,则元素a添加成功.-->情况2
                若hash值相同,进而需要调用元素a所在类的equals()方法
                        equals()返回true,则元素a添加失败
                        equals()返回false,则元素a添加成功-->情况3
        对于添加成功的情况2和情况3而言:元素a与已经存在指定索引位置上数据以链表的方式存储  

jdk 7 :元素a放到数组中 指向原来的元素
jdk 8 : 原来的元素在数组中 指向元素a

       要求:向set中添加数据,其所在的类一定要重写equals()和hashCode(Object obj)方法
       要求:重写equals()和hashCode(Object obj)方法要保持一致性  "相等的对象必须具有相等的散列码"

1.Set实现类之一:HashSet 作为Set接口的主要实现类
        ·HashSet是Set接口的典型实现,大多数时候使用Set集合时都使用这个实现类.
        ·HashSet按Hash算法来存储集合中的元素,因此具有很好的存取、查找、删除性能
        ·HashSet具有以下特点
                ①不能保证元素的排列顺序
                ②HashSet不是线程安全的
                ③集合元素可以是null
        ·HashSet集合判断两个元素相等的标准:两个对象通过hashCode()方法比较相等且两个对象的equals()方法返回值也相等
        ·对于存放在Set容器中的对象,对应的类一定要重写equals()和hashCode(Object obj)方法,以实现对象相等规则.即:"相等的对象必须具有相等的散列码"
        ·HashSet底层:数组+链表的结构

import org.junit.Test;

import java.util.HashSet;
import java.util.Iterator;

public class SetTest {
    @Test
    public void test(){
        HashSet hashSet = new HashSet();
        hashSet.add(1);
        hashSet.add(2);
        hashSet.add(new p("zk",1));
        hashSet.add(new p("zk",1));
        //在不重写p的hashcode()的情况下 默认调用Object里的hashcode()
//        Object里的hashcode() 可以理解为 给你一个随机数 所以两个对象的hash值一定不同 所以可以添加成功
//        重写之后 两个内容完全相同的p对象 hash值相同 且equals 所以只能添加一个
        hashSet.add('a');

        Iterator iterator = hashSet.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }

    }
}
class p{
    private String name;
    private int age;

    public p() {
    }

    public p(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 boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        p p = (p) o;

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

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        //neme是String  String 重写过hashcode 他保证两个"zhangke" hash值一样
        result = 31 * result + age;
        return result;
    }
}

重写hashCode()方法的基本原则
        ·在程序运行时,同一个对象多次调用hashCode()方法应该返回相同的值.
        ·当两个对象的equals()方法比较返回true时,这两个对象的hashCode()方法返回值也应相等
        ·对象中用做equals()方法比较的Field,都应该用来计算hashCode值
 

重写equals()方法的基本原则       
     以自定义的p类为例子,何时需要重写equals()?

        ·当一个类有自己特有的"逻辑相等"概念,当改写equals()的时候,总是要改写hashCode(),根据一个类的equals方法(改写后),两个截然不同的实例有可能在逻辑上是相等的,但是,根据Object.hashCode()方法,它们仅仅是两个对象
        ·因此,违反了"相等的对象必须具有相等的散列码"
        ·结论:复写equals方法的时候一般都需要同时复写hashCode方法.通常参与计算hashCode的对象的属性也应该参与到equals中进行计算
        


        
2.Set实现类之二:LinkedHashSet
        ·LinkedHashSet是HashSet的子类
        ·LinkedHashSet根据元素的hashCode值来决定元素的存储位置,但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的
        ·LinkedHashSet插入性能略低于HashSet,但在迭代访问Set里的全部元素时有很好的性能.
        ·LinkedHashSet不允许集合元素重复
        ·对于频繁的遍历操作,LinkedHashSet效率高于HashSet
        ·遍历其内部数据时,可以按照添加的顺序遍历


3.Set实现类之二:TreeSet
        ·TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态
        ·TreeSet底层使用红黑树结构存储数据
        ·可以按照添加对象的指定属性进行排序

        ·TreeSet两种排序方法:自然排序(实现Comparable接口)和定制排序(Comparator),默认情况下,TreeSet使用自然排序
                TreeSet的自然排序中,比较两个对象是否为相同的标准为:compareTo()返回0.不再是equals().
                TreeSet的定制排序中,比较两个对象是否为相同的标准为:compare()返回0.不再是equals().
        ·向TreeSet中添加的数据,要求是同一个类提供的

 

自然排序:
import org.junit.Test;

import java.util.Iterator;
import java.util.TreeSet;

public class SetTest {
    @Test
    public void test(){
        TreeSet Set = new TreeSet();
        //不能添加不同类的对象
//        Set.add(1);
//        Set.add(2);
//        Set.add(new p("zk",1));
//        Set.add(new p("zk",1));
//        Set.add('a');
//举例1:
//        Set.add(1);
//        Set.add(2);
//        Set.add(13);
//        Set.add(-234);

        //举例2:要重写 compareTo
        Set.add(new p("zk4",1));
        Set.add(new p("zk2",3));
        Set.add(new p("zk1",2));
        Set.add(new p("zk3",5));

     Iterator iterator = Set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next().toString());
        }
    }
}
class p implements Comparable{
    private String name;
    private int age;

    public p() {
    }

    public p(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 "p{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        p p = (p) o;

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

    @Override
    public int compareTo(Object o) {
        if(o instanceof p)
        {
            p p1 = (p)o;
            int x =  this.name.compareTo(p1.name);
            if(x!=0){
                return x;
            }else{
                return Integer.compare(this.age,p1.age);
            }
        }else {
            throw  new RuntimeException("No!");
        }
    }
}

 
定制排序:

public class SetTest {
    @Test
    public void test(){
        Comparator com = new Comparator() {
            //按照年龄从小到大排序
            @Override
            public int compare(Object o1, Object o2) {
                if(o1 instanceof  p && o2 instanceof  p){
                    p p1 = (p) o1;
                    p p2 = (p) o2;
                    return Integer.compare(p1.getAge(),p2.getAge());
                }else {
                    throw new RuntimeException("No");
                }
            }
        };

        TreeSet Set = new TreeSet(com);

        Set.add(new p("zk4",13));
        Set.add(new p("zk2",3));
        Set.add(new p("zk1",3));
        Set.add(new p("zk3",6));

     Iterator iterator = Set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next().toString());
        }
    }
}

 
练习:
        1.集合Collection中存储的如果是自定义类的对象,需要自定义类重写哪个方法
                        equals()方法 contains()\remove()/retainsAll()...要调用equals()
                List:也要重写equals()方法.
               Set:(HashSet、LinkedHashSet):equals()和hashCode()
                        (TreeSet):自然排序(compareTo(Object obj))
                                         定制排序(compate(Object o1,Object o2))

练习:

 mainTest .java

import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

public class mainTest {
    public static void main(String[] args) {
        MyDate myDate1 = new MyDate(1996, 9, 19);
        MyDate myDate2 = new MyDate(1997, 8, 12);
        MyDate myDate3 = new MyDate(1998, 7, 11);
        MyDate myDate4 = new MyDate(1999, 6, 13);
        MyDate myDate5 = new MyDate(2000, 5, 14);
        Employee employee1 = new Employee("dzk1",25,myDate1);
        Employee employee2 = new Employee("czk2",24,myDate2);
        Employee employee3 = new Employee("azk3",23,myDate3);
        Employee employee4 = new Employee("bzk4",22,myDate4);
        Employee employee5 = new Employee("fzk5",21,myDate5);
        Comparator com = new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
               if(o1 instanceof Employee && o2 instanceof Employee){
                   Employee p1 = (Employee) o1;
                   Employee p2 = (Employee) o2;
                   MyDate d1 = p1.getBirthday();
                   MyDate d2 = p2.getBirthday();
                   int y = d1.getYear()-d2.getYear();
                   if(y!=0){
                       return y;
                   }

                   int m = d1.getMonth()-d2.getMonth();
                   if(m!=0){
                       return m;
                   }

                 return d1.getDay()- d2.getDay();

               }
                throw new RuntimeException("No");
            }
        };
        //定制排序
        TreeSet treeSet1 = new TreeSet(com);
        treeSet1.add(employee1);
        treeSet1.add(employee2);
        treeSet1.add(employee3);
        treeSet1.add(employee4);
        treeSet1.add(employee5);
        Iterator iterator = treeSet1.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
        System.out.println("__________________________");
        //自然排序
        TreeSet treeSet2 = new TreeSet();
        treeSet2.add(employee1);
        treeSet2.add(employee2);
        treeSet2.add(employee3);
        treeSet2.add(employee4);
        treeSet2.add(employee5);
        Iterator iterator2 = treeSet2.iterator();
        while(iterator2.hasNext()){
            System.out.println(iterator2.next());
        }
    }
}

Mydate.java
 

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

    public MyDate(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    public MyDate() {
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public int getMonth() {
        return month;
    }

    public void setMonth(int month) {
        this.month = month;
    }

    public int getDay() {
        return day;
    }

    public void setDay(int day) {
        this.day = day;
    }

    @Override
    public String toString() {
        return "MyDate{" +
                "year=" + year +
                ", month=" + month +
                ", day=" + day +
                '}';
    }
}

Employee.java
 

import java.util.Date;

public class Employee implements Comparable{
    private String name;
    private int age;
    private MyDate birthday;

    public Employee() {
    }

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

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

    public MyDate getBirthday() {
        return birthday;
    }

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

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", birthday=" + birthday +
                '}';
    }

    @Override
    public int compareTo(Object o) {
        if(o instanceof Employee){
            Employee e = (Employee) o;
            return this.name.compareTo(e.name);
        }else{
            throw new RuntimeException("No");
        }
    }
}

练习:

 //set不允许有相同的元素 所以在addAll时候自动去除了相同的元素


练习:

 //set.remove()是根据hashcode来删除的 刚开始存数据的时候p1以(1001,"AA")来计算hashcode,可remove的时候以p1(1001,"CC")来计算hashcode的 两者的哈希值很大概率不同 所以其实没有删除成功 相同的此时set里的p1(1001,"CC")是以(1001,"AA")来计算hashcode的 可是新添加的p以(1001,"CC")计算哈希值 所以可以添加成功   最后 添加p(1001,"AA")时,虽然和上面第一次添加的p1hashcode相同 但在随后的equals计算时 p(1001,"CC")和p(1001,"AA")不同 所以可以添加成功

 Map接口


一、Map实现类的结构

 Map:双列数据,存储key-value对的数据---类似于函数y=f(x);
        1.HashMap:作为Map的主要实现类 线程不安全 效率高 可以存储null的key和value
                ①LinkedHashMap:保证遍历map元素时,可以按照添加的顺序实现遍历
                        原因:在原有的HashMap底层结构上,添加了一堆指针,指向前一个和后一个元素,对于频繁的遍历操作 此类的执行效率高于HashMap
        2.TreeMap:保证按照添加的key-value对进行排序 实现排序遍历 此时考虑key的自然排序和定制排序
                        底层使用红黑树
        3.Hashtable:作为古老的实现类 线程安全 效率高 不可以存储null的key和value
                ①properties:常用来处理配置文件 key和value是String类型

HashMap的底层:数组+链表(jdk7及之前)

                            数组+链表+红黑树(jdk8)

面试题:
        ①HashMap的底层实现原理?
        ②HashMap和HashTable的异同?
        ③CurrentHashMap与Hashtable的异同?(暂时不讲)

二、Map结构的理解

        Map中的Key: 使用Set存储所有的Key 无序 不可重复 -->key所在的类要重写equals()和hashCode()       
        Map中的Value: 使用Collection存储所有的Value 无序 可重复-->Value所在的类要重写equals()
        一个键值对:key-value构成了一个Entry对象
        Map中的Entry:无序的、不可重复的 使用Set存储所有的Entry
                put()也是一个一个的放数据(Entry)  其中一个Entry中有两个属性 一个key 一个 value

三、HashMap的底层实现原理?以jdk7为例


HashMap map = new HashMap();
       在实例化以后,底层创建了长度是16的一维数组Entry[ ] table;
        ....可能已经执行过多次put...
        map.put(key1,value1):
            首先,调用key1所在类的hashCode()计算key1的哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置.若此位置上的数据为空,此时的key1-value1(Entry)添加成功,  --情况1
若此位置上的数据不为空(意味着此位置上存在一个或者多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据的哈希值:
        ①若key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功  --情况2
        ②若key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals()方法,比较: 
               若equals()返回false:此时key1-value1添加成功  --情况3
               若equals()返true:使用value1替换相同value2
tips:情况2和3 此时key1-value1 和原来的数据以链表的方式存储
        在不断的添加过程中 会涉及到扩容问题 当超出临界值(且要存放的位置非空) 默认的扩容方式:扩容为原来容量的2倍 并将原有的数据复制过来

jdk8相较于jdk7在底层实现方面的不同
        1.new HashMap():底层没有创建一个长度为16的数组
        2.jdk8底层的数组是:Node[],而非Entry[]
        3.首次调用put()方法时 底层创建长度为16的数组
        4.jdk7的底层结构只有 数组+链表 jdk8中的底层结构 数组+链表+红黑树
                当数组的某一个索引位置上的元素以链表形式存在的数据个数>8且当前数组的长度>64时,此时索引位置上的所有数据改为使用红黑树存储


   
LinkedHashMap的底层实现原理(了解一下就行)  

Map中的常用方法

 
 

import org.junit.Test;

import java.util.*;

public class MapMethodTest {
    @Test
    public void test(){
        HashMap hashMap = new HashMap();
        HashMap hashMap1 = new HashMap();
        //添加
        hashMap.put(1,1);
        hashMap.put('Z','K');
        hashMap.put("tian","jiawen");
        System.out.println(hashMap.entrySet());
        //修改
        hashMap.put(1,3);
        System.out.println(hashMap.entrySet());
        hashMap1.put(2,2);
        hashMap1.put('T','J');
        hashMap1.put("zhang","ke");

        hashMap.putAll(hashMap1);
        System.out.println(hashMap);
        hashMap.remove(1);
        System.out.println(hashMap);
        hashMap.clear();
        System.out.println(hashMap);
        System.out.println(hashMap1.get(2));
        System.out.println(hashMap1.containsKey(2));
        System.out.println(hashMap1.containsValue('J'));
        System.out.println(hashMap1.size());
        System.out.println(hashMap1.isEmpty());
        System.out.println(hashMap1.equals(hashMap));
        //遍历所有的key集:keyset()
        Set set = hashMap1.keySet();
        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
        
        //遍历所有的value集:values()
        Collection values = hashMap1.values();
        for (Object obj:values
             ) {
            System.out.println(obj);
        }
        
        //遍历所有的key-value:entrySet()
        //方式1:
//        Set entrySet = hashMap1.entrySet();
//        Iterator iterator1 = entrySet.iterator();
//        while (iterator1.hasNext()){
//
//            Object obj = iterator1.next();
//            //entrtySet集合中的元素都是entry
//            Map.Entry entry = (Map.Entry) obj;
//            System.out.println(entry.getKey()+"--->"+entry.getValue());
//        }
        //方式2:
        Set keySet = hashMap1.keySet();
        Iterator iterator1 = keySet.iterator();
        while (iterator1.hasNext()){
            Object key = iterator1.next();
            Object value = hashMap1.get(key);
            System.out.println(key+"--->"+value);
        }
        //这样的遍历是有意义的
        System.out.println(hashMap1.values());
        System.out.println(hashMap1.keySet());
        System.out.println(hashMap1.entrySet());
        //这三个方式  只能看到结果 取不出 key和value但是在开发中意义不大
    }
}

     

 

TreeMapTest 
        向TreeMap中添加key-value,要求key必须是由同一个类创建的对象,因为要按照key进行自然排序、定制排序

 

import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

public class mainTest {
    public static void main(String[] args) {
        MyDate myDate1 = new MyDate(1996, 9, 19);
        MyDate myDate2 = new MyDate(1997, 8, 12);
        MyDate myDate3 = new MyDate(1998, 7, 11);
        MyDate myDate4 = new MyDate(1999, 6, 13);
        MyDate myDate5 = new MyDate(2000, 5, 14);
        Employee employee1 = new Employee("dzk1",25,myDate1);
        Employee employee2 = new Employee("czk2",24,myDate2);
        Employee employee3 = new Employee("azk3",23,myDate3);
        Employee employee4 = new Employee("bzk4",22,myDate4);
        Employee employee5 = new Employee("fzk5",21,myDate5);
        Comparator com = new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
               if(o1 instanceof Employee && o2 instanceof Employee){
                   Employee p1 = (Employee) o1;
                   Employee p2 = (Employee) o2;
                   MyDate d1 = p1.getBirthday();
                   MyDate d2 = p2.getBirthday();
                   int y = d1.getYear()-d2.getYear();
                   if(y!=0){
                       return y;
                   }

                   int m = d1.getMonth()-d2.getMonth();
                   if(m!=0){
                       return m;
                   }

                 return d1.getDay()- d2.getDay();

               }
                throw new RuntimeException("No");
            }
        };
        //定制排序
        TreeSet treeSet1 = new TreeSet(com);
        treeSet1.add(employee1);
        treeSet1.add(employee2);
        treeSet1.add(employee3);
        treeSet1.add(employee4);
        treeSet1.add(employee5);
        Iterator iterator = treeSet1.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
        System.out.println("__________________________");
        //自然排序
        TreeSet treeSet2 = new TreeSet();
        treeSet2.add(employee1);
        treeSet2.add(employee2);
        treeSet2.add(employee3);
        treeSet2.add(employee4);
        treeSet2.add(employee5);
        Iterator iterator2 = treeSet2.iterator();
        while(iterator2.hasNext()){
            System.out.println(iterator2.next());
        }
    }
}

 

 

Properties处理数据:
        ·Properties类是Hashtable的子类 该对象用于处理属性文件
        ·由于属性文件里的key、value都是字符串累心 所以Properties里的key和value都是字符串类型
        ·存取数据时,建议采用setProperty(String key,String value)方法和getProperty(String key)方法
        

代码:

import org.junit.Test;

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

public class PropertiesTest {
    @Test
    public void test() {
        FileInputStream fis = null;
        try {
            Properties pros = new Properties();
            fis = new FileInputStream("jdbc.properties");
            pros.load(fis);
            String name = pros.getProperty("name");
            String passWord = pros.getProperty("pasWd");
            System.out.println(name + "," + passWord);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fis!=null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }

    }
}

 jdbc.properties

name=ZHANGKE张柯
pasWd=1234

 

Collections工具类 ---->操作数组的工具类:Arrays
        ·Collections是一个操作Set、List、Map等集合的工具类
        ·Collections中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象 实现同步控制等方法
        ·排序方法:(均为static方法)
                ①reverse(List):反转List中元素的顺序
                ②shuffle(List):对List集合元素进行随机排序
                ③sort(List):根据元素的自然顺序对指定List集合元素按升序排序
                ④sort(List,Comparator):根据指定的Comparator产生的顺序对List集合元素进行排序
                ⑤swap(List,int,int):将指定list集合中的i处元素和j处元素进行交换
                ⑥Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
                ⑦Object max(Collection,Comparator):
根据 Comparator指定的顺序,返回给定集合中的最大元素

                ⑧Object min(Collection)
                ⑨Object min(Collection, Comparator)
                ⑩int frequency(Collection,Object):返回指定集合中指定元素的出现次数
                11.void copy(List dest.List src)
src中的内容复制到dest中
                12.boolean replaceAII(List list,Object oldVal, Object newVal):使用新值替换
List
对象的所有旧值

                

面试题:Collection和Collections的区别?
        ·Collections是一个操作Set、List、Map等集合的工具类
        ·Collections中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象 实现同步控制等方法
       
        ·Collection接口:单例数据、定义了存取一组对象的方法的集合,用来存储一个一个的对象
                ①List:元素有序、可重复的集合--->"动态数组"
                        ArrayList、LinkedList、Vector
                ②Set:元素无序、不可重复的集合--->高中的"集合"概念
                        HashSet、LinkedHashSet、TreeSet
        

import org.junit.Test;

import java.util.*;

public class CollectionsTest {
    @Test
    public void test(){
        List list = new ArrayList();
        list.add(1);
        list.add(-1);
        list.add(13);
        list.add(12);
        list.add(21);
        System.out.println(list);
        Collections.reverse(list);
        System.out.println(list);
        Collections.shuffle(list);
        System.out.println(list);
        Collections.sort(list);
        System.out.println(list);//调用Integer里的排序方法
        Comparator com = new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                if(o1 instanceof Integer && o2 instanceof Integer){
                    Integer i1 = (Integer) o1;
                    Integer i2 = (Integer) o2;
                    return i2-i1;
                }else {
                    throw new RuntimeException("No!");
                }

            }
        };
        Collections.sort(list,com);
        System.out.println(list);
        Collections.swap(list,0,2);
        System.out.println(list);
        int frequency = Collections.frequency(list, 1);
        System.out.println(frequency);
        //错误写法 因为list2的size()不确定
//        List list2 = new ArrayList();
//        Collections.copy(list2,list);
        List list2 = Arrays.asList(new Object[list.size()]);
        Collections.copy(list2,list);
        System.out.println(list2);
    }
}

 

 Collections常用方法:同步控制

List list1 = Collections.synchronizedList(list);//返回的list1 就是线程安全的

练习:
1.请从键盘随机输入10个整数保存到List中,并按倒序、从大到小的顺序显示出来

 

import org.junit.Test;

import java.util.*;

public class listTest {
    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        List list = new ArrayList();
        for (int i = 0; i < 10; i++) {
            list.add(sc.nextInt());
        }
        Comparator com = new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                if (o1 instanceof Integer && o1 instanceof Integer) {
                    Integer i1 = (Integer) o1;
                    Integer i2 = (Integer) o2;
                    return Integer.compare(i2, i1);
                } else {
                    throw new RuntimeException("NO");
                }
            }
        };
        System.out.println(list);
        Collections.reverse(list);//反转
        System.out.println(list);
        Collections.sort(list,com);//从大到小
        System.out.println(list);

    }
}

2.请把学生名与考试分数录入到集合中,并按照分数显示前三名成绩学员的名字
 

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

public class main1Test {
    public static void main(String[] args) {
        Student s1 = new Student("ZK4", 96);
        Student s2 = new Student("ZK3", 100);
        Student s3 = new Student("ZK2", 99);
        Student s4 = new Student("ZK5", 97);
        Student s5 = new Student("ZK1", 99);
        List list = new ArrayList();
        list.add(s1);
        list.add(s2);
        list.add(s3);
        list.add(s4);
        list.add(s5);
        Collections.sort(list);//要先排序 再定义iterator
        Iterator iterator = list.iterator();
        int count = 0;
        while(count<3 && iterator.hasNext()){
            System.out.println(iterator.next());
            count++;
        }
    }
}




public class Student implements Comparable {
    private  String name;
    private int score;

    public String getName() {
        return name;
    }

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

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }

    public Student() {

    }

    @Override
    public int compareTo(Object o) {
        if(o instanceof Student){
            Student s = (Student) o;
            return -this.score+s.score;
        }else{
            throw new RuntimeException("NO!");
        }
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", score=" + score +
                '}';
    }
}

3.姓氏统计:一个文本文件中存储着北京所有高校在校生的姓名,格式如下:
每行一个名字,姓与名以空格分隔:
张 三
李 四
....
现在想统计所有姓氏在文件中出现的次数,请描述一下你的解决方案

 4.对一个Java源文件中的关键字进行计数
        提示:Java源文件中的每一个单词,需要确定该单词是否是一个关键字.为了高效处理这个问题,将所有的关键字保存在一个HashSet中.用contains()来测试
        File file = new File("Test.java");
        Scanner sc = new Scanner(file);
        while(sc.hasNext()){
                String word = sc.next();
                System.out.print(word);
        }
        

Enumeration

面试题: 负载因子值的大小 对HashMap有什么影响

高频面试题:
        ArrayList和LinkedList的比较

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值