哈希表

哈希表相较于数组来说,具有优良的低时间复杂度特性,其增删改查操作的时间复杂度都是O(1)。

注意点1:比较包装数据类型时,应使用equals方法

 String str1 = new String("Hello");
 String str2 = new String("Hello");
 System.out.println(str1 == str2);// false,因为不同的内存地址
 System.out.println(str1.equals(str2));// true,因为它们的值是相同的
 HashSet<String> set = new HashSet<>();
 set.add(str1);
 System.out.println(set.contains("Hello"));//true
 System.out.println(set.contains(str2));//true 因为比较的是值
 set.add(str2);
 System.out.println(set.size());// 1 值相同,作为同一个key
 set.remove(str1);
 set.clear();
 System.out.println(set.isEmpty());

对于HashMap而言,同理:

 HashMap<String, String> map1 = new HashMap<>();
 map1.put(str1, "World");//key:"Hello"-value:"World"
 System.out.println(map1.containsKey("Hello"));//true
 System.out.println(map1.containsKey(str2));//true 比较的都是字面值
 System.out.println(map1.get(str2));//World
 System.out.println(map1.get("你好") == null);//true
 map1.remove("Hello");
 System.out.println(map1.size());//0
 map1.clear();
 System.out.println(map1.isEmpty());//true

注意点2:在key值较小且范围可控的情况下,可以使用数组替代哈希结构

不是必须要申请哈希结构,在key值在某一固定范围内且可控的情况下,可以使用数组这样的稳定结构来代替哈希结构。

 HashMap<Integer, Integer> map2 = new HashMap<>();
 map2.put(56, 7285);
 map2.put(34, 3671263);
 map2.put(17, 716311);
 map2.put(24, 1263161);
 // 上面的map2行为,可以被如下数组的行为替代
 int[] arr = new int[100];
 arr[56] = 7285;
 arr[34] = 3671263;
 arr[17] = 716311;
 arr[24] = 1263161;

注意点3:自定义类型对象在哈希结构中以地址形式存在

 Student s1 = new Student(17, "张三");
 Student s2 = new Student(17, "张三");
 HashMap<Student, String> map3 = new HashMap<>();
 map3.put(s1, "这是张三");//key:"'17,张三'"-value:"张三"
 System.out.println(map3.containsKey(s1));//true
 System.out.println(map3.containsKey(s2));//false
 map3.put(s2, "这是另一个张三");
 System.out.println(map3.size());//2
 System.out.println(map3.get(s1));//"这是张三"
 System.out.println(map3.get(s2));//"这是另一个张三"

自定义类型对象对于哈希结构而言,即时内部字面值一样,也是完全不同的两个对象。

注意点4:任何类都可以自定义实现hashCode()和equals()方法

有序表

有序表可以认为是集合,但是其内部数据是根据键的值来顺序排列的。

TreeSet和TreeMap原理一样,有无伴随数据的区别。

与哈希表不同之处在于有序表存在对键值对的顺序的操作,比如:firstKey()、floorKey()(返回小于等于参数且离参数最近的key)、ceilingKey()(返回大于等于参数且离参数最近的key)

增、删、改、查 + 很多和有序相关的操作(floor、ceilling等),时间为O(log n)

比较器

实现比较器接口

 public static class EmployeeComparator implements Comparator<Employee> {}

重写比较方法:

 @Override
  public int compare(Employee o1, Employee o2) {
  return o1.age - o2.age;
  }

如果返回负数认为o1的优先级更高,如果返回正数认为o2的优先级更高 任何比较器都是这样,所以利用这个设定,可以定制优先级怎么确定,也就是怎么比较不再有大小的概念,就是优先级的概念

使用lambda表达式

 Arrays.sort(arr, (a, b) -> b.age - a.age);

在创建有序表对象时,如果是自定义类型,需要在构造参数处指定比较方法,否则报错。

如果不希望去重,需要增加更多的比较,比如下面的逻辑中先比较公司,再比较年龄,若前两者相同,则比较内存地址(新的对象肯定不同):

 ((a, b) -> a.company != b.company ? (a.company - b.company)
  : a.age != b.age ? (a.age - b.age)
  : a.toString().compareTo(b.toString()));

可以以对象内存地址或数组下标这样独特的信息来做比较。


字典序

字典序是两个字符串比较大小的方式。

不管是大小写字母还是类似@¥%这样的符号,都可以使用256进制的数字来表示。

非等长字符串比较时,会在短的字符串末尾补256进制中最小的数值。以达到"ab" > "efgh"的效果

字符串的compareTo()方法同样也是之前讲的规则,附一段源码:

此作者没有提供个人介绍
最后更新于 2024-08-14