容器

冷暖自知 提交于 2019-11-27 22:07:55

容器


  容器与数组的概念比较类似,数组用于存储多个数据,特点有定长,长度一旦确定不可改变;数据类型要一致;数组是有序的,有索引。

  自定义容器类:只能存储字符串类型的数据,容器的大小可以随着数据的多少动态的改变。

public class AppDemo01 {
	public static void main(String[] args) {
		Container c=new Container();
		//添加功能
		c.add("蜡笔小新");
		c.add("柯南");
		c.add("海贼王");
		System.out.println(c.size());
		//获取方法
		System.out.println(c.get(0));;
		System.out.println(c.get(1));;
		System.out.println(c.get(2));;
		System.out.println("-------------");
		//删除
		c.romove(1);
		System.out.println(c.get(0));;
		System.out.println(c.get(1));;
		System.out.println(c.size());
	}
}	

//自定义容器类
class Container{
	//容器中存储数据的数组  永远指向最新的当前的数据对象
	private String[] arr;
	//容器中数据的个数
	private int size;
	
	public Container() {
		arr=new String[0];
	}

	/**
	 * 删除数据方法
	 * @param i : 索引
	 */
	public void romove(int index) {
		if(index<0 || index>=size){
			throw new ArrayIndexOutOfBoundsException(index+"索引越界啦");
		}
		//备份原数组
		String[] temp=arr;
		arr=new String[size-1];
		for(int i=0;i<size;i++){
			if(i>=index){
				if(i==index){
					continue;
				}else if(i>index){
					arr[i-1]=temp[i];
				}
			}else{
				arr[i]=temp[i];
			}
		}
		size--;
	}
	//判断索引
	public String get(int index) {
		if(index<0 || index>=size){
			throw new ArrayIndexOutOfBoundsException(index+"索引越界啦");
		}
		return arr[index];
	}

	/*
	 * 添加方法
	 */
	public void add(String value) {
		//备份原数组
		String[] temp=arr;
		arr=new String[size+1];
		//数组的拷贝
		for(int i=0;i<size;i++){
			arr[i]=temp[i];
		}
		arr[size]=value;
		size++;
	}
	
	//长度
	public int size(){
		return this.size;
	};
}

一、Collection接口

  Collection容器体系的父接口,定义容器的一些规则。

  根据动态根据数据的大小改变容量

  只能存储引用类型的数据,如果要存储基本数据类型会进行自动装箱

  可以存储任意类型的数据

public class CollectionDemo01 {
	public static void main(String[] args) {
		//容器对象
		Collection con=new ArrayList();
		Collection con2=new ArrayList();
		Collection con3=new ArrayList();
		//能够调用的必须是Collection接口中方法
		
		//1.add
		con.add(1);
		con.add("啊哈哈");
		con.add(false);
		
		con2.add('a');
		con2.add('b');
		con3.add('a');
		con3.add('b');
		
		System.out.println(con);
		//addAll()
		con.addAll(con2);
		System.out.println(con);
		
		//2.clear() 清除全部
		//con.clear();
		
		//boolean remove(Object o)  
		con.remove(false);
		System.out.println(con);
		
		//3.contains() 判断是否包含
		System.out.println(con.contains("啊哈哈"));
		
		//4.equals() 
		System.out.println(con2.equals(con3));
		
		//5.求交集
		con.retainAll(con2);
		System.out.println(con);
		System.out.println(con.size());
		
		//6.toArray
		System.out.println(Arrays.toString(con.toArray()));
	}
}

  容器的遍历,可以使用增强for和迭代器遍历。

public class CollectionDemo02 {
	public static void main(String[] args) {
		//容器对象
		//泛型  : 增强容器的稳定性和可读性
		//规范容器中所有数据的数据类型
		Collection<String> con=new ArrayList();
		con.add("哈哈");
		con.add("呵呵");
		con.add("嘿嘿");
		//con.add(1);
		
		//1.增强for
		for(String o:con){
			System.out.println(o);
		}
		
		//2.迭代器  Iterator<E> iterator()  
		//1)获取迭代器对象 
		Iterator<String> it=con.iterator();
		//2)循环判断是否存在下一个元素
		while(it.hasNext()){
			//3)如果存在,就获取
			System.out.println(it.next());
		}		
	}
}

Set接口

  Set接口实现了Collection接口,无序且不可重复,没有新增方法,完全使用Collection中的方法,遍历方式:增强for和迭代器。

public class SetDemo01 {
	public static void main(String[] args) {
		//无序: 添加顺序和内部真实存储的顺序不一定相同,一旦存储不会改变
		Set<String> set=new HashSet();
		set.add("哈哈");
		set.add("嘻嘻");
		set.add("123");
		set.add("abc");
		set.add("呵呵");
		set.add("啦啦");
		set.add("嘿嘿");
		System.out.println(set);
	}
}
//输出:[123, abc, 哈哈, 嘻嘻, 呵呵, 啦啦, 嘿嘿]

1.TreeSet

底层实现:红黑树(二叉树的一种),是由TreeMap维护

特点:有序的,默认升序排序,查询等效率高,自动去重

新增方法:E ceiling(E e) 返回大于等于参数的元素,没有返回null,还有很多类似的新增方法。

存储自定义的引用数据类型的数据:必须实现内部比较器/外部比较器–>去重+排序

public class TreeSetDemo {
	public static void main(String[] args) {
		TreeSet<Double> tree = new TreeSet();
		tree.add(3.1);
		tree.add(4.5);
		tree.add(1.1);
		tree.add(6.3);
		tree.add(2.9);
		tree.add(5.4);
		System.out.println(tree);
		System.out.println(tree.ceiling(5.5));//输出比输入的值大的数
		
		////如果存储自定义的引用数据类型: 1)默认比较规则 2)必须指定比较规则
		TreeSet<Person> set = new TreeSet();
        //TreeSet<Person> set=new TreeSet((o1,o2)-> ((Person)o2).getName()-((Person)o1).getAge());指定比较规则
		set.add(new Person("张三",10));
		set.add(new Person("李四",12));
		set.add(new Person("李四",13));
		set.add(new Person("李四",14));
		set.add(new Person("王五",11));
		System.out.println(set);
		//当外部比较器是使用年龄排序的结果:[Person [name=张三, age=10], Person [name=王五, age=11], Person [name=李四, age=12], Person [name=李四, age=13], Person [name=李四, age=14]]
	}
}

//也可以重写Person类中的compareTO()方法
@Override
public int compareTo(Object o) {
    //使用名字比较,有自带的比较规则
	return this.name.compareTo(o.name);
}


2.HashSet

HashSet:无序 去重

底层实现:有哈希表(数组+链表+红黑树)结构实现的,内部是由HashMap维护

优点:查询,增加,删除效率高

缺点:无序,去重

Hash算法:

  1.使用算法对数据进行计算,决定其存储在数组中的位置,位置=数据%9,在程序中就使用hashCode()方法来计算值。

  2.找到对应的位置存储数据,位置中的数据默认链表的结构连接,先调用equals()方法判断位置中是否已经存在相同的数据,如果存在就去重,如果不存在就存放;如果位置中的数据大于8个,那么就转为红黑树的结构存储数据。

  3.相同的数据,hashCode()结果一定相同;hashCode()结果相同的,数据不一定相同。

自定义类型对象去重:(所有成员属性的值都相同的对象,就是一个对象,应该去重)

重写hashCode()和equals()方法

如果hashcode()值不相同,不会调用equals()方法进一步比较内容

public class HashSetDemo03 {
	public static void main(String[] args) {
		HashSet<Person> set=new HashSet();
		set.add(new Person("张三",10));
		set.add(new Person("李四",12));
		set.add(new Person("赵六",13));
		set.add(new Person("赵六",13));
		set.add(new Person("赵六",13));
		set.add(new Person("王五",11));
		System.out.println(set);
        //去重输出:[Person [name=王五, age=11], Person [name=张三, age=10], Person [name=赵六, age=13], Person [name=李四, age=12]]
	}
}

//重写hashCode()和equals()方法
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Person other = (Person) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}

二、List接口

  List接口是有序可重复的,新增了一些根据索引操作的功能

public class ListDemo01 {
	public static void main(String[] args) {
        //使用引用数据类型
		List<Integer> list=new ArrayList();
		list.add(3);
		list.add(0);
		list.add(1);
		list.add(2);
		list.add(1);
		//指定索引位置添加数据
		list.add(2, 4);
		//get(index)
		System.out.println(list.get(1));
		System.out.println(list);
		//indexOf   lastIndexOf
		System.out.println(list.indexOf(1));
		System.out.println(list.lastIndexOf(1));
		
		//remove(index)
		System.out.println(list.remove(3));  //如果出现数据和索引类型相同情况,优先决定是索引
		System.out.println(list);
		//set(int index, E element) 
		System.out.println(list.set(3, 333));
		System.out.println(list);
		//subList 结束索引获取不到
		System.out.println(list.subList(1, 3));
	}
}

  List接口的遍历方式可以使用增强for、迭代器和普通for。

public class ListDemo02 {
	public static void main(String[] args) {
		List<Integer> list=new ArrayList();
		list.add(3);
		list.add(0);
		list.add(1);
		list.add(2);
		list.add(1);
	
		//普通for遍历
		for(int i=0;i<list.size();i++){
			System.out.println(list.get(i));
		}
	
	}
}

  List接口下所有的实现类都是有序的可重复的


1.ArrayList

  该类的底层实现是用数组/可变数组实现的,在内存中一块连续的内存空间。

  优点:根据索引遍历,获取效率高

  缺点:添加、删除操作需要大量的拷贝数据,效率低

  扩容机制:根据空构造器创建对象,默认构建空数组,当第一次添加以后才会扩容,初始容量是10,扩容机制每次扩容的是原容量的1.5倍(int newCapacity = oldCapacity + (oldCapacity >> 1);使用了copyOf方法进行动态扩容

  推荐使用的情况:大量做查询多推荐使用,做增删多不推荐使用

//创建一个存储字符的ArrayList对象
ArrayList<Character> list=new ArrayList();
/*
 * ArrayList(Collection<? extends E> c) 构造一个包含指定 collection 的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的。
 * ArrayList(int initialCapacity) 构造一个具有指定初始容量的空列表。
 */

//添加数据到列表的尾部
list.add('a');
System.out.println(list.size());
//使用ArrayList存储对象类型数据,并使用常用方法测试
//创建一个存储引用类型的ArrayList对象
ArrayList<Person> persons=new ArrayList();
//添加信息
persons.add(new Person("金珍",26));
persons.add(new Person("田国",23));
persons.add(new Person("闵其",25));
//输出列表信息
System.out.println(persons);
//查找该信息第一次出现的索引位置		
System.out.println(persons.indexOf(new Person("闵其",25)));  //默认调用equals()方法比较,需要在Person类中重写
//重写equals()方法
@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Person other = (Person) obj;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}

2.Vector

  Vector:向量

  Vector和ArrayList非常像

  Vector和ArrayList的区别:

   1.Vector线程安全,ArrayList线程不安全

   2.每次扩容原容量的2倍,ArrayList是1.5倍,更有效的节省内存


3.LinkedList

  LinkedList的底层实现是使用双向链表的。

  优点:做增删效率高

  缺点:根据索引获取或遍历效率低(本身链表结构不存在索引,模拟索引使用)

  使用推荐:大量做增删推荐使用LinkedList,大量做查询推荐使用ArrayList

  新增方法:添加了一写操作与链表头尾的方法

  自定义LinkedList:使用简单的单向链表实现

  链表结构中的每一个数据元素都是以字节为单位的,单项链表的节点中存储当前数据和下一个节点的地址。

public class LinkListDemo02 {
	public static void main(String[] args) {
		MyLinkedList my=new MyLinkedList();
		my.add("haha1");
		my.add("haha2");
		my.add("haha3");
		System.out.println(my.size());
		System.out.println(my.get(0));
		System.out.println(my.get(1));
		System.out.println(my.get(2));
	}
}

//自定义的MyLinkedList容器类
class MyLinkedList{
	//链表头节点 
	private Node head=null; //链表头的不能随意改变
	//链表的长度
	private int size;
	
	public MyLinkedList() {
		// TODO Auto-generated constructor stub
	}

	/*
	 * 获取功能
	 */
	public String get(int index) {
		//备份一个链表头数据,便于操作查找节点使用
		Node temp=head;  //永远指向当前节点,从链表头节点开始
		for(int i=0;i<size;i++){
			if(i==index){
				return temp.getData();
			}
			temp=temp.getNext();  //指向下一个节点
		}
		return null;
	}

	//添加
	public void add(String value) {
		//1.构建新节点,作为链表尾部
		Node newNode=new Node(value,null);
		
		//2.判断是否已存在链表头节点,如果不存在,新节点作为链表头,如果存在
		if(head == null){
			 head=newNode;
			 size++;
			 return;
		}
		//找到原链表的链表尾部
		//备份一个链表头数据,便于操作查找节点使用
		Node temp=head;
		while(temp.getNext()!=null){
			temp=temp.getNext();
		}
		//3.让原链表的链表尾指向新节点
		temp.setNext(newNode);
		//4.长度+1
		size++;
	}
	
	public int size(){
		return this.size;
	}
	
}


//节点类
class Node{
	private String data;
	private Node next;  //存储下一个对象的地址,代表下一个节点对象
	
	public Node() {
		// TODO Auto-generated constructor stub
	}

	public Node(String data, Node next) {
		super();
		this.data = data;
		this.next = next;
	}

	public String getData() {
		return data;
	}

	public void setData(String data) {
		this.data = data;
	}

	public Node getNext() {
		return next;
	}

	public void setNext(Node next) {
		this.next = next;
	}

	@Override
	public String toString() {
		return "Node [data=" + data + ", next=" + next + "]";
	}
	
}

三、比较器

自然排序/内部比较器

  类实现java.lang.Comparable 接口,重写compareTo方法,自定义比较规则

  不够符合开闭原则,不灵活,可能频繁修改源代码

public class Demo01 {
	public static void main(String[] args) {
		Person[] arr=new Person[3];
		arr[0]=new Person("张三",10);
		arr[1]=new Person("李四",12);
		arr[2]=new Person("王五",11);
		System.out.println(Arrays.toString(arr));
		//排序前:[Person [name=张三, age=10], Person [name=李四, age=12], Person [name=王五, age=11]]
		Arrays.sort(arr);//默认调用内部比较器,默认升序排序
		System.out.println(Arrays.toString(arr));
        //排序后:[Person [name=张三, age=10], Person [name=王五, age=11], Person [name=李四, age=12]]
	}
}

//重写person类中的compare()方法,自定义比较规则
@Override
	public int compareTo(Object o) {
		//o是object类型,无法使用person类型的,所以需要强转
        //想改降序排序可以改成:((Person)o).age-this.age
		return this.age-((Person)o).age;
	}

自定义排序/外部比较器

  java.util Comparator<T> 重写compare()方法,自定义比较规则

public class Demo01 {
	public static void main(String[] args) {
		Person[] arr=new Person[3];
		arr[0]=new Person("张三",10);
		arr[1]=new Person("李四",12);
		arr[2]=new Person("王五",11);
			
		Arrays.sort(arr,new Test());
		//排序后:[Person [name=李四, age=12], Person [name=王五, age=11], Person [name=张三, age=10]]
        
        //匿名内部类
		Arrays.sort(arr,new Comparator<Person>(){

			@Override
			public int compare(Person o1, Person o2) {
				return o2.getAge()-o1.getAge();
			}});
        
        //Lambda表达式
		Arrays.sort(arr,(o1,o2)-> o2.getAge()-o1.getAge());
		
		System.out.println(Arrays.toString(arr));
		
		
	}
}

//外部比较器
class Test implements Comparator<Person>{
	@Override
	public int compare(Person o1, Person o2) {
		return o2.getAge()-o1.getAge();//降序
	}
}

四、Map

HashMap

Map:存储键值对的形式的数据 Key-Value

 k 和 v之间存在映射关系,可以通过k找到v

 k 的数据 无序的, 不可重复 -->Set集合

 v 的数据 无序的, 可以重复 -->Collection集合

 一个key只能对应一个value(v中可以存放集合List)

 Map无序,去重根据key决定;如果key相同,value会覆盖

遍历:

 1.获取所有的key Set<K> keySet()

 2.获取所有的值 Collection<V> values()

 3.Set<Map.Entry<K,V>> entrySet()

public class MapDemo01 {
	public static void main(String[] args) {
		Map<Integer,String> map=new HashMap();
		// V put(K key, V value)  将指定的值与此映射中的指定键关联(可选操作)。 
		map.put(01, "哪吒");
		map.put(02, "烈火英雄");
		System.out.println(map.put(03, "上海堡垒"));//返回null
		System.out.println(map.put(03, "上海堡垒2"));//替换原内容,返回原内容“上海堡垒”
		System.out.println(map);
        
		//get(key)方法
		System.out.println(map.get(02));//返回“烈火英雄”
		 /*  boolean containsKey(Object key) 
		          如果此映射包含指定键的映射关系,则返回 true。 
		   boolean containsValue(Object value) 
		 */
		System.out.println(map.containsKey(03));//true
		System.out.println(map.containsValue("上海堡垒"));//false
		
        //遍历
		//1.获取所有的key    Set<K> keySet() 
		Set<Integer> keys=map.keySet();
		for(Integer i:keys){
			System.out.println(i+"---->"+map.get(i));
		}
		
		//2.获取所有的值	     Collection<V> values()  
		Collection<String> col=map.values();
		Iterator<String> it=col.iterator();
		while(it.hasNext()){
			System.out.println(it.next());
		}
		
		//3.Set<Map.Entry<K,V>> entrySet()  
		Set<Map.Entry<Integer,String>> set=map.entrySet();
		for(Map.Entry i:set){
			System.out.println(i.getKey()+"---->"+i.getValue());
		}
	}
}

使用HashMap容器存储数据,要求key为引用数据类型Hero类型的对象

key是对象类型去重:重写hashCode()和equals()

HashMap:认初始容量 (16):可以根据要存储数据设置  默认加载因子 (0.75) :不建议修改

public class MapDemo02 {
	public static void main(String[] args) {
		HashMap<Hero,Integer> map=new HashMap();
		map.put(new Hero("钢铁侠","复仇者联盟"), 5);
		map.put(new Hero("毒液","复仇者联盟"), 2);
		map.put(new Hero("灭霸","复仇者联盟"), 2);
		map.put(new Hero("灭霸","复仇者联盟"), 2);
		System.out.println(map);
        //比较key,改变value就会覆盖
        //去重后输出:{Hero [name=毒液, tv=复仇者联盟]=2, Hero [name=灭霸, tv=复仇者联盟]=2, Hero [name=钢铁侠, tv=复仇者联盟]=5}
	}
}

class Hero{
	private String name;
	private String tv;
	
	public Hero() {
		// TODO Auto-generated constructor stub
	}

	public Hero(String name, String tv) {
		super();
		this.name = name;
		this.tv = tv;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getTv() {
		return tv;
	}

	public void setTv(String tv) {
		this.tv = tv;
	}

	@Override
	public String toString() {
		return "Hero [name=" + name + ", tv=" + tv + "]";
	}
	//重写方法
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		result = prime * result + ((tv == null) ? 0 : tv.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Hero other = (Hero) obj;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		if (tv == null) {
			if (other.tv != null)
				return false;
		} else if (!tv.equals(other.tv))
			return false;
		return true;
	}
}

HashMap线程不安全的问题:
​ 1.Hashtable
​ 2.synchronizedMap(Map<K,V> m) 返回由指定映射支持的同步(线程安全的)映射。
​ 3.java.util.concurrent 类 ConcurrentHashMap<K,V> 线程安全的哈希表 —推荐


TreeMap

底层实现:红黑树的结构默认升序排序

key是自定义引用数据类型时,必须实现内部比较器/外部比较器(在创建TreeMap构造器中指定)

public class TreeMap03 {
	public static void main(String[] args) {
		TreeMap<Character,Double> map=new TreeMap();
		map.put('c', 2.2);
		map.put('b', 2.3);
		map.put('a', 2.2);
		map.put('a', 2.5);
		System.out.println(map);
        //输出:{a=2.5, b=2.3, c=2.2},map会覆盖value值
	}
}

Properties

  properties用于配置文件,继承Map类。

  properties的key和value都是String类型的数据。

使用方法:

1.创建一个资源文件夹,在里面新建File文件,在文件里写代码。

2.在主方法中创建properties对象,加载File文件,可以获取文件中的内容。

//db.properties文件中的内容
name=zhangsan;
pwd=125;

public class PropertiesDemo01 {
	public static void main(String[] args) throws IOException {
		Properties pro=new Properties()//加载
            pro.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties"));
		System.out.println(pro.getProperty("name"));
		System.out.println(pro.getProperty("pwd"));
        /*输出:
        zhangsan
        125
        */
	}
	
}

五、Collections

  Collections和Arrays很像,Arrays是操作数组的工具类,Collections是操作容器的工具类。

  Collection和Collections的区别:

   Collection是容器体系的父接口,Collections是操作容器的工具类。

public class Demo01 {
	public static void main(String[] args) {
		List<Integer> lis=new ArrayList();
		lis.add(5);
		lis.add(2);
		lis.add(4);
		lis.add(3);
		lis.add(1);
		System.out.println(lis);
        
		//void sort(List) //对 List 容器内的元素排序,按照升序进行排序。
		Collections.sort(lis);
		System.out.println(lis);
		
		//void shuffle(List) //对 List 容器内的元素进行随机排列
		Collections.shuffle(lis);
		System.out.println(lis);
		
		//void reverse(List) //对 List 容器内的元素进行逆续排列
		Collections.reverse(lis);
		System.out.println(lis);
		
		//void fill(List, Object) //用一个特定的对象重写整个 List 容器
		Collections.fill(lis, null);
		System.out.println(lis);
		
		//int binarySearch(List, Object)//采用折半查找的方法查找特定对象
		System.out.println(Collections.binarySearch(lis, 6));
	}
}

rays很像,Arrays是操作数组的工具类,Collections是操作容器的工具类。

  Collection和Collections的区别:

   Collection是容器体系的父接口,Collections是操作容器的工具类。

public class Demo01 {
	public static void main(String[] args) {
		List<Integer> lis=new ArrayList();
		lis.add(5);
		lis.add(2);
		lis.add(4);
		lis.add(3);
		lis.add(1);
		System.out.println(lis);
        
		//void sort(List) //对 List 容器内的元素排序,按照升序进行排序。
		Collections.sort(lis);
		System.out.println(lis);
		
		//void shuffle(List) //对 List 容器内的元素进行随机排列
		Collections.shuffle(lis);
		System.out.println(lis);
		
		//void reverse(List) //对 List 容器内的元素进行逆续排列
		Collections.reverse(lis);
		System.out.println(lis);
		
		//void fill(List, Object) //用一个特定的对象重写整个 List 容器
		Collections.fill(lis, null);
		System.out.println(lis);
		
		//int binarySearch(List, Object)//采用折半查找的方法查找特定对象
		System.out.println(Collections.binarySearch(lis, 6));
	}
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!