java的容器
1.数组与其他容器的区别
效率较容器高(访问数据的效率是最高的),大小在生命周期类不能改变,可以存储基本类似的数据,而其他容器在运行时都是作为Object处理(编译时会进行检验,编译时泛型会去除作为Object处理,容器存储基本类型的原因是自动装拆箱,所以效率会降低。
2.List
ArriyList 基于数组,查询与更改效率高,增删效率低 ,可重复。
自定义ArriyList 集合
public class MyArraylist { private Object arrList[]; private int size; public MyArraylist() { this(10); } public MyArraylist(int bound) { if (bound < 0) { try { throw new Exception(); } catch (Exception e) { e.printStackTrace(); } } arrList = new Object[bound]; } public void add(Object obj) { if (size >= arrList.length) { Object arrList1[] = new Object[2 * size + 1]; System.arraycopy(arrList, 0, arrList1, 0, size); arrList = arrList1; } else { arrList[size++] = obj; } } public int size() { return size; } public Object get(int index) { boundCheck(index); return arrList[index]; } public void remove(int index) { boundCheck(index); System.arraycopy(arrList, index + 1, arrList, index, size); arrList[size--] = null; } public boolean remove(Object obj) { for (int i = 0; i < size; i++) { if (arrList[i].equals(obj)) { remove(i); return true; } } return false; } public Object set(int index, Object obj) { boundCheck(index); return arrList[index] = obj; } public void boundCheck(int index) { if (index < 0 || index >= size) { try { throw new Exception(); } catch (Exception e) { e.printStackTrace(); } } } public void add(int index, Object obj) { if (index == size) { add(obj); } else { boundCheck(index); arrList[index] = obj; Object transfer = arrList[size - 1]; System.arraycopy(arrList, index, arrList, index + 1, size); add(transfer); size++; } } public static void main(String[] args) { MyArraylist ml = new MyArraylist(); ml.add("aaa"); ml.add("bbb"); ml.add("ccc"); ml.set(1, "ddd"); System.out.println(ml.size()); System.out.println(ml.get(0)); } }
vector 向量 ,线程安全 ,基于数组 ,可重复
LinkedList 基于链表,查询与更改效率低,增删效率高 ,可重复。
自定义LinkedList 集合
class Node { //不被垃圾回收器回收的原因是因为垃圾回收的算法是基于根搜索算法 private Node previse; private Object obj; private Node next; public Node() { super(); } public Node(Node previse, Object obj, Node next) { super(); this.previse = previse; this.obj = obj; this.next = next; } public Node getPrevise() { return previse; } public void setPrevise(Node previse) { this.previse = previse; } 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; } } public class MyLinkelist { private Node first; private Node last; private int size; public void add(Object obj) { if (first == null) { Node n = new Node(); n.setPrevise(null); n.setObj(obj); n.setNext(null); first = n; last = n; } else { Node n = new Node(); n.setPrevise(last); n.setObj(obj); n.setNext(null); last.setNext(n); last = n; } size++; } public int size() { return size; } public Object get(int index) { rangeCheck(index); Node temp = first; for (int i = 0; i < index; i++) { temp = temp.getNext(); } return temp.getObj(); } public void remove(int index) { rangeCheck(index); Node temp = first; for (int i = 0; i < index; i++) { temp = temp.getNext(); } Node pre = temp.getPrevise(); Node nt = temp.getNext(); nt.setPrevise(pre); pre.setNext(nt); size--; } public void rangeCheck(int index) { if (index < 0 || index >= size) { try { throw new Exception(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public static void main(String[] args) { MyLinkelist ml = new MyLinkelist(); ml.add("aaa"); ml.add("bbb"); ml.add("ccc"); ml.remove(1); System.out.println(ml.size()); System.out.println(ml.get(0)); } }
3.Set
HashSet 不可重复,效率高,重写了hashCode方法,通通过哈希值计算下标位置,下标相同放置在同一个区间采用链表及红黑树(二叉树的一种)结构,基于数组加链表加红黑树,基于HashMap
自定义HashSet 集合(略)
TreeSet 实现Comparalbe接口重写compareTo,不可重复,放入后改变值不会影响顺序(自身并没有修改的操作,需要通过修改变量来间接的修改),放入元素时排序的,利用的是比较器的值是否为0来去重,大于小于0来排序
LinkedHashSet 不可重复
自定义
4.Map
HashMap 键不可重复,效率高,重写了hashCode方法,,通过哈希值计算下标位置,下标相同放置在同一个区间采用链表及红黑树(二叉树的一种)结构,基于数组加链表加红黑树,键可以一个为null,值可以多个为null
自定义HashMap(只是简单的实现,没有重写hashcode和equals方法,没有考虑各个区间的均衡分布)
public class MyHashMap<K, V> { LinkedList<Node<K, V>>[] objs; int n; public MyHashMap() { this.n = 10; this.objs = new LinkedList[n]; } public void put(K k, V v) { int mod = k.hashCode() % n; if (objs[mod] == null) { objs[mod] = new LinkedList<>(); } int flag = 1; for (int i = 0; i < objs[mod].size(); i++) { if (objs[mod].get(i).getKey().equals(k)) { objs[mod].get(i).setValue(v); flag = 0; break; } } if (flag == 1) { objs[mod].add(new Node<K, V>(k, v)); } } public V get(K k) { int mod = k.hashCode() % n; if (objs[mod] == null) { System.out.println(1); return null; } for (int i = 0; i < objs[mod].size(); i++) { if (objs[mod].get(i).getKey().equals(k)) { return objs[mod].get(i).getValue(); } } return null; } public static void main(String[] args) {
//自动拆包装以及Number和String自身已经重写了hashcode和equals方法 MyHashMap<String, String> myHashMap = new MyHashMap<String, String>(); myHashMap.put("a", "A"); myHashMap.put("a", "AA"); myHashMap.put("b", "B"); myHashMap.put("b", "Bb"); System.out.println(myHashMap.get("a")); System.out.println(myHashMap.get("b")); MyHashMap<Object, Object> myHashMap1 = new MyHashMap<>(); myHashMap1.put(1, 11); myHashMap1.put(1, 111); System.out.println(myHashMap1.get(1)); } static class Node<K, V> { K key; V value; public K getKey() { return key; } public V getValue() { return value; } public void setValue(V value) { this.value = value; } public Node(K key, V value) { super(); this.key = key; this.value = value; } } }
重写hash码(内存泄漏问题)
public class HashCodeDivulge { private int x; private int y; public void setX(int x) { this.x = x; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + x; result = prime * result + y; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; HashCodeDivulge other = (HashCodeDivulge) obj; if (x != other.x) return false; if (y != other.y) return false; return true; } public static void main(String[] args) { HashCodeDivulge hashCodeDivulge1 = new HashCodeDivulge(); HashCodeDivulge hashCodeDivulge2 = new HashCodeDivulge(); System.out.println(hashCodeDivulge1.hashCode()==hashCodeDivulge2.hashCode());//true hashCodeDivulge2.setX(10); System.out.println(hashCodeDivulge1.hashCode()==hashCodeDivulge2.hashCode());//false //HashMap基于哈希值 HashMap<? super HashCodeDivulge,Object> hashMap = new HashMap<>(); hashMap.put(hashCodeDivulge1, "AA"); hashMap.put(hashCodeDivulge2, "BB"); System.out.println(hashMap.size()); //改变哈希值导致没移除掉 hashCodeDivulge2.setX(11); hashMap.remove(hashCodeDivulge2); System.out.println(hashMap.size()); } }
HashTable 键和值都不能为null 线程安全的
LinkedHashMap 键不可重复,顺序基于插入的顺序,基于链表
Treemap:键可以排序,原理同TreeSet
5.队列
PriorityQueue 优先级队列,实现了Comparalbe接口
Queue 单向队列
略
Deque 双向队列
略
6.迭代器(iterator和Enumeration和ListIterator)
Iterator是Enumeration的升级版;
vector和hashtable用的迭代器是Enumeration;
hashmap,arraylist等的迭代器用是Iterator。
ListIterator是list集合特有的迭代器,可以在遍历的过程中对对元素的增、删、改、查的动作,而其他两个集合自身在迭代的过程中自身不能这么做
@Test public void forTest(){ ArrayList<Integer> arrayList = new ArrayList<>(); arrayList.add(1); arrayList.add(2); arrayList.add(3); arrayList.add(4); for (int i = 0; i <arrayList.size(); i++) { System.out.println(arrayList.get(i)); if(arrayList.get(i)==1){ arrayList.add(5); arrayList.remove(i); } } System.out.println(arrayList); } @Test public void foreachTest(){ ArrayList<Integer> arrayList = new ArrayList<>(); arrayList.add(1); arrayList.add(2); arrayList.add(3); arrayList.add(4); //可以使用set方法,而不能增删 //实现foreach需要实现Iterator接口 for (Integer integer : arrayList) { // arrayList.add(5);//运行时错误 arrayList.set(1, 5);//运行时正常 } System.out.println(arrayList); } @Test public void iteratorTest(){ ArrayList<Integer> arrayList = new ArrayList<>(); arrayList.add(1); arrayList.add(2); arrayList.add(3); arrayList.add(4); Iterator<Integer> iterator = arrayList.iterator(); while(iterator.hasNext()){ Integer next = iterator.next(); if(next==1){ iterator.remove();//运行时正常 } } System.out.println(arrayList); } }
原因:逻辑上讲,迭代时可以添加元素,但是一旦开放这个功能,很有可能造成很多意想不到的情况。 比如你在迭代一个ArrayList,迭代器的工作方式是依次返回给你第0个元素,第1个元素,等等,假设当你迭代到第5个元素的时候,你突然在ArrayList的头部插入了一个元素,使得你所有的元素都往后移动,于是你当前访问的第5个元素就会被重复访问。 java认为在迭代过程中,容器应当保持不变。因此,java容器中通常保留了一个域称为modCount,每次你对容器修改,这个值就会加1。当你调用iterator方法时,返回的迭代器会记住当前的modCount,随后迭代过程中会检查这个值,一旦发现这个值发生变化,就说明你对容器做了修改,就会抛异常。
7.Properties
public class PopertiesDemo1 { public static void main(String[] args) { try {
//getResourceAsStream需要看编译后的bin目录中文件位置,但是src与bin目录有一定的联系可以通过相应的设置,src的java文件会解析到相应的bin目录成class文件,其他的复制
//以下PopertiesDemo1.class.getResourceAsStream是建立在PopertiesDemo1.class.getClassLoader() .getResourceAsStream基础之上的 // 方式1:默认相对位置在工程目录下,src前面不能添加/ FileInputStream fileInputStream = new FileInputStream("src/cn/collection/demo/config.properties"); // 方式2:可以写相对位置默认在PopertiesDemo1.class相同的包下,PopertiesDemo1.class.getResourceAsStream是以PopertiesDemo1.class类所在的包为基准 InputStream resourceAsStream = PopertiesDemo1.class.getResourceAsStream("config.properties"); // 对方式2进行说明cn.collection.demo1.TestHashCode同包下,..上一级目录,是以Class.forName("cn.collection.demo1.TestHashCode")为基准 InputStream resourceAsStream2 = Class.forName("cn.collection.demo1.TestHashCode") .getResourceAsStream("../../../cn/collection/demo/config.properties"); // 工程目录可以用/表示编译后的bin目录 InputStream resourceAsStream3 = Class.forName("cn.collection.demo1.TestHashCode") .getResourceAsStream("/cn/collection/demo/config.properties"); // 方式3 默认在src下面,不能添加/,是以PopertiesDemo1.class.getClassLoader()的编译后的bin为基准 InputStream resourceAsStream4 = PopertiesDemo1.class.getClassLoader() .getResourceAsStream("cn/collection/demo/config.properties"); Properties properties = new Properties(); properties.load(resourceAsStream4); fileInputStream.close(); resourceAsStream.close(); resourceAsStream2.close(); resourceAsStream3.close(); resourceAsStream4.close(); System.out.println(properties.getProperty("name")); properties.setProperty("name", "李四"); System.out.println(properties.getProperty("name")); // 写入config1文件,在后面添加的模式 FileWriter fileWriter = new FileWriter("config1", true); // "aa" 描述信息 properties.store(fileWriter, "aa"); fileWriter.close(); } catch (Exception e) { System.out.println("没有找到文件"); } } }
8.其他
1)Weakhashmap
假如键为弱引用,垃圾回收机制下会被回收,用法和hashmap差不多。
2)IdentityHashMap
根据地址相同去重,用法和hashmap差不多。
3)Enummap
键必须是枚举值,用法和hashmap差不多。
4)Collections工具类常用方法
Sort()按照自然顺序排序
sort(List<T> list, Comparator<? super T> c)根据指定比较强排序
synchronized 保证线程安全
Empty 空容器可以避免空指针异常
Singleton 只包含一个元素的容器
Unmodifiable 不可变的容器
reverse(List<?> list) 反转顺序