JAVA容器,爱并恨着。
对于校招生,容器部分以下应该够你用了。加油吧,骚年。
对于JAVA容器简单分类如下图:
红色:表示接口
容器顾名思义就是为了装东西JAVA而提供的一个解决方法。
说到存储,很多人会想到数组。
选择容器而不是数组的原因:
数组:
长度必须在初始化时指定,且固定不变
数组缺乏封装,操作繁琐
数组无法直接保存映射关系
数组采用连续存储空间,删除和添加效率低下
整体集合架构
Collection 接口存储一组不唯一,无序的对象
List 接口存储一组不唯一,有序(索引顺序)的对象
Set 接口存储一组唯一,无序的对象
Map接口存储一组键值对象,提供key到value的映射 (其中key唯一无 序,value不唯一,无序。)
List(ArrayList,LinkedList)
List 特点:有序 不唯一(可重复
ArrayList:线性表中的顺序表
特点:在内存中分配了连续空间,实现了长度可变的数组。
优点:遍历元素和随机访问元素的效率较高
缺点:添加和删除需大量移动元素效率低,按照内容查询效率低
手写ArrayList
package com.lidadaibiao.ArrayList;
import javax.management.RuntimeErrorException;
import org.w3c.dom.ranges.RangeException;
/**
*
* @author 李大代表
*
*/
public class DongArrayList5 {
public static void main(String[] args) {
ArrayList5<String> arr = new ArrayList5<String>(20);
//System.out.println("测试数组扩容---------------");
/*for(int i = 0;i<30;i++){
arr.add("代表");
}*/
for(int i = 0;i<30;i++){
arr.add(""+i);
}
System.out.println("数组删除");
arr.remove(2);
arr.remove("7");
System.out.println(arr.toString());
/* System.out.println(arr.toString());
System.out.println("-----get/set/索引越界问题------");
arr.set("兵长",10);
System.out.println(arr.toString());
String string = arr.get(10);
System.out.println(string);
arr.set("dd",50);*/
/*arrayList1.add("aa");
arrayList1.add("aa1");
String object = arrayList1.get(0);
System.out.println(object);
String object1 = arrayList1.get(1);
String object2 = arrayList1.get(2);
String object3 = arrayList1.get(3);
System.out.println(object1);
System.out.println(object2);
System.out.println(object3);
System.out.println("----------");
System.out.println(arrayList1.toString());*/
}
}
class ArrayList5<E>{
private Object[] elementData;
private int size;
private static final int DEFALT_CAPACITY=10;
public ArrayList5(){
elementData = new Object[DEFALT_CAPACITY];
}
public ArrayList5(int size){
if(size<0){
throw new RuntimeException("容器容量范围>=0");
}else if(size == 0){
elementData = new Object[DEFALT_CAPACITY];
}else{
elementData = new Object[size];
}
}
public void checkRange(int index){
//判断索引是否合法
if(index<0||index>=size){
throw new RuntimeException("索引不合法:"+index);
}
}
public void add(E e){
//开始扩容 判断是否到达最大值
if(size == elementData.length){
//如果到达容器最大值则扩容
//新建一个数组
//elementData.length+(elementData.length>>1)括号的原因是因为+号优先级比>>大
Object[] newelementData = new Object[elementData.length+(elementData.length>>1)];
//将老数组中的元素拷贝进入新数组中
System.arraycopy(elementData, 0,newelementData,0,elementData.length);
//将新数组的内存地址赋给老数组
elementData = newelementData;
}
elementData[size++] = e;
}
public E get(int size){
checkRange(size);
return (E)elementData[size];
}
public void set(E e,int index){
checkRange(index);
elementData[index] = e;
}
//按照元素进行删除元素 理论上都是移除,因为内存中并不会删除该元素
public void remove(E e){
//循环查找需要移除的元素
for(int i = 0 ; i<size;i++){
if(e.equals(get(i))){
remove(i);
}
}
}
//按照索引进行删除元素
public void remove(int index){
int num = elementData.length-index-1;
//判断的原因是防止删除的是最后一个
if(num>0){
System.arraycopy(elementData, index+1, elementData, index,num);
}
elementData[--size]=null;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("{");
for(int i = 0;i<size;i++){
sb.append(elementData[i]+",");
}
sb.setCharAt(sb.length()-1,'}');
return sb.toString();
}
}
LinkedList 线性表中双向链表
特点: 采用双向链表存储方式
优点:插入、删除元素效率比较高(但是前提也是必须先低效率查询才可。如果插入删除发 生在头尾可以减少查询次数)
缺点:遍历和随机访问元素效率低下。
手写LinkedList
package com.lidadaibiao.LinkedList;
/**
* 李大代表
* @author ASUS
*
*
*/
public class DongLinkedList2 <E>{
private int size;//定义大小
private Node frist;//定义首节点
private Node last;//定义终结点
public void checkRange(int index){
if(index<0 || index>size){
throw new RuntimeException("索引不合法:"+index);
}
}
public void add(int index,E obj){
Node newNode = new Node(obj);
Node temp = getNode(index);
if(temp!=null){
Node up = temp.privious;
up.next = newNode;
newNode.privious = up;
newNode.next = temp;
temp.privious = newNode;
}
}
//根据索引删除一个值
public void remove(int index){
Node temp = getNode(index);
//先判断是否为空
if(temp!=null){
Node up = temp.privious;
Node down = temp.next;
if(up!=null)
{
up.next = down;
}
if(down!=null)
{
down.privious = up;
}
if(index==0){
frist = down;
}
if(index==size-1){
//last = up;
last = null;
}
size--;
}
}
//根据索引get一个值
public E get(int index){
return (E)getNode(index).element;
}
//得到一个node
private Node getNode(int index){
Node temp ;
checkRange(index);
if(index<(size>>1)){//>>1 相当于除以2
temp = frist;
for(int i = 0;i<index;i++){//小于一半 向后找
temp =temp.next;
}
}
else{
temp = last;
for(int i = size;i>index;i--){//大于一半 从后向前找
temp = temp.privious;
}
}
return temp;
}
//打印
public String toString(){
Node temp = frist;
StringBuilder sb = new StringBuilder();
sb.append("{");
while(temp!=null){
sb.append(temp.element+",");
temp = temp.next;
}
sb.setCharAt(sb.length()-1,'}');
return sb.toString();
}
public void add(Object obj){
Node node = new Node(obj);//创建一个节点 并赋值
//判断是否为首节点
if(frist==null){
frist = node;
last = node;
}
else{
node.privious=last;
node.next = null;
last.next = node;
last = node;
}
size++;
}
public static void main(String[] args) {
DongLinkedList2 dongLinkedList = new DongLinkedList2();
dongLinkedList.add("1");
dongLinkedList.add("2");
dongLinkedList.add("3");
dongLinkedList.add("4");
dongLinkedList.add("5");
System.out.println(dongLinkedList.toString());
/*System.out.println(dongLinkedList.get(0));
dongLinkedList.remove(5);
dongLinkedList.remove(0);
System.out.println(dongLinkedList.toString());*/
dongLinkedList.add(2,"22");
System.out.println(dongLinkedList.toString());
}
}
class Node {
public Node privious;
public Node next;
public Object element;
public Node() {
super();
// TODO Auto-generated constructor stub
}
public Node(Node privious, Node next, Object element) {
super();
this.privious = privious;
this.next = next;
this.element = element;
}
public Node(Object element) {
super();
this.element = element;
}
public Node getPrivious() {
return privious;
}
public void setPrivious(Node privious) {
this.privious = privious;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
public Object getElement() {
return element;
}
public void setElement(Object element) {
this.element = element;
}
}
Set(HashSet,TreeSet)
特点:无序并唯一
HashSet
特点:采用哈希表存储结构
优点: 添加,删除,查询速度快。
缺点:无序。
手写HashSet
package com.lidadaibiao.hashSet;
import java.util.HashMap;
import java.util.Map;
/**
* 底层手写HashSet
* @author ASUS
* 无顺序 不重复
*
*/
public class DoHashSet {
public Map map;
private static final Object PRESENT = new Object();
public DoHashSet(){
map = new HashMap();
}
public int size(){
return map.size();
}
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append("{");
for (Object v : map.keySet()) {
sb.append(v+",");
}
sb.setCharAt(sb.length()-1, '}');
return sb.toString();
}
public void add(Object o){
map.put(o, PRESENT);
}
public static void main(String[] args) {
DoHashSet dset = new DoHashSet();
dset.add("111");
dset.add("222");
dset.add("333");
dset.add("444");
dset.add("555");
dset.add("111");
System.out.println(dset.toString());
}
}
TreeSet
特点:采用二叉树(红黑树)的存储结构
优点:有序 (内容)查询速度比List快
缺点:查询速度慢于HashSet
LinkedHashSet
特点如其名:采用哈希表存储结构,用链表维护次序,有序。
Map(HashMap,LinkedHashMap,TreeMap
特点:采用键值对映射。
HashMap
Key无序 唯一 采用 Set方式进行存储。
Value无序 不唯一 采用Collection方式进行存储。
手写HashMap
package com.lidadaibiao.hashMap;
/**
* 自定义一个hashmap
* 实现了put 方法增加键值对 并解决了键重复的时候覆盖相应的节点
* 实现tostring 方法
* 实现Get方法
* 完善封装 增加泛型
* @author ASUS
*
*/
public class DoHashMap4<K,V> {
public static void main(String[] args) {
DoHashMap4<Integer,String> doHashMap = new DoHashMap4();
doHashMap.put(10, "22");
doHashMap.put(30, "33");
doHashMap.put(40, "11");
System.out.println(doHashMap.get(10));
}
Node2[] table;//位桶数组 bucket array
int size;//存放的键值个数
public DoHashMap4(){
table = new Node2[16];//长度为2的整数幂
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("{");
for(int i = 0;i<table.length;i++){
Node2 temp = table[i];
while(temp!=null){
sb.append("["+temp.key+","+temp.value+"]"+",");
temp = temp.next;
}
}
sb.setCharAt(sb.length()-1,'}');
return sb.toString();
}
public V get(K key){
int hash = myHash(key.hashCode(),table.length);
V value = null;
if(table[hash]!=null){
Node2 temp = table[hash];
while(temp!=null){
if(temp.key.equals(key))
{
value = (V)temp.value;
break;
}
else{
temp = temp.next;
}
}
}
return value;
}
public void put(K key,V value){
//定义一个新的节点
Node2 newNode2 = new Node2();
newNode2.hash = myHash(key.hashCode(),table.length);
newNode2.key = key;
newNode2.value = value;
newNode2.next = null;
Node2 temp = table[newNode2.hash];
Node2 itrLast =null;//正在遍历的最后一个元素
boolean flag = false;//是否发生重复的标志
if(temp==null){
//此处数组为空则直接将新节点放进去
table[newNode2.hash] = newNode2;
size++;
}
else{
//此处数组不为空,则遍历链表
while(temp!=null){
if(temp.key.equals(key)){
//如果存在重复
flag = true;
temp.value = value;
break;
}
else{
//如果不重复 遍历下一个
itrLast = temp;
temp = temp.next;
}
}
if(!flag){
itrLast.next = newNode2 ;
size++;
}
}
}
private int myHash(int hashCode, int length) {
// TODO Auto-generated method stub
/* System.out.println("位运算"+(hashCode&(length-1)));
System.out.println("模运算"+(hashCode%(length-1)));*/
return hashCode&(length-1);
}
}
class Node2<K,V> {
int hash;
Node2 next;
K key;
V value;
}
LinkedHashMap
特点;链表+哈希表,有序,速度快。
TreeMap
特点:红黑树,有序。
List Set Map 遍历方式
(Iterator迭代器的使用)
package com.lidadaibiao.list;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class IteratorTest {
public static void main(String[] args) {
System.out.println("list set map 遍历方式");
IteratorTestList();
System.out.println("-------------");
IteratorTestSet();
System.out.println("-------------");
IteratorTestMap();
}
public static void IteratorTestList(){
List<String> list = new ArrayList<String>();
list.add("11");
list.add("12");
list.add("13");
list.add("14");
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
String next = iterator.next();
System.out.println(next);
}
}
public static void IteratorTestSet(){
Set<String> set = new HashSet<>();
set.add("22");
set.add("23");
set.add("24");
set.add("25");
set.add("22");
Iterator<String> iterator = set.iterator();
while(iterator.hasNext()){
String next = iterator.next();
System.out.println(next);
}
}
public static void IteratorTestMap(){
Map<Integer,String> map = new HashMap<Integer, String>();
map.put(1, "111");
map.put(2, "22");
map.put(3, "333");
//方式1
Set<Entry<Integer, String>> entrySet = map.entrySet();
Iterator<Entry<Integer, String>> iterator = entrySet.iterator();
while(iterator.hasNext()){
Entry<Integer, String> next = iterator.next();
System.out.println(next.getKey()+","+next.getValue());
}
//方式二
Set<Integer> keySet = map.keySet();
Iterator<Integer> iterator2 = keySet.iterator();
while(iterator2.hasNext()){
Integer key = iterator2.next();
System.out.println(key+","+map.get(key));
}
}
}
几种容器比较
Vector(底层使用synchronized代码块锁 ) 和ArrayList
原理,功能,数据存储相同很多情况下可以互用。
不同:Vector线程安全,效率低下;ArrayList重速度轻安全,线程非安全
Hashtable(底层使用synchronized代码块锁 )和HashMap
原理,功能,底层都是哈希表结构,查询速度快,很多情况下可以互用。
不同:Hashtable继承Dictionary类,HashMap实现Map接口
Hashtable线程安全,HashMap线程非安全
Hashtable不允许null值,HashMap允许null值