哈希表和有序表的简单介绍

161 阅读2分钟

哈希表具有的性质

  • 哈希表在使用层面上可以理解为一种集合结构
  • 如果只有key, 没有伴随数据value, 可以使用HashSet结构
  • 如果既有key, 又有伴随数据value, 可以使用HashMap结构
  • 有无伴随数据, 是HashMap和HashSet唯一的区别,底层的实际结构是一回事
  • 内部数据不重复
  • 内部数据无序
  • 使用哈希表进行增(put), 删(remove), 改(put), 查(get)的操作,可以认为时间复杂度为O(1),只不过这里的常数时间比较大
  • 放入哈希表的东西,如果是基础类型,内部按值传递,内存占用就是这个东西的大小
  • 放入哈希表的东西,如果不是基础类型,内部按引用传递,内存占用是这个东西内存地址的大小
  • HashSet重复add会返回false,HashMap重复put会返回1
  • TreeSet==>O(log(n))==> 基于树的搜索,只需要搜索一半即可,保证元素不重复以及元素有序,放的数字的话,默认是升序排序,TreeSet的排序
  • TreeSet用法

相关的代码如下:

    public static void main(String[] args) {
        //这里hashSet的key是基础类型,所以是按值传递,也就是传入的是什么是多大,哈希表都会copy一份
        HashSet<Integer> hashSet1 = new HashSet<>();
        hashSet1.add(3);
        hashSet1.remove(3);
        System.out.println(hashSet1.contains(3));
        System.out.println("======1======");

        //这里hashSet的key是非基础类型,所以是引用传递,也就是传入的是内存地址,如下面的两次new的内存地址并不相同
        Node nodeA = null;
        Node nodeB = null;
        nodeA = new Node(1);
        nodeB = new Node(1);
        HashSet<Node> hashSet2 = new HashSet<>();
        hashSet2.add(nodeA);
        hashSet2.add(nodeB);//true
        System.out.println(hashSet2.add(nodeB));//false不可以重复加入
        hashSet2.remove(nodeA);
        System.out.println(hashSet2.contains(nodeA));//false
        System.out.println("=======2=====");

        //hashMap1的key是基础类型 -> String类型
        HashMap<String, Integer> hashMap1 = new HashMap<>();
        String str1 = "key";
        String str2 = "key";
        hashMap1.put(str2, 1);
        System.out.println(hashMap1.put(str2, 1));//返回的是1
        System.out.println(hashMap1.containsKey(str1));//true,这是因为key是基础类型是按照值来传递的
        System.out.println(hashMap1.get(str1));//1
        System.out.println(hashMap1.get(str2));//1
        hashMap1.put(str2, 2);//改
        System.out.println(hashMap1.get(str2));//2
        hashMap1.remove(str2);
        System.out.println(hashMap1.containsKey(str2));//false
        System.out.println("======3======");

        //hashMap2的key是非基础类型->Node类型
        HashMap<Node, String> hashMap2 = new HashMap<>();
        hashMap2.put(nodeA, "A节点");
        System.out.println(hashMap2.containsKey(nodeA));//true
        System.out.println(hashMap2.containsKey(nodeB));//false
        System.out.println(hashMap2.get(nodeA));//A节点
        hashMap2.put(nodeA, "B节点");
        System.out.println(hashMap2.get(nodeA));//B节点
        hashMap2.remove(nodeA);
        System.out.println(hashMap2.containsKey(nodeA));//false
        System.out.println("======4======");

    }
    
    public static class Node{
        public int value;
        public Node next;
        public Node(int val){
            value = val;
        }
    }

有序表具有的性质

  • 有序表在使用层面上可以理解为一种集合结构
  • 如果只有key, 没有伴随数据value, 可以使用TreeSet结构
  • 如果既有key, 又有伴随数据value, 可以使用TreeMap结构
  • 有无伴随数据, 是TreeSet和TreeMap唯一的区别,底层的实际结构是一回事
  • 有序表和哈希表的区别是,有序表把key按照顺序组织起来,而哈希表完全不组织
  • 红黑树、AVL树、size-balance-tree和跳表等都属于有序表结构,只是底层具体实现不同
  • 放入有序表的东西,如果是基础类型,内部按值传递,内存占用就是这个东西的大小
  • 放入有序表的东西,如果不是基础类型,必须提供比较器,内部按引用传递,内部占用是这个东西内存地址的大小
  • 使用有序表进行增(put), 删(remove), 改(put), 查(get)的操作,可以认为时间复杂度为O(logN)
  • 不管是什么底层具体实现,只要是有序表, 都有以下固定的基本功能和固定的时间复杂度,下面有代码展示

相关的代码是:

    public static void main(String[] args) {
        //展示有序表的常用操作
        //TreeMap1的key是基础类型 -> Integer类型
        TreeMap<Integer, String> treeMap1 = new TreeMap<>();
        treeMap1.put(7, "我是7");
        treeMap1.put(5, "我是5");
        treeMap1.put(4, "我是4");
        treeMap1.put(3,"我是3");
        treeMap1.put(9,"我是9");
        treeMap1.put(2,"我是2");
        treeMap1.put(2,"我是2");
        System.out.println(treeMap1.containsKey(5));//true
        System.out.println(treeMap1.get(5));//我是5
        System.out.println(treeMap1.firstKey());//最小的key  2
        System.out.println(treeMap1.lastKey());//最大的key  9
        System.out.println(treeMap1.floorKey(8));//<=8且离8最近的key 7
        System.out.println(treeMap1.ceilingKey(8));//>=8且离8最近的key 9
        treeMap1.remove(5);
        System.out.println(treeMap1.get(5));//null
        treeMap1.remove(2);
        System.out.println(treeMap1.get(2));//false,说明有序表也没有重复的值
        treeMap1.put(3, "改动了");
        System.out.println(treeMap1.get(3));//改动了
        System.out.println("===========");

        //treeMap2的key是非基础类型->Node类型
        Node nodeA = new Node(5);
        Node nodeB = new Node(3);
        Node nodeC = new Node(7);
        //注:需要提供Node类型的比较器,否则会报错
        TreeSet<Node> treeSet = new TreeSet<>(new NodeComparator());
        treeSet.add(nodeA);
        treeSet.add(nodeB);
        treeSet.add(nodeC);
    }

    public static class Node{
        public int value;
        public Node next;
        public Node(int val){
            value = val;
        }
    }
    public static class NodeComparator implements Comparator<Node>{
        @Override
        public int compare(Node o1, Node o2) {
            return o1.value - o2.value;
        }
    }