Java常用集合解析

目录

1.List集合

2.Set集合

3.Mep集合


 

1.List集合

特点:元素有序,可重复。

三种遍历:下标,foie循环,迭代器。

扩容:
    初始容量10 , 负载因子0.5,扩容增量0.5倍
    新容量 = 原容量 + 原容量 * 0.5  , 如 ArrayList的容量为10,一次扩容后是容量为15

实现类:ArrayList . LinkedList . Vector . CopyOnWriteArrayList


ArrayList:1.动态数组,自动扩容。线程不安全。不适合随机增加和删除。

定义一个数组。如下:

package com.zking.demo.test;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.junit.Before;
import org.junit.Test;

public class ListDemo {
	private List<Integer> list;
	@Before
	public void Demo01() {
		list=new ArrayList<Integer>();
		list.add(1);
		list.add(2);
		list.add(3);
		list.add(3);
		list.add(4);
		list.add(5);
	}
	
}

 ArrayList正确删除元素的三种方法:

第一种(for循环):

@Test
	public void Demo02() {
		System.out.println("删除前:"+list);
		for (int i = list.size()-1; i >=0 ; i--) {
			if(list.get(i)==3) {
				Integer remove = list.remove(i);
			}
		}
		System.out.println("删除后:"+list);
	}

第二种(迭代器):

@Test
	public void Demo03() {
		System.out.println("删除前:"+list);
		Iterator<Integer> iterator = list.iterator();
		while(iterator.hasNext()) {
			if(iterator.next()==3) {
				iterator.remove();
			}
		}
		System.out.println("删除后:"+list);
	}

第三种(for循环):

@Test
	public void Demo04() {
		System.out.println("删除前:"+list);
		for (int i = 0; i < list.size(); i++) {
			if(list.get(i)==3) {
				list.remove(i--);
			}
		}
		System.out.println("删除后:"+list);
	}

结果是:

ArrayList错误删除方式:

第一种(相邻且相同无法删除):

@Test
	public void Demo05() {
		System.out.println("删除前:"+list);
		for (int i = 0; i < list.size(); i++) {
			if(list.get(i)==3) {
				list.remove(i);
			}
		}
		System.out.println("删除后:"+list);
	}

结果是:

看上面这张图,还有个元素3没有删除掉,出现这个错误是因为在删除第一个元素3之后后面的元素会挤上来,下标随之改变-1,相邻的第二个3就从下标为3变成了下标为2,从而第二个元素3就没有删掉。

第二种方式(有重复元素无法删除):

@Test
	public void Demo06() {
		System.out.println("删除前:"+list);
		for (Integer integer : list) {
			if(integer==3) {
				list.remove(integer);
			}
		}
		System.out.println("删除后:"+list);
	}

第三种方式(有重复元素无法删除):

Iterator<Integer> it=list.iterator();
 while(it.hasNext()){
  Integer value=it.next();
   if(value==3){
   list.remove(value);
  }
}
	

结果都是:

第四种方式(根据下标删除): 

@Test
	public void Demo07() {
		System.out.println("删除前:"+list);
		list.remove(1);
		System.out.println("删除后:"+list);
	}
	

结果是:

 LinkedList
    LinkedList提供额外的get,remove,insert方法在LinkedList的首部或尾部
    线程不安全
    LinkedList可被用作堆栈(stack)【包括了push,pop方法】,队列(queue)或双向队列(deque)
    以双向链表实现,链表无容量限制,允许元素为null,线程不安全
    适合做随机的增加或删除

Vector
    线程安全
    并行性能慢,不建议使用 

CopyOnWriteArrayList
    写时复制
    线程安全
    适合于读多,写少的场景
    写时复制出一个新的数组,完成插入、修改或者移除操作后将新数组赋值给array
    比Vector性能高
    最终一致性
    实现了List接口,使用方式与ArrayList类似 

2.Set集合

特点:无序,不可重复。

扩容: 初始容量16,负载因子0.75,扩容增量1倍

实现类:HashSet . TreeSet

 HashSet:

1.不重复,无序,允许空值。:依据对象的hashcode来确定该元素是否存在

2.底层是hashMap实现。

3.线程不安全

4.性能参数:初始容量,负载因子
    默认值: 初始容量16,负载因子0.75
    示例:new HashSet<>(20, 0.5f);

遍历:fore循环,迭代器。

实体类:

package com.zking.demo.entity;

public class Student {

	private Integer id;
	private String name;
	private Integer age;
	
	public Student() {
		// TODO Auto-generated constructor stub
	}

	public Student(Integer id, String name, Integer age) {
		super();
		this.id = id;
		this.name = name;
		this.age = age;
	}

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

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

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((age == null) ? 0 : age.hashCode());
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		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;
		Student other = (Student) obj;
		if (age == null) {
			if (other.age != null)
				return false;
		} else if (!age.equals(other.age))
			return false;
		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;
	}

	

	
	
	
}
//fore循环遍历
	@Test
	public void demo03() {
		Set<Student> set =new HashSet<>();
		set.add(new Student(1, "xm", 18));
		set.add(new Student(1, "xm", 18));
		set.add(new Student(2, "xh", 19));
		set.add(new Student(2, "xh", 19));
		set.add(new Student(3, "xl", 20));
		set.add(new Student(3, "xl", 20));
		for (Student student : set) {
			System.out.println(student);
		}
	}
	
	//迭代器遍历
	@Test
	public void demo04() {
		Set<Student> set =new HashSet<>();
		set.add(new Student(1, "xm", 18));
		set.add(new Student(1, "xm", 18));
		set.add(new Student(2, "xh", 19));
		set.add(new Student(2, "xh", 19));
		set.add(new Student(3, "xl", 20));
		set.add(new Student(3, "xl", 20));
		Iterator ite=set.iterator();
		while(ite.hasNext()) {
			System.out.println(ite.next());
		}
	}

结果是:

这个结果是因为Student学生实体实现了 hashCode()  ,  equals()方法。

如果不实现这两个方法,那么就不会自动去重。则就是如下图:

如果自定义实体类对象的属性有很多,20个、30个等等,那么我们只需要重写equals和hashcode方法里面特定的几个属性,只要足够判断去重就可以了。 

比如上面Student类是三个属性,但判断学生类去重只需要重写hashcode和equals方法中的一个编号就可以了。

也就是如下实体类代码:

package com.zking.demo.entity;

public class Student {

	private Integer id;
	private String name;
	private Integer age;
	
	public Student() {
		// TODO Auto-generated constructor stub
	}

	public Student(Integer id, String name, Integer age) {
		super();
		this.id = id;
		this.name = name;
		this.age = age;
	}

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

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

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.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;
		Student other = (Student) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}

	
}

 TreeSet
    是一个包含有序的且没有重复元素的集合
    作用是提供有序的Set集合,自然排序或者根据提供的Comparator进行排序
    TreeSet是基于TreeMap实现的

思考:list集合怎么去重?

        答案:借助set集合。首先先将list集合对象通过set集合的构造函数转化为set集合对象,再通过同样的操作将set集合对象通过list集合的构造函数转化为list集合对象就可以了。

例如:

package com.zking.demo.test;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

import org.junit.Before;
import org.junit.Test;
public class Set {

	private List<Integer> list=new ArrayList<Integer>();
	@Before
	public void demo01() {
		list.add(1);
		list.add(1);
		list.add(2);
		list.add(3);
		list.add(3);
	}
	
	@Test
	public void demo02() {
		List<Integer> list=new ArrayList<>(new HashSet<>(this.list));
		System.out.println(list);
		
	}
	
}

 Set集合想要排序,就要借助它的实现类treeSet来进行排序。(默认是自然排序)

@Test
	public void demo05() {
		Set<Integer> set=new TreeSet<>();
		set.add(1);
		set.add(3);
		set.add(4);
		set.add(6);
		set.add(2);
		set.add(5);
		for (Integer integer : set) {
			System.out.println(integer);
		}
	}

结果是:

如果想要倒序排序就需要用到比较器Comparator,可以实现也可以通过构造方法传入。

下面是通过构造方法:

@Test
	public void demo05() {
		Set<Integer> set=new TreeSet<>(new Comparator<Integer>() {

			@Override
			public int compare(Integer o1, Integer o2) {
				// TODO Auto-generated method stub
				return o2-o1;
			}
			
		});
		set.add(1);
		set.add(3);
		set.add(4);
		set.add(6);
		set.add(2);
		set.add(5);
		for (Integer integer : set) {
			System.out.println(integer);
		}
	}

结果就是:

那如果是对象呢?该通过什么方式排序?

就比如Student学生类,通过构造方法传入比较器之后,通过对象的属性来比较大小进行排序

如下通过年龄来进行排序:

@Test
	public void demo06() {
		Set<Student> set=new TreeSet<>(new Comparator<Student>() {

			@Override
			public int compare(Student o1, Student o2) {				
				return o2.getAge()-o1.getAge();//通过学生的年龄来进行排序。
			}
			
		});
		
		set.add(new Student(1, "xm", 18));
		set.add(new Student(2, "xh", 19));
		set.add(new Student(3, "xl", 20));
		set.add(new Student(4, "xx", 21));
		Iterator ite=set.iterator();
		while(ite.hasNext()) {
			System.out.println(ite.next());
		}
		
	}

结果是:

 但有一个小细节需要注意,如果出现同样年龄的人,就会漏掉一个学生。所以我们得在做一个判断,当学生的年龄相同时,用一个特有属性再进行排序即可。

如下:

@Test
	public void demo06() {
		Set<Student> set=new TreeSet<>(new Comparator<Student>() {

			@Override
			public int compare(Student o1, Student o2) {	
				if(o2.getAge()==o1.getAge()) {
					return o2.getId()-o1.getId();//再通过编号进一步排序。
				}
				return o2.getAge()-o1.getAge();//通过学生的年龄来进行排序。
			}
			
		});
		
		set.add(new Student(1, "xm", 18));
		set.add(new Student(2, "xh", 19));
		set.add(new Student(3, "xl", 20));
		set.add(new Student(4, "xx", 20));
		Iterator ite=set.iterator();
		while(ite.hasNext()) {
			System.out.println(ite.next());
		}

结果是:

 这样就不会漏掉数据了。

就比如Student学生类,通过实现比较器接口,通过对象的属性来比较大小进行排序

如下通过年龄来进行排序:

实体类:

package com.zking.demo.entity;

import java.util.Comparator;

public class Student implements Comparable<Student> {

	private Integer id;
	private String name;
	private Integer age;
	
	public Student() {
		// TODO Auto-generated constructor stub
	}

	public Student(Integer id, String name, Integer age) {
		super();
		this.id = id;
		this.name = name;
		this.age = age;
	}

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

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

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.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;
		Student other = (Student) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}

	@Override
	public int compareTo(Student o) {

		if(o.getAge()==this.getAge()) {
			return o.getId()-this.getId();
		}
		return o.getAge()-this.getAge();
	}

	

	
}

 测试类:

@Test
	public void demo06() {
		Set<Student> set=new TreeSet<>();
		
		set.add(new Student(1, "xm", 18));
		set.add(new Student(2, "xh", 19));
		set.add(new Student(3, "xl", 20));
		set.add(new Student(4, "xx", 20));
		Iterator ite=set.iterator();
		while(ite.hasNext()) {
			System.out.println(ite.next());
		}
		
	}

结果是一样的:

如果实体类实现了比较器接口Comparabled,而测试方法通过构造方法传入了构造器,那么是构造方法的生效。

3.Mep集合

特点:
    无序,键值对,键不能重复,值可以重复,
    键重复则覆盖,没有继承Collection接口

扩容:初始容量16,负载因子0.75,扩容增量1倍

遍历
    先获取所有键的Set集合,再遍历(通过键获取值)
    取出保存所有Entry的Set,再遍历此Set即可

 hashMap:

第一种遍历方式,通过keySet()获取所有键Set集合,然后遍历再得到值。

如下:

@Test
	public void demo01() {
		map.put("1", "小明");
		map.put("2", "小明2");
		map.put("3", "小明3");
		map.put("4", "小明4");
		Iterator<String> iterator = map.keySet().iterator();
		while(iterator.hasNext()) {
			String key = iterator.next();
			Object value = map.get(key);
			System.out.println(value);
		}
	}

结果是:

第二种遍历方式:通过entrySet()方法,得到所有键值对对象的Set集合,然后通过迭代器遍历分别得到键和值。

如下:

@Test
	public void demo02() {
		map.put("1", "小明");
		map.put("2", "小明2");
		map.put("3", "小明3");
		map.put("4", "小明4");
		Set<Entry<String,Object>> entrySet = map.entrySet();
		Iterator<Entry<String, Object>> iterator = entrySet.iterator();
		while(iterator.hasNext()) {
			Entry<String, Object> next = iterator.next();
			System.out.println("key:"+next.getKey()+"-----"+"value:"+next.getValue());
		}

 结果是:

 

HashTable
    线程安全,不太常用 ,效率慢。

 ConcurrentHashMap
    线程安全,比HashTable性能高

TreeMap
    key值按一定的顺序排序
    添加或获取元素时性能较HashMap慢
        因为需求维护内部的红黑树,用于保证key值的顺序 

代码如下:

@Test
	public void demo03() {
		Map<String, Object> treeMap=new TreeMap<>(new Comparator<String>() {

			@Override
			public int compare(String o1, String o2) {
				// TODO Auto-generated method stub
				return o2.compareTo(o1);
			}
			
		});
		treeMap.put("1", "小明");
		treeMap.put("2", "小明2");
		treeMap.put("3", "小明3");
		treeMap.put("4", "小明4");
		Set<Entry<String,Object>> entrySet = treeMap.entrySet();
		Iterator<Entry<String, Object>> iterator = entrySet.iterator();
		while(iterator.hasNext()) {
			Entry<String, Object> next = iterator.next();
			System.out.println("key:"+next.getKey()+"-----"+"value:"+next.getValue());
		}
		
	}

结果:

 默认是升序排序,而上面o1和o2调货了位置,则变成了降序。

LinkedHashMap:

LinkedHashMap是有序的,且默认为插入顺序
    当我们希望有顺序地去存储key-value时,就需要使用LinkedHashMap了

代码如下:


	@Test
	public void demo04() {
		 Map<String, String> linkedHashMap = new LinkedHashMap<>();
	        linkedHashMap.put("name1", "josan1");
	        linkedHashMap.put("name2", "josan2");
	        linkedHashMap.put("name3", "josan3");
	        Set<Entry<String, String>> set = linkedHashMap.entrySet();
	        Iterator<Entry<String, String>> iterator = set.iterator();
	        while(iterator.hasNext()) {
	            Entry entry = iterator.next();
	            String key = (String) entry.getKey();
	            String value = (String) entry.getValue();
	            System.out.println("key:" + key + ",value:" + value);
	        }
	}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值