ArrayList Api调用:

  1. public class ArrayListDemo {
  2. public static void main(String[] args) {
  3. final ArrayList<String> list = new ArrayList<>();
  4. list.add("wgx");
  5. list.add("ssn");
  6. list.add("wh");
  7. list.add("wbt");//添加数据
  8. System.out.println(list);
  9. System.out.println(list.get(2));//读取指定下标的数据
  10. list.add(1,"zmy");//指定下下标加数据,剩余下边向后移
  11. System.out.println(list);
  12. System.out.println(list.isEmpty());//判断是否为空
  13. final ArrayList<String> list1 = new ArrayList<>();
  14. list1.add("wgx");
  15. list1.add("wbt");
  16. System.out.println(list.containsAll(list1));//是否包含后面的集合中的元素和顺序无关
  17. System.out.println(list.contains("ssn"));//是否包含后面的元素
  18. System.out.println(list.size());//获取集合中元素的个数
  19. list.addAll(list1);//将集合中的元素全部添加到自己中
  20. System.out.println(list);
  21. list.remove("wbt");//移除了元素,并且是靠前的元素(从下标0开始找,找到了就停止,)
  22. System.out.println(list);
  23. ArrayList<String> list2 = new ArrayList<String>();
  24. list2.add("wgx");
  25. list.removeAll(list2);//移除了后面的元素,并且重复了也移除了
  26. /* System.out.println(list);
  27. list.ensureCapacity(7);//*/
  28. //System.out.println(list);
  29. Object clone = list.clone();//克隆之后发现是Object类型
  30. System.out.println(clone);
  31. System.out.println(list.indexOf("wbt"));//获得该元素下标
  32. list.add("ssn");
  33. list.add("zmy");
  34. list.add("ssn");
  35. System.out.println(list);
  36. System.out.println(list.lastIndexOf("ssn"));//获得最后一个此元素的下标
  37. //list.retainAll(list1);除了list1中包含的元素,其余都删除
  38. //System.out.println(list);
  39. //list.removeIf("zmy","ssn","wh","wbt");
  40. //list.ensureCapacity(7);
  41. }
  42. }

LinkedList Api的调用

  1. public class LinkedListDemo {
  2. public static void main(String[] args) {
  3. final LinkedList<String> strings = new LinkedList<>();
  4. strings.add("wgx");
  5. strings.add("wh");
  6. strings.add("wbt");
  7. strings.add("zmy");
  8. strings.add("ssn");//添加元素,按照插入的顺序保存,添加到最后
  9. System.out.println(strings);
  10. strings.add(1, "wbt");//在指定位置插入元素,直接更改指针指向
  11. System.out.println(strings);
  12. strings.removeFirst();//移除第一个元素
  13. System.out.println(strings);
  14. strings.remove("wh");//移除对应元素
  15. System.out.println(strings);
  16. strings.remove(0);//移除指定位置的元素
  17. System.out.println(strings);
  18. System.out.println(strings.peek());//获取第一个元素
  19. System.out.println(strings);
  20. strings.addFirst("wgx");//在最前面添加元素
  21. System.out.println(strings);
  22. strings.addLast("wh");//在最后面添加元素,和add一样使用的都是linkLast()
  23. System.out.println(strings);
  24. System.out.println(strings.get(2));//获取指定下标元素
  25. strings.offer("zmy");//在最后添加元素,使用的也是linkLast();方法
  26. System.out.println(strings);
  27. strings.add("wh");
  28. System.out.println(strings);
  29. strings.removeLastOccurrence("wh");//移除相同的此元素中,最后一个该元素
  30. System.out.println(strings);
  31. strings.poll();//移除第一个元素
  32. System.out.println(strings);
  33. strings.pollFirst();//移除第一个元素
  34. strings.pollLast();//移除最后一个元素
  35. System.out.println(strings);
  36. System.out.println(strings.peekFirst());//读取第一个元素
  37. System.out.println(strings.peekLast());//读取最后一个元素
  38. System.out.println(strings);
  39. strings.add("wh");
  40. strings.add("wh");
  41. System.out.println(strings.indexOf("wh"));
  42. }
  43. }

List集合的特点:可以将元素维护在特定的序列中,并且按照它的存入的顺序进行存放,可以在中间插入或者删除元素,由两种类型的List,ArrayList和LinkedList。
ArrayList开辟一整片空间存入元素,按照插入的顺序保存元素,对进行随机访问元素较快
LinkedList类型,链表形式,按照插入顺序保存元素,通过指针的形式指向下一个元素,对插入和删除操作比较方便
Arrays.asList()方法返回的是new Arraylist(),首先将其转换成数组,之后调用System.arrayCopy方法,将其创建出来,由于他底层使用数组实现的,可以使用SystemCopy进行备份,但是无法对其进行修改
Set:首先,Set较于Collection接口,没有多的方法,并且Set的底层是由Map实现的。然后Set并不保存重复的元素

  1. public class HashSetDemo {
  2. public static void main(String[] args) {
  3. final HashSet<String> strings = new HashSet<>();
  4. strings.add("wuhu");
  5. strings.add("dag");//添加元素
  6. System.out.println(strings.contains("dag"));//是否包含后面的元素
  7. strings.remove("dag");//移除该元素
  8. System.out.println(strings);
  9. //System.out.println(strings.spliterator());
  10. System.out.println(strings.size());//集合中元素的个数
  11. final Object clone = strings.clone();
  12. System.out.println(clone);//克隆,Object类型
  13. }
  14. }
  1. public class TreeSetDemo {
  2. public static void main(String[] args) {
  3. final TreeSet<Integer> integerTreeSet = new TreeSet<>();
  4. integerTreeSet.add(1);
  5. integerTreeSet.add(2);
  6. integerTreeSet.add(3);
  7. integerTreeSet.add(4);//添加元素
  8. System.out.println(integerTreeSet);
  9. System.out.println(integerTreeSet.ceiling(0));//获取它的元素,如果没有这个元素
  10. //就会返回大于这个元素的最小的元素
  11. System.out.println(integerTreeSet);
  12. System.out.println(integerTreeSet.contains(4));//是否包含这个元素
  13. System.out.println(integerTreeSet.first());//读取第一个元素
  14. System.out.println(integerTreeSet.floor(100));//获取它的元素,如果没有这个元素
  15. //就会返回小于这个元素的最大的元素
  16. System.out.println(integerTreeSet.higher(0));//同ceiling方法
  17. System.out.println(integerTreeSet.last());//获取最后一个元素
  18. integerTreeSet.pollFirst();//移除第一个元素
  19. System.out.println(integerTreeSet);
  20. integerTreeSet.pollLast();//移除最后一个元素
  21. System.out.println(integerTreeSet);
  22. }
  23. }

填充容器

  1. class StringAddress{
  2. private String s;
  3. public StringAddress(String s) {
  4. this.s = s;
  5. }
  6. @Override
  7. public String toString() {
  8. return super.toString() + " " + s;
  9. }
  10. }
  11. public class FillingLists {
  12. public static void main(String[] args) {
  13. final ArrayList<StringAddress> list = new ArrayList<>(
  14. Collections.nCopies(4, new StringAddress("Hello")));//底层是将其转换成为数组
  15. System.out.println(list);
  16. Collections.fill(list,new StringAddress("world"));//只可以对已经存在的元素进行修改
  17. System.out.println(list);
  18. }
  19. }

Generator的解决方案

所有的Collection的子类型都有一个接受另外一个Collection对象的构造器

  1. public class CollectionData<T> extends ArrayList<T> {
  2. public CollectionData(Generator<T> gen, int quantity){
  3. for (int i = 0; i < quantity; i++) {
  4. add(gen.next());
  5. }
  6. }
  7. public static <T> CollectionData<T> list(Generator<T> gen,int quantity) {
  8. return new CollectionData<T>(gen, quantity);
  9. }
  10. }
  1. class Goverment implements Generator<String>{
  2. String[] s = ("strange women lying in ponds distributing swords is no " +
  3. "basis for a System of").split(" ");
  4. private int index;
  5. @Override
  6. public String next() {
  7. return s[index++];
  8. }
  9. }
  10. public class CollectionDataTest {
  11. public static void main(String[] args) {
  12. final CollectionData<String> strings = new CollectionData<>(new Goverment(), 15);
  13. final Set<String> set = new LinkedHashSet<>(strings);
  14. //通过接受的Collection对象中的元素来填充新的容器
  15. set.addAll(CollectionData.list(new Goverment(), 15));
  16. System.out.println(set);
  17. }
  18. }

Map生成器

  1. public class Pair<K,V> {
  2. public final K key;
  3. public final V value;
  4. public Pair(K k, V v) {
  5. key = k;
  6. value = v; //就是为了生成一对键值对,将他们定义成final类型,这样就可以让他们只读不能修改
  7. }
  8. }
  1. public class MapData<K, V> extends LinkedHashMap<K, V> {
  2. public MapData(Generator<Pair<K, V>> gen, int quantity) {
  3. for (int i = 0; i < quantity; i++) {
  4. Pair<K, V> p = gen.next();
  5. put(p.key, p.value);//生成键值对
  6. }
  7. }
  8. public MapData(Generator<K> genK, Generator<V> genV,
  9. int quantity) {
  10. for (int i = 0; i < quantity; i++) {
  11. put(genK.next(), genV.next());
  12. }
  13. }
  14. // 生成键
  15. public MapData(Generator<K> genK, V value, int quantity) {
  16. for (int i = 0; i < quantity; i++) {
  17. put(genK.next(), value);
  18. }
  19. }
  20. // 生成值
  21. public MapData(Iterable<K> genK, Generator<V> genV) {
  22. for (K key : genK) {
  23. put(key, genV.next());
  24. }
  25. }
  26. //
  27. public MapData(Iterable<K> genK, V value) {
  28. for (K key : genK) {
  29. put(key, value);
  30. }
  31. }
  32. public static <K, V> MapData<K, V>
  33. map(Generator<Pair<K, V>> gen, int quantity) {
  34. return new MapData<K, V>(gen, quantity);
  35. }
  36. public static <K, V> MapData<K, V>
  37. map(Generator<K> genK, Generator<V> genV, int quantity) {
  38. return new MapData<K, V>(genK, genV, quantity);
  39. }
  40. public static <K, V> MapData<K, V>
  41. map(Generator<K> genK, V value, int quantity) {
  42. return new MapData<K, V>(genK, value, quantity);
  43. }
  44. public static <K, V> MapData<K, V>
  45. map(Iterable<K> genK, Generator<V> genV) {
  46. return new MapData<K, V>(genK, genV);
  47. }
  48. public static <K, V> MapData<K, V>
  49. map(Iterable<K> genK, V value) {
  50. return new MapData<K, V>(genK, value);
  51. }
  52. }
  1. class Letters implements Generator<Pair<Integer, String>>,
  2. Iterable<Integer> {
  3. private int size = 9;
  4. private int number = 1;
  5. private char letter = 'A';
  6. public Pair<Integer, String> next() {
  7. return new Pair<Integer, String>(
  8. number++, "" + letter++); //生成键值对
  9. }
  10. public Iterator<Integer> iterator() {
  11. return new Iterator<Integer>() {
  12. public Integer next() {
  13. return number++; //对键进行迭代
  14. }
  15. public boolean hasNext() {
  16. return number < size;
  17. }
  18. public void remove() {
  19. throw new UnsupportedOperationException();
  20. }//执行此操作就会报错
  21. };
  22. }
  23. }
  24. public class MapDataTest {
  25. public static void main(String[] args) {
  26. // 生成键,值是固定的
  27. print(MapData.map(new Letters(), 11));
  28. //将字符作为键,将随机产生字符串作为值
  29. print(MapData.map(new CountingGenerator.Character(),
  30. new RandomGenerator.String(3), 8));
  31. // 将字符作为键,值是固定的
  32. print(MapData.map(new CountingGenerator.Character(),
  33. "Value", 6));
  34. //对它的键迭代并且随机产生字符串最为值
  35. print(MapData.map(new Letters(),
  36. new RandomGenerator.String(3)));
  37. // 对键进行迭代,并且值是不变的
  38. print(MapData.map(new Letters(), "Pop"));//对应上方代码大方法
  39. }
  40. }

使用一个abstract类

Collection的功能方法

  1. public class CollectionMethods {
  2. public static void main(String[] args) {
  3. Collection<String> c = new ArrayList<String>();
  4. c.addAll(net.mindview.util.Countries.names(6));
  5. c.add("ten");
  6. c.add("eleven");//添加元素
  7. print(c);
  8. Object[] array = c.toArray();//转换称为数组
  9. String[] str = c.toArray(new String[0]);
  10. print("Collections.max(c) = " + Collections.max(c));//找出最大的元素
  11. print("Collections.min(c) = " + Collections.min(c));//找到最小的元素
  12. Collection<String> c2 = new ArrayList<String>();
  13. c2.addAll(net.mindview.util.Countries.names(6));
  14. c.addAll(c2);
  15. print(c);//添加后面的集合中所有的元素
  16. c.remove(net.mindview.util.Countries.DATA[0][0]);
  17. print(c);
  18. c.remove(net.mindview.util.Countries.DATA[1][0]);
  19. print(c);
  20. c.removeAll(c2);//移除后面集合中所有的元素
  21. print(c);
  22. c.addAll(c2);
  23. print(c);
  24. // 是否包含这个元素
  25. String val = net.mindview.util.Countries.DATA[3][0];
  26. print("c.contains(" + val + ") = " + c.contains(val));
  27. //是否包含这个集合
  28. print("c.containsAll(c2) = " + c.containsAll(c2));
  29. Collection<String> c3 = ((List<String>) c).subList(3, 5);
  30. //除了后面的集合中包含的元素,其余都删除
  31. c2.retainAll(c3);
  32. print(c2);
  33. // 删除后面集合中包含的元素
  34. c2.removeAll(c3);
  35. print("c2.isEmpty() = " + c2.isEmpty());//判断是否为空
  36. c = new ArrayList<String>();
  37. c.addAll(Countries.names(6));
  38. print(c);
  39. c.clear(); //清楚所有的元素
  40. print("after c.clear():" + c);
  41. }
  42. }

可选操作

执行某些方法就会抛出异常

  1. public class Unsupported {
  2. static void test(String msg, List<String> list) {
  3. System.out.println("--- " + msg + " ---");
  4. Collection<String> c = list;
  5. Collection<String> subList = list.subList(1,8);
  6. Collection<String> c2 = new ArrayList<String>(subList);
  7. try { c.retainAll(c2); } catch(Exception e) {
  8. System.out.println("retainAll(): " + e);
  9. }
  10. try { c.removeAll(c2); } catch(Exception e) {
  11. System.out.println("removeAll(): " + e);
  12. }
  13. try { c.clear(); } catch(Exception e) {
  14. System.out.println("clear(): " + e);
  15. }
  16. try { c.add("X"); } catch(Exception e) {
  17. System.out.println("add(): " + e);
  18. }
  19. try { c.addAll(c2); } catch(Exception e) {
  20. System.out.println("addAll(): " + e);
  21. }
  22. try { c.remove("C"); } catch(Exception e) {
  23. System.out.println("remove(): " + e);
  24. }
  25. try {
  26. list.set(0, "X");
  27. } catch(Exception e) {
  28. System.out.println("List.set(): " + e);
  29. }
  30. }
  31. public static void main(String[] args) {
  32. List<String> list =
  33. Arrays.asList("A B C D E F G H I J K L".split(" "));
  34. test("Modifiable Copy", new ArrayList<String>(list)); //可以执行上述的方法,所以不会报错
  35. test("Arrays.asList()", list);//因为是asList形式,仅支持读和此修改的操作,改变大小的操作
  36. //都不能够执行
  37. test("unmodifiableList()",
  38. Collections.unmodifiableList(
  39. new ArrayList<String>(list)));//虽然是ArrayList形式,但是由unmodifiablelist包裹,走的是
  40. //UnmodifiableList(List<? extends E> list)这个内部类
  41. }
  42. }

Set和存储顺序

set:不保存重复元素,和Collection接口中的方法一样
HashSet:根据它的hashcode保存元素,查找非常的快速
TreeSet:底层是树结构,可以对添加进来的元素进行排序
LinkedhashSet:拥有hashset的查询速度,按照插入的顺序保存元素
set的底层是由map实现的

  1. class SetType {
  2. int i;
  3. public SetType(int n) { i = n; }
  4. public boolean equals(Object o) {
  5. return o instanceof SetType && (i == ((SetType)o).i);
  6. }
  7. // public int hashCode(){
  8. // return i;
  9. // }
  10. public String toString() { return Integer.toString(i); }
  11. }
  12. class HashType extends SetType {
  13. public HashType(int n) { super(n); }
  14. public int hashCode() { return i; }
  15. }
  16. class TreeType extends SetType
  17. implements Comparable<TreeType> {
  18. public TreeType(int n) { super(n); }
  19. public int compareTo(TreeType arg) {
  20. return (Integer.compare(arg.i, i));
  21. }
  22. /*public int hashCode(){
  23. return i;
  24. }*/
  25. }
  26. public class TypesForSets {
  27. static <T> Set<T> fill(Set<T> set, Class<T> type) {
  28. try {
  29. for(int i = 0; i < 10; i++)
  30. set.add(//获取传进来的Class对象的构造器
  31. type.getConstructor(int.class).newInstance(i));//根据反射来创建实例
  32. } catch(Exception e) {
  33. throw new RuntimeException(e);
  34. }
  35. return set;
  36. }
  37. static <T> void test(Set<T> set, Class<T> type) {
  38. fill(set, type);
  39. fill(set, type);
  40. fill(set, type);
  41. System.out.println(set);
  42. }
  43. public static void main(String[] args) {
  44. test(new HashSet<HashType>(), HashType.class);//由于HashType这个类重写了hashcode,就算执行100次,也只会输出10个元素
  45. test(new LinkedHashSet<HashType>(), HashType.class);
  46. test(new TreeSet<TreeType>(), TreeType.class);
  47. test(new HashSet<SetType>(), SetType.class);//以下的类中没有重写hashcode方法,所以会输出30个元素
  48. test(new HashSet<TreeType>(), TreeType.class);
  49. test(new LinkedHashSet<SetType>(), SetType.class);
  50. test(new LinkedHashSet<TreeType>(), TreeType.class);
  51. try {
  52. test(new TreeSet<SetType>(), SetType.class);
  53. } catch(Exception e) {
  54. System.out.println(e.getMessage());
  55. }
  56. try {
  57. test(new TreeSet<HashType>(), HashType.class);
  58. } catch(Exception e) {
  59. System.out.println(e.getMessage());
  60. }
  61. }
  62. }

SortedSet

Sorted中的元素可以处于排序状态

  1. public class SortedSetDemo {
  2. public static void main(String[] args) {
  3. SortedSet<String> sortedSet = new TreeSet<String>();
  4. Collections.addAll(sortedSet,
  5. "one two three four five six seven eight"
  6. .split(" "));
  7. print(sortedSet); //根据asci码对其进行排序
  8. String low = sortedSet.first();//获取第一个元素
  9. String high = sortedSet.last();//获取最后的一个元素
  10. //print(low);
  11. //print(high);
  12. Iterator<String> it = sortedSet.iterator();
  13. for (int i = 0; i <= 6; i++) {
  14. if (i == 3) low = it.next();
  15. if (i == 6) high = it.next();
  16. else it.next();
  17. }
  18. print(low);
  19. print(high);
  20. print(sortedSet.subSet(low, high));//获取视图,左闭右开
  21. print(sortedSet.headSet(high));//获取high之前的元素
  22. print(sortedSet.tailSet(low));//获取low之后的元素
  23. }
  24. }

队列

LinkedList和PriorityQueue 他们的差异在于排序行为而不是性能

  1. public class QueueBehavior {
  2. private static int count = 10;
  3. static <T> void test(Queue<T> queue, Generator<T> gen) {
  4. for (int i = 0; i < 10; i++) {
  5. queue.offer(gen.next());
  6. }
  7. while (queue.peek() != null) {
  8. System.out.print(queue.remove() + " ");
  9. }
  10. System.out.println();
  11. }
  12. static class Gen implements Generator<String> {
  13. String[] s = "one two three four five six seven eight nine ten".split(" ");
  14. int i;
  15. @Override
  16. public String next() {
  17. return s[i++];
  18. }
  19. }
  20. public static void main(String[] args) {
  21. test(new LinkedList<>(), new Gen());
  22. test(new PriorityQueue<>(), new Gen());//根据元素的Ascii码的元素顺序排序
  23. test(new ArrayBlockingQueue<String>(count), new Gen());//使用固定的容量和默认的访问策略创建一个队列
  24. test(new ConcurrentLinkedQueue<>(), new Gen());
  25. test(new LinkedBlockingQueue<>(), new Gen());//创建一个容量为Integer.Max_VALUE的集合
  26. test(new PriorityBlockingQueue<>(),new Gen());
  27. }
  28. }

优先级队列

列表中每个对象都包含一个字符串和一个主要的以及次要的优先级值,该列表的排列顺序也是通过实现comparable而进行控制的

  1. public class ToDoList extends PriorityQueue<ToDoList.ToDoItem> {
  2. static class ToDoItem implements Comparable<ToDoItem> {
  3. private char primary;
  4. private int secondary;
  5. private String item;
  6. public ToDoItem(String td, char pri,int sec) {
  7. primary = pri;
  8. secondary = sec;
  9. item = td;
  10. }
  11. @Override
  12. public int compareTo(ToDoItem arg) {
  13. if (primary > arg.primary) {//先根据char来判断它的顺序
  14. return +1;
  15. }
  16. if (primary == arg.primary) {
  17. //如果字符相等,就根据字符后面的数字来判断顺序
  18. if (secondary > arg.secondary) {
  19. return +1;
  20. } else if (secondary == arg.secondary) {
  21. return 0;
  22. }
  23. }
  24. return -1;
  25. }
  26. @Override
  27. public String toString() {
  28. return Character.toString(primary) + secondary + ": " + item;
  29. }
  30. }
  31. public void add(String td, char pri, int sec) {
  32. super.add(new ToDoItem(td, pri, sec));
  33. }
  34. public static void main(String[] args) {
  35. ToDoList toDoList = new ToDoList();
  36. toDoList.add("Empty trash", 'C', 4);
  37. toDoList.add("Feed dog", 'A', 2);
  38. toDoList.add("Feed bird", 'B', 7);
  39. toDoList.add("Mow lawn", 'C', 3);
  40. toDoList.add("Water lawn", 'A', 1);
  41. toDoList.add("Feed cat", 'B', 1);
  42. while (!toDoList.isEmpty()) {
  43. System.out.println(toDoList.remove());
  44. }
  45. }
  46. }

双向队列

双向队列(双端队列)就像是一个队列,但是可以在任何一端添加或者移除元素。LinkedList无法实现这样的接口,可以使用组合来创建一个Deque类(见工具类)

  1. public class DequeTest {
  2. static void fillTest(Deque<Integer> deque) {
  3. for (int i = 20; i < 27; i++) {
  4. deque.addFirst(i);//加到前面
  5. }
  6. for (int i = 50; i < 55; i++) {
  7. deque.addLast(i);//加到后面
  8. }
  9. }
  10. public static void main(String[] args) {
  11. Deque<Integer> di = new Deque<>();
  12. fillTest(di);
  13. System.out.println(di);
  14. while (di.size() != 0) {
  15. System.out.print(di.removeLast()+" ");//从后面开始移除
  16. }
  17. }
  18. }

理解Map

映射表(关联数组)的基本思想是它维护的是键-值对关联,因此可以使用键来查找值
利用二位数组来模拟MapAPI的实现

  1. public class AssociativeArray<K, V> {
  2. private Object[][] pairs;
  3. private int index;
  4. public AssociativeArray(int length) {
  5. pairs = new Object[length][2];
  6. }
  7. public void put(K key, V value) {
  8. if (index >= pairs.length) {
  9. throw new ArrayIndexOutOfBoundsException();
  10. }
  11. pairs[index++] = new Object[]{key, value};
  12. }
  13. public V get(K key) {
  14. for (int i = 0; i < index; i++) {
  15. if (key.equals(pairs[i][0])) {//如果和key相等的话,就返回它的value
  16. return (V) pairs[i][1];
  17. }
  18. }
  19. return null;
  20. }
  21. @Override
  22. public String toString() {
  23. StringBuilder result = new StringBuilder();//拼接
  24. for (int i = 0; i < index; i++) {
  25. result.append(pairs[i][0].toString());
  26. result.append(": ");
  27. result.append(pairs[i][1].toString());
  28. if (i < index - 1) {
  29. result.append("\n");
  30. }
  31. }
  32. return result.toString();
  33. }
  34. public static void main(String[] args) {
  35. AssociativeArray<String, String> map = new AssociativeArray<>(6);
  36. map.put("sky", "blue");
  37. map.put("grass", "green");
  38. map.put("ocean", "dancing");
  39. map.put("tree", "tall");
  40. map.put("earth", "brown");
  41. map.put("sun", "warm");
  42. try {
  43. map.put("extra", "object");
  44. } catch (ArrayIndexOutOfBoundsException e) {
  45. System.out.println("Too many objects");
  46. }
  47. System.out.println(map);
  48. System.out.println(map.get("ocean"));
  49. }
  50. }

性能

性能是映射表中的一个很重要的问题,当在get()中使用线性搜索的时候,执行速度会变得相当的慢,所以HashMap使用了特殊的值,称作散列码,来取代对键的缓慢搜索,散列码是“相对唯一的”用以代表对象的int值
image.png
解决Hash冲突:一开始的容量是16,当存放的元素为16*0.75个时,就会扩容成为它的两倍
Map中键的的要求和Set中元素的要求是一样的,任何的键都必须具有一个equals方法,如果键被用于散列Map,那么它还必须具有恰当的hashCode方法,如果键被用于TreeMap,那么它必须实现Comparable

  1. public class Maps {
  2. public static void printKey(Map<Integer, String> map) {
  3. System.out.print("Size = " + map.size() + ". ");
  4. System.out.print("Keys : ");
  5. System.out.println(map.keySet());//返回由Map键组成的Set集合
  6. }
  7. public static void test(Map<Integer, String> map) {
  8. System.out.println(map.getClass().getSimpleName());
  9. map.putAll(new CountingMapData(25));
  10. map.putAll(new CountingMapData(25));
  11. printKey(map);
  12. System.out.println("Values" + map.values());
  13. System.out.println(map);
  14. System.out.println("map.containsKey : " + map.containsKey(11));
  15. System.out.println("map.get(11) : " + map.get(11));
  16. System.out.println("map.containsValue(\"F0\") : " + map.containsValue("F0"));
  17. Integer key = map.keySet().iterator().next();
  18. System.out.println("First Key in map: " + key);
  19. map.remove(key);
  20. printKey(map);
  21. map.clear();
  22. System.out.println("map.isEmpty: " + map.isEmpty());
  23. map.putAll(new CountingMapData(25));
  24. map.keySet().removeAll(map.keySet());
  25. System.out.println("map.isEmpty: " + map.isEmpty());
  26. }
  27. public static void main(String[] args) {
  28. test(new HashMap<>());
  29. }
  30. }

SortedMap

可以确保键可以处于排序状态

  1. public class SortedMapDemo {
  2. public static void main(String[] args) {
  3. TreeMap<Integer, String> sortedMap = new TreeMap<>(new CountingMapData(25));
  4. System.out.println(sortedMap);
  5. Integer low = sortedMap.firstKey();
  6. Integer high = sortedMap.lastKey();
  7. System.out.println(low);
  8. System.out.println(high);
  9. Iterator<Integer> it = sortedMap.keySet().iterator();
  10. for (int i = 0; i <= 6; i++) {
  11. if (i == 3) {
  12. low = it.next();
  13. }
  14. if (i == 6) {
  15. high = it.next();
  16. } else {
  17. it.next();
  18. }
  19. }
  20. System.out.println(low);
  21. System.out.println(high);
  22. System.out.println(sortedMap.subMap(low, high));
  23. System.out.println(sortedMap.headMap(high));
  24. System.out.println(sortedMap.tailMap(low));
  25. }
  26. }

散列和散列码

  1. public class Groundhog {
  2. protected int number;
  3. public Groundhog(int number) {
  4. this.number = number;
  5. }
  6. @Override
  7. public String toString() {
  8. return "Ground# " + number;
  9. }
  10. }
  11. public class Prediction {
  12. private static Random rand = new Random(47);
  13. private boolean shadow = rand.nextDouble() > 0.5;
  14. @Override
  15. public String toString() {
  16. if (shadow){
  17. return "Six more weeks of winter";
  18. }
  19. else{
  20. return "Early spring";
  21. }
  22. }
  23. }
  24. public class SpringDetector {
  25. public static <T extends Groundhog> void detectSpring(Class<T> type) throws Exception {
  26. Constructor<T> ghog = type.getConstructor(int.class);
  27. final Map<Groundhog, Prediction> map = new HashMap<>();
  28. for (int i = 0; i < 10; i++) {
  29. map.put(ghog.newInstance(i), new Prediction());
  30. }
  31. System.out.println("map: " + map);
  32. Groundhog gh = ghog.newInstance(3);
  33. //虽然像构造器中传的是3,但是和map比较的时候用的是Object的equals,比较的是地址
  34. //所以它不在map中,所以并不能根据它来当作key来取出来value
  35. System.out.println("Looking up prediction for " + gh);
  36. if (map.containsKey(gh)) {
  37. System.out.println(map.get(gh));
  38. } else {
  39. System.out.println("Key not Found" + gh);
  40. }
  41. }
  42. public static void main(String[] args) throws Exception {
  43. detectSpring(Groundhog.class);
  44. }
  45. }

如果想让gh可以取出在map中的value的话,必须同时重写hashcode和equals方法

  1. public class Groundhog2 extends Groundhog{
  2. public Groundhog2(int n) {
  3. super(n);
  4. }
  5. @Override
  6. public int hashCode() {
  7. return number;
  8. }
  9. @Override
  10. public boolean equals(Object o) {
  11. return o instanceof Groundhog2 && (number == ((Groundhog2) o).number);
  12. }
  13. }
  14. public class SpringDetector2 {
  15. public static void main(String[] args) throws Exception {
  16. SpringDetector.detectSpring(Groundhog2.class);
  17. }
  18. }

hash()用于计算在hash表中的位置,然后再使用equals判断他俩是否相等,所以如果想让他俩相等的话都需要重写

为速度而散列

散列的价值在于速度,散列使得查询得以快速进行。
散列将键存在某处,以便可以很快的找到,存储一组元素最快的数据结构式数组,所以使用它来表示键的信息。(保存的是键的信息,而不是键本身)
数组并不保存键本身,而是通过键对象生成一个数字,将其作为数组的下标,这个数组就是散列码
因为数组的容量是固定的,所以不同的键可以产生相同的下标,但是可能会有冲突,因此数组多大并不重要,任何键总能在数组中找到它的位置
通常,冲突是由外部连接进行处理的,数组并不直接保存值,而是保存值的list,然后对list中的值使用equals方式进行线性的查询

  1. public class SimpleHashMap<K, V> extends AbstractMap<K, V> {
  2. static final int SIZE = 997;
  3. @SuppressWarnings("unchecked")
  4. LinkedList<MapEntry<K, V>>[] buckets =
  5. new LinkedList[SIZE];
  6. public V put(K key, V value) {
  7. V oldValue = null;
  8. //计算数组下标
  9. int index = Math.abs(key.hashCode()) % SIZE;
  10. //判断数组下标处链表是否初始化
  11. if (buckets[index] == null)
  12. //初始化
  13. buckets[index] = new LinkedList<>();
  14. //持有数组下标处链表的引用
  15. LinkedList<MapEntry<K, V>> bucket = buckets[index];//
  16. //链表将要保存或覆盖的Entry
  17. MapEntry<K, V> pair = new MapEntry<K, V>(key, value);
  18. boolean found = false;
  19. //迭代数组下标处的链表
  20. ListIterator<MapEntry<K, V>> it = bucket.listIterator();
  21. while (it.hasNext()) {
  22. MapEntry<K, V> iPair = it.next();
  23. //判断是否已经放入容器中 包含则覆盖
  24. if (iPair.getKey().equals(key)) {
  25. oldValue = iPair.getValue();
  26. it.set(pair);
  27. found = true;
  28. break;
  29. }
  30. }
  31. //不包含放入链表尾端
  32. if (!found)
  33. buckets[index].add(pair);
  34. return oldValue;
  35. }
  36. public V get(Object key) {
  37. //根据key的哈希值计算桶下标
  38. int index = Math.abs(key.hashCode()) % SIZE;
  39. if (buckets[index] == null) return null;
  40. for (MapEntry<K, V> iPair : buckets[index])
  41. //如果包含,返回它的值
  42. if (iPair.getKey().equals(key))
  43. return iPair.getValue();
  44. return null;
  45. }
  46. public Set<Entry<K, V>> entrySet() {
  47. Set<Entry<K, V>> set = new HashSet<Entry<K, V>>();
  48. for (LinkedList<MapEntry<K, V>> bucket : buckets) {
  49. //对链表进行遍历
  50. if (bucket == null) continue;//如果为空继续下一次寻找
  51. for (MapEntry<K, V> mpair : bucket)
  52. set.add(mpair);
  53. }
  54. return set;
  55. }
  56. public static void main(String[] args) {
  57. SimpleHashMap<String, String> m =
  58. new SimpleHashMap<String, String>();
  59. m.putAll(Countries.capitals(25));
  60. System.out.println(m);
  61. System.out.println(m.get("ERITREA"));
  62. System.out.println(m.entrySet());
  63. }
  64. }

覆盖hashcode()

散列码不必是独一无二的,应该关注它的速度,而不是唯一性,但是使用hashCode()和equals(),必须能完全确定对象的身份