java07 map
map底层,数组加链表
集合: 是一个对象,只不过这个对象可以容纳别的对象。存放对象就是操作地址。 List:是有序可重复的。 Set:无顺序,不可重复,有重复则后面把前面的覆盖。 Map:键值对。 四大接口(Collection、Set、List、Map): --Collection(集合) --Set(没有顺序,不可重复) --HashSet --List(有顺序,重复) --Map --HashMap Collection为null表示容器都没有,Collection的方法:Collection为isEmpty()表示容器有,但是容器为空。Iterator<E> iterator()遍历容器,Object[] toArray()容器转换为数组,boolean add(E e)放入到容器,boolean remove(Object o)表示移除一个元素,但是这个元素还在,删除就是这个元素也没有了。boolean containsAll(Collection<?> c)有没有包含另一个容器里面所有的元素,boolean addAll(Collection<? extends E> c)把另一个容器的所有元素都包含进去, boolean removeAll(Collection<?> c)移除另一个容器中的所有元素,boolean retainAll(Collection<?> c)两个容器取交集,void clear()清除容器。 Set和List为Collection的子类,所以Set、List继承了Collection的所有方法。 List list = new ArrayList(); ArrayList数组列表,底层是private transient Object[] elementData(一个Object数组), public class Test01 { @SuppressWarnings("unchecked") public static void main(String[] args) { List list = new ArrayList(); //ArrayList:底层实现时数组,线程不安全,效率高。所以,查询快(数组查询最快,挨个遍历)。修改、插入(插入一个后面也要移动)、删除(后面也要移动)慢。 //LinkedList:底层实现是链表,线程不安全,效率高。所以,查询慢(一个个的挨着向后找)。修改、插入(改变指针就可以了)、删除(改变指针就可以了)快。 //Vector:底层也是数组实现,线程安全的(多个线程共享的时候会有线程安全问题,但是定义成局部变量就跟线程没有关系),效率低。 list.add("aaa"); list.add("aaa"); list.add(new Date()); list.add(new Dog()); list.add(1234); //包装类的:自动装箱!ArrayList里面用的是一个Object对象数组,1234不是对象,理论上是不能存进去的,但是会自动转为Integer对象。 list.remove(new String("aaa")); System.out.println(list.size()); for(int i=0;i<list.size();i++){ System.out.println(list.get(i)); } list.set(3, new String("3333")); list.add(4, new String("3333")); System.out.println(list.isEmpty()); list.remove(new Dog()); //hashcode和equals System.out.println(list.size()); List list2 = new ArrayList(); list2.add("bbb"); list2.add("ccc"); list.add(list2); //跟顺序的操作 String str = (String) list.get(0);//返回的是Object类型 System.out.println(str); list.set(1, "ababa"); list.remove(0); } } class Dog { } System.arraycopy(elementData, index+1, elementData, index, numMoved);//原数组,原数组起点,目标数组,目标数组地点 list.remove(new String("aaa")); list.remove("aaa"); new String("aaa")和"aaa"是2个不同的对象。 Dog a1 = new Dog();//cn.bjsxt.collection.Dog@123b25c Dog a2 = new Dog();//cn.bjsxt.collection.Dog@2ba11b System.out.println(a1 == a2);//false System.out.println(a1.equals(a2));//false /** * 自己实现一个ArrayList,帮助我们更好的理解ArrayList类的底层结构! * */ public class SxtArrayList /*implements List*/ { private Object[] elementData; private int size; public int size(){ return size; } public boolean isEmpty(){ return size==0; } public SxtArrayList(){ this(10); } public SxtArrayList(int initialCapacity){ if(initialCapacity<0){ try { throw new Exception(); } catch (Exception e) { e.printStackTrace(); } } elementData = new Object[initialCapacity]; } public void add(Object obj){ //数组扩容和数据的拷贝 if(size==elementData.length){ Object[] newArray = new Object[size*2+1]; System.arraycopy(elementData, 0, newArray, 0, elementData.length); for(int i=0;i<elementData.length;i++){ newArray[i] = elementData[i]; } elementData = newArray; } elementData[size++]=obj; size++; } public Object get(int index){ rangeCheck(index); return elementData[index]; } public void remove(int index){ rangeCheck(index); //删除指定位置的对象 //a b d e int numMoved = size - index - 1; if (numMoved > 0){ System.arraycopy(elementData, index+1, elementData, index, numMoved);//原数组,原数组起点,目标数组,目标数组地点 } elementData[--size] = null; // Let gc do its work } public void remove(Object obj){ for(int i=0;i<size;i++){ if(get(i).equals(obj)){ //注意:底层调用的equals方法而不是==. remove(i); } } } public Object set(int index,Object obj){ rangeCheck(index); Object oldValue = elementData[index]; elementData[index] = obj; return oldValue; } public void add(int index,Object obj){ rangeCheck(index); ensureCapacity(); //数组扩容 System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = obj; size++; } private void ensureCapacity(){ //数组扩容和数据的拷贝 if(size==elementData.length){ Object[] newArray = new Object[size*2+1]; System.arraycopy(elementData, 0, newArray, 0, elementData.length);//原数组,被拷贝的起点索引。目标数组,拷贝到的起点索引,拷贝的长度。 for(int i=0;i<elementData.length;i++){ newArray[i] = elementData[i]; } elementData = newArray; } } private void rangeCheck(int index){ if(index<0||index>=size){ try { throw new Exception(); } catch (Exception e) { e.printStackTrace(); } } } public static void main(String[] args) { SxtArrayList list = new SxtArrayList(3); list.add("333"); list.add("444"); list.add("5"); list.add("344433"); list.add("333"); list.add("333"); System.out.println(list.size()); // System.out.println(list.get(6)); list.remove("444"); System.out.println(list.size()); } } //链表操作链表操作链表操作链表操作链表操作链表操作链表操作 //链表操作链表操作链表操作链表操作链表操作链表操作链表操作 import java.util.LinkedList; //双向链表 public class SxtLinkedList /*implements List*/ { private Node first;//由于是双向链表所以有首尾节点,而且只维护了首尾节点。 private Node last; private int size; public void add(Object obj){ Node n = new Node(); if(first==null){ n.setPrevious(null); n.setObj(obj); n.setNext(null); first = n; last = n;//赋值就是改变栈中变量的地址值的指向 }else{ //直接往last节点后增加新的节点 n.setPrevious(last); n.setObj(obj); n.setNext(null); last.setNext(n);//堆中这个对象的里面的值改变 last = n;//改变栈中last的地址值的指向。 } size++; } public int size(){ return size; } private void rangeCheck(int index){ if(index<0||index>=size){ try { throw new Exception(); } catch (Exception e) { e.printStackTrace(); } } } public Object get(int index){ //2 rangeCheck(index); // 0 1 2 3 4 Node temp = node(index); if(temp!=null){ return temp.obj; } return null; } public Node node(int index){ Node temp = null; if(first!=null){ temp = first;//temp指向第一个节点对象 for(int i=0;i<index;i++){ temp = temp.next;//这里的赋值就是temp的地址值的指向依次向下移动。 } } LinkedList l; return temp; } public void remove(int index){ Node temp = node(index); if(temp!=null){ Node up = temp.previous;//对象赋值就是传递地址 Node down = temp.next; up.next = down; down.previous = up; size--; } } public void add(int index,Object obj){//数组列表的插入要做数组的copy,链表的插入直接打断连接。 Node temp = node(index); Node newNode = new Node(); newNode.obj = obj; if(temp!=null){ Node up = temp.previous;//对象的赋值赋的是地址的值, //互相指向 up.next = newNode; newNode.previous = up; //互相指向 newNode.next = temp; temp.previous = newNode; size++; } } public static void main(String[] args) { SxtLinkedList list = new SxtLinkedList(); list.add("aaa"); list.add("bbb"); list.add(1,"BBBB"); list.add("ccc"); // list.remove(1); System.out.println(list.get(1)); } } //用来表示一个节点 public class Node { Node previous; //上一个节点 Object obj; Node next; //下一个节点 public Node() { } public Node(Node previous, Object obj, Node next) { super(); this.previous = previous; this.obj = obj; this.next = next; } public Node getPrevious() { return previous; } public void setPrevious(Node previous) { this.previous = previous; } public Object getObj() { return obj; } public void setObj(Object obj) { this.obj = obj; } public Node getNext() { return next; } public void setNext(Node next) { this.next = next; } } //链表操作链表操作链表操作链表操作链表操作链表操作链表操作 Map map中的key,value也是对象,key不能重复,Map中存的也是对象的地址。map.remove("高琪");表示只是把这个对象从容器中移除,这个对象并没有删除还在。 HashMap效率高线程不安全,HashTable效率低线程安全。 Map实现1: /** *自定义实现Map的功能! *暂不完美! *Map:存放键值对,根据键对象找对应的值对象.键不能重复! * */ public class SxtMap001 { SxtEntry[] arr = new SxtEntry[990];//SxtEntry数组 int size; public void put(Object key,Object value){ SxtEntry e = new SxtEntry(key,value); //解决键值重复的处理 for(int i=0;i<size;i++){ if(arr[i].key.equals(key)){ arr[i].value=value; return ; } } arr[size++] = e; } public Object get(Object key){//每次查的时候都要遍历一次,所以效率低。 for(int i=0;i<size;i++){ if(arr[i].key.equals(key)){ return arr[i].value; } } return null; } public boolean containsKey(Object key){ for(int i=0;i<size;i++){ if(arr[i].key.equals(key)){ return true; } } return false; } public boolean containsValue(Object value){ for(int i=0;i<size;i++){ if(arr[i].value.equals(value)){ return true; } } return false; } public static void main(String[] args) { SxtMap001 m = new SxtMap001(); m.put("高琪", new Wife("杨幂")); m.put("高琪", new Wife("李四")); Wife w = (Wife) m.get("高琪"); System.out.println(w.name); } } class SxtEntry {//定义一个类Entry(条目),里面是键值对。 Object key; Object value; public SxtEntry(Object key, Object value) { super(); this.key = key; this.value = value; } } Map实现2,提高查询的效率。 对象的地址是根据哈希码生成的一个数。 Map的底层结构就是数组+链表(一个Map整体是一个数组,每个数组元素是一个链表,每个链表的节点是一个键值对的对象). public class SxtMap002 { //底层仍然是数组,每个数组元素是一个链表,链表的每个节点是key、value对。就是说一个数组元素里面,可以存多个值,取的时候遍历这个数组元素的链表。 LinkedList[] arr = new LinkedList[9]; //Map的底层结构就是:数组+链表! int size; public void put(Object key,Object value){ SxtEntry e = new SxtEntry(key,value); int a = key.hashCode()%arr.length; if(arr[a]==null){ LinkedList list = new LinkedList(); arr[a] = list;//数组的每个元素是链表, list.add(e);//链表的每个节点是键值对。 }else{ LinkedList list = arr[a]; for(int i=0;i<list.size();i++){ SxtEntry e2 = (SxtEntry) list.get(i); if(e2.key.equals(key)){ e2.value = value; //键值重复直接覆盖! return; } } arr[a].add(e); } //a:1000-->1 b:10000-->13 } public Object get(Object key){ int a = key.hashCode()%arr.length;//获取这个对象的索引。 if(arr[a]!=null){//找到这个数组元素,且不为空。 LinkedList list = arr[a]; for(int i=0;i<list.size();i++){//遍历这个链表(某一个数组元素) SxtEntry e = (SxtEntry) list.get(i); if(e.key.equals(key)){ return e.value; } } } return null; } public static void main(String[] args) { SxtMap002 m = new SxtMap002(); m.put("高琪", new Wife("杨幂")); m.put("高琪", new Wife("李四")); Wife w = (Wife) m.get("高琪"); System.out.println(w.name); } } public class Wife { public Wife(String string) { } } HashCode: java中,两个内容相同的对象有相同的hashcodes,2个对象调用equals方法返回true则hashcodes一定相等。哈希算法可以快速定位这个对象存放的地方。 HashCode方法和equals要重写的话,这2个方法要一起重写,保证equals方法为true则hashcode一定相等。 =是判断2个对象是不是同一个对象,equals方法是判断2个对象的内容是不是相等。 Object类的equals方法是用=实现的,即比较对象的地址是不是相等。 string也重写了equals方法,即比较值是不是相等。 public class Student extends Object{ //类的equals、hashCode方法有一个重写则2个都要重写。 private Integer id;//Integer(包装类)的equals、hashCode重写了,即比较数值是不是相等。 private String name;//String的equals、hashCode方法重写了,比较字符串值。 private Date birthday;//Date类的equals、hashCode,比较毫秒数是不是相等。 //如果这2个方法不重写,默认hashCode是返回对象的地址,equals是比较地址(比较是不是同一个对象) //重写后,这里根据id和name进行重写,如果是同一个对象则为true,如果id和name相等就是true @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } //这里的equals是比较2个对象的id和name是不是一样的,一样则这2个对象相等(equals为true)。 @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Student other = (Student) obj; if (id == null) { if (other.id != null) return false; } else if (!id.equals(other.id)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } } public class TestEquals { public static void main(String[] args) { List list = new ArrayList();//List有顺序可重复 Integer s1 = new Integer(243); Integer s2 = new Integer(243); list.add(s1); list.add(s2); System.out.println(list.size()); Map map = new HashMap(); //键不能重复(判断键是不是重复了是通过equals方法)。 map.put(s1, "AAAA"); map.put(s2, "BBBBBB"); System.out.println(map.get(243)); } }