哈希表
哈希表相较于数组来说,具有优良的低时间复杂度特性,其增删改查操作的时间复杂度都是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()方法同样也是之前讲的规则,附一段源码:
Comments NOTHING