第十七章 深入理解容器
Object.hasCode()默认使用对象的地址计算散列码。
Object.equals()默认比较对象地址。
一、散列与散列码
Hash结构容器散列原理(大致的原理,没有必要较真)
数组保存ArrayList的引用,散列码就是数组的下标值。存储元素时计算散列码,找到元素所属List,用该对象equals()方法与List中的元素比较,若发现相等元素则覆盖,没有相等元素则在空位插入。如果散列均匀,通过散列码可以急速定位List,大大减少equals()比较操作的执行次数,提高查询速度。
二、容器
1、List
ArrayList:数组实现,随机访问更快。
LinkedList:链表实现,增删数据更快。
2、Set
Set:加入Set的元素必须实现equals()接口,以确保元素的唯一性。
HashSet:(默认选择)哈希表实现,专门对快速查找进行了优化,加入HashSet的元素必须实现hashCode()方法。
LinkedHashSet:具有HashSet的查询速度,链表维持插入顺序,元素必须实现hashCode()。
TreeSet:红黑树实现,保持元素次序,必须实现Comparable接口。
红黑树引用广泛,主要是用来存储有序的数据,它的时间复杂度是O(lgn),效率非常之高。
mport java.util.*; class SetType { int i; public SetType(int n) { i =n; } public boolean equals(Object o) { return o instanceof SetType && (i == ((SetType)o).i); } public String toString() { return String.valueOf(i); } } class HashType extends SetType{ public HashType(int n) { super(n); } public int hashCode() { return i%5; } } class TreeType extends SetType implements Comparable<TreeType>{ public TreeType(int n) { super(n); } public int compareTo(TreeType t) { return (i < t.i ? -1 : ((i == t.i) ? 0 : 1)); } } public class Demo1 { static <T> Set<T> fill(Set<T> set, Class<T> type) { try { for(int i = 0; i < 10; i++) { set.add(type.getConstructor(int.class).newInstance(i)); } }catch(Exception e) { throw new RuntimeException(); } return set; } static <T> void test(Set<T> set, Class<T> type) { fill(set,type); System.out.println(set); } public static void main(String[] args) { test(new HashSet<HashType>(), HashType.class); test(new TreeSet<TreeType>(), TreeType.class); } }
输出结果:
[0, 5, 1, 6, 2, 7, 3, 8, 4, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
结果分析:通过这个简单的程序了解一下原理,真正写hashCode()和equals()时的细节还是蛮多,蛮不好写的。一般来说,实现他们都是基于对象内容的。
3、Map
HashMap:(默认)散列表实现,查询键快。
LinkedHashMap:链表实现,迭代更快,插入(相对HashMap)稍慢,按照插入顺序或最近最少使用顺序存储元素。
TreeMap:红黑树实现,保持次序。
三、Collections类的静态方法
1、填充容器:
import java.util.*; class StringAddress { private String s; public StringAddress(String s) { this.s = s; } public String toString() { return super.toString() + " " + s; } } public class Demo2 { public static void main(String[] args) { List<StringAddress> l = new ArrayList<>(Collections.nCopies(2, new StringAddress("Hello"))); l.add(new StringAddress("Hello")); System.out.println(l); Collections.fill(l, new StringAddress("World")); System.out.println(l); } }
输出结果:
[chapter17.StringAddress@6cd8737 Hello, chapter17.StringAddress@6cd8737 Hello, chapter17.StringAddress@22f71333 Hello]
[chapter17.StringAddress@13969fbe World, chapter17.StringAddress@13969fbe World, chapter17.StringAddress@13969fbe World]
结果分析:
nCopies():填充对象的引用,返回一个不可变数组。
fill():替换已存在的元素,且不能够添加新元素。
2、动态类型安全
import java.util.*; class Fruit {} class Apple extends Fruit {} class Banana extends Fruit {} public class CheckedList { @SuppressWarnings("unchecked") static void oldStyleMethod(List l) { l.add(new Banana()); } public static void main(String[] args) { List<Apple> l1 = new ArrayList<Apple>(); oldStyleMethod(l1); System.out.println(l1); List<Apple> l2 = Collections.checkedList(new ArrayList<Apple>(), Apple.class); try { oldStyleMethod(l2); }catch(Exception e) { e.printStackTrace(); } } }
输出结果:
[chapter17.Banana@1554909b]
java.lang.ClassCastException
结果分析:可以看到使用checkedList(),checkedMap(),checkedSet()进行类型的检查,可以防止老代码的偷梁换柱行为。
3、排序与查询
import java.util.*; public class Demo3 { public static void main(String[] args) { String[] s = {"one", "two", "three", "four", "five", "Four", "six", "Two"}; List<String> list = new ArrayList<String>(Arrays.asList(s)); Collections.sort(list); System.out.println("sorted: " + list); Collections.shuffle(list); System.out.println("shuffle: " + list); Collections.sort(list, String.CASE_INSENSITIVE_ORDER); System.out.println("sorted-insensitive: " + list); int index = Collections.binarySearch(list, "Two", String.CASE_INSENSITIVE_ORDER); System.out.println("Location of " + "\"Two\" is " + index); } }
输出结果:
sorted: [Four, Two, five, four, one, six, three, two]
shuffle: [Two, Four, four, six, one, two, three, five]
sorted-insensitive: [five, Four, four, one, six, three, Two, two]
Location of "Two" is 6
结果说明:还是强调注意sort()与binarysearch()使用同样的Comparator。
4、设定不可修改的Collection或Map
import java.util.*; public class Demo4 { public static void main(String[] args) { List<Integer> l1 = Arrays.asList(1,2,3,4); List<Integer> l2 = Collections.unmodifiableList(new ArrayList<Integer>(l1)); System.out.println(l2); try { l2.set(0, 3); }catch(Exception e) { e.printStackTrace(); } } }
输出结果:
[1, 2, 3, 4]
java.lang.UnsupportedOperationException
结果说明:unmodifiableList()等方法可以创建不可修改容器。
5、其他API
import java.util.*; public class Demo5 { public static void main(String[] args) { List<Integer> l1 = new ArrayList<Integer>(); List<Integer> l2 = new ArrayList<Integer>(); Collections.addAll(l1, 2,3,1,4,6,8,7,5); Collections.addAll(l2, 8,9,10); System.out.println("max: " + Collections.max(l1)); System.out.println("min: " + Collections.min(l1)); Collections.sort(l1); Collections.rotate(l1, 3); System.out.println("rotate: " + l1); System.out.println("disjoint: " + Collections.disjoint(l1, l2)); Collections.swap(l1, 0, 7); System.out.println("swap: " + l1); } }
输出结果:
max: 8
min: 1
rotate: [6, 7, 8, 1, 2, 3, 4, 5]
disjoint: false
swap: [5, 7, 8, 1, 2, 3, 4, 6]
结果分析:这里列出了一部分方法
max():找最大
min():找最小
rotate():所有元素向后移动distance,后面元素循环到前面来
disjoint():两个集合没有相同元素返回true
swap():交换两个位置的元素