java集合
此文档为java集合的学习总结
集合框架
Collection接口:
List接口:有序,可以重复
ArrayList(主要实现类)
LinkedList(频繁的插入、删除操作)
Vector(古老的实现类,线程安全)
Set接口:无序、不可重复
HashSet(主要实现类)
LinkedHashSet(使用链表维护了添加元素顺序,可以有序遍历,但底层存储元素还是hash无序的)
TreeSet(可以按照添加进集合中的元素的指定顺序遍历)
Map接口:存储key-value对
HashMap、LinkedHashMap、TreeMap、HashTable(-Properties)
两个排序接口:
自然排序:实现Comparable接口重写compareTo()方法
定制排序:创建TreeSet时传入Comparator对象
ArrayList
将Employee[]数组替换成ArrayList后:
不必指出数组的大小
使用add将任意多的元素添加到数组中
使用size()替代length计算元素的数目
使用a.get(i)替代a[i]访问元素
ArrayList<>尖括号中的类型参数不允许是基本类型。即不允许写成ArrayList
ArrayList<>与数组之间的转换
- 1.数组转ArrayList–直接转
import java.util.ArrayList;
public class Test
{
public static void main(String args[])
{
Employee[] staff = new Employee[2];
staff[0] = new Employee("alice");
staff[1] = new Employee("bob");
ArrayList<Employee> list = new ArrayList<>();
list.add(staff[0]);
list.add(staff[1]);
System.out.println(list.size());
for(Employee e: list)
{
System.out.println(e.getName());
}
}
}
class Employee
{
private String name;
public Employee(String n)
{
name = n;
}
public String getName()
{
return name;
}
}
output:
2
alice
bob
- 2.ArrayList转数组–toArray
public class Test
{
public static void main(String args[])
{
ArrayList<Employee> list = new ArrayList<>();
list.add(new Employee("alice"));
list.add(new Employee("bob"));
System.out.println(list.size());
Employee[] staff = new Employee[list.size()];
list.toArray(staff);//把list放进staff数组中
for(Employee e: staff)
{
System.out.println(e.getName());
}
}
}
output:
2
alice
bob
ArrayList示例小程序
import java.util.*;
/**
* This program demonstrates the ArrayList class.
*/
public class Test
{
public static void main(String args[])
{
ArrayList<Employee> staff = new ArrayList<>();
staff.add(new Employee("Alice", 75000, 1987, 12, 15));
staff.add(new Employee("Bob", 50000, 1989, 10, 1));
staff.add(new Employee("Carl", 40000, 1990, 3, 15));
for(Employee e: staff)
{
e.raiseSalary(5);
}
for(Employee e: staff)
{
System.out.println("name="+e.getName()+", salary="+e.getSalary()
+", hireDay="+e.getHireDay());
}
}
}
class Employee
{
private String name;
private double salary;
private Date hireDay;
public Employee(String n, double s, int year, int month, int day)
{
name = n;
salary = s;
GregorianCalendar cal = new GregorianCalendar(year, month-1, day);
hireDay = cal.getTime();
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;
}
public void raiseSalary(double percent)
{
salary *= (1 + percent/100);
}
public boolean equals(Object obj)
{
if(this == obj)
return true;
if(this == null)
return false;
if(this.getClass() != obj.getClass())
return false;
Employee tmp = (Employee)obj;
return Objects.equals(name, tmp.name) && this.salary==tmp.salary && hireDay.equals(tmp.hireDay);
}
public int hashCode()
{
return Objects.hash(name, salary, hireDay);
}
public String toString()
{
return getClass().getName()+"[name="+name+",salary="+salary+",hireDay="+hireDay+"]";
}
}
output:
name=Alice, salary=78750.0, hireDay=Tue Dec 15 00:00:00 CST 1987
name=Bob, salary=52500.0, hireDay=Sun Oct 01 00:00:00 CST 1989
name=Carl, salary=42000.0, hireDay=Thu Mar 15 00:00:00 CST 1990
LinkedList
LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的get,remove,insert方法在LinkedList的首部或尾部。这些操作使LinkedList可被用作队列(queue)或双向队列(deque),因为LinkedList类也实现了Queue、Deque接口。
import java.util.LinkedList; //这是实现了List、Queue、Deque等接口的实现类
import java.util.Queue; //这是队列的接口
public class Main {
public static void main(String[] args) {
//add()和remove()方法在失败的时候会抛出异常(不推荐)
Queue<String> queue = new LinkedList<String>();
//队尾添加元素
queue.offer("a");
queue.offer("b");
queue.offer("c");
queue.offer("d");
queue.offer("e");
for(String q : queue){
System.out.println(q);
}
System.out.println("===");
System.out.println("poll="+queue.poll()); //返回并删除队头元素
for(String q : queue){
System.out.println(q);
}
System.out.println("===");
System.out.println("element="+queue.element()); //返回第一个元素
for(String q : queue){
System.out.println(q);
}
System.out.println("===");
System.out.println("peek="+queue.peek()); //返回第一个元素
for(String q : queue){
System.out.println(q);
}
}
}
out:
a
b
c
d
e
===
poll=a
b
c
d
e
===
element=b
b
c
d
e
===
peek=b
b
c
d
e
注意LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:
List list = Collections.synchronizedList(new LinkedList(...));
Set
Set中的元素是用hash算法来存储的。
hash算法:当向Set中添加对象时,首先调用此对象所在类的hashCode()方法,计算此对象的hash值,此hash值决定了此对象在Set中的存储位置。若此位置没有对象存储,则存储该对象,若已有对象,再通过equals()方法比较两个对象是否相等,如果相等则不再存储,如果不相等,则hash冲突往后进行存储。
hash算法使Set有不可重复性,因此若要往Set中添加元素时,该元素所在的类需要同时重写equals()和hashCode()方法。
TreeSet
与其他Set不同,TreeSet中的元素必须是同一类的,且涉及排序(自然排序、定制排序),TreeSet底层按照红黑树进行排序。
自然排序:需要TreeSet中元素所在的类实现Comparable接口,重写相应的compareTo()方法。
compareTo()、equals()、hashCode()三者须一致。如果不一致,判断set重复元素时先按照compareTO()中定义的来,如compareTO()定义的name比较,那么相同名字的就算重复元素。
定制排序:创建TreeSet时传入Comparator对象
import org.junit.Test;
import java.util.*;
/**
* TreeSet自然排序、定制排序的演示例子
* 自然排序:需要TreeSet中元素所在的类实现Comparable接口,重写相应的compareTo()方法
* 定制排序:创建TreeSet时传入Comparator对象
*/
public class TestTreeSet{
//自然排序:需要Employee实现Comparable接口。这里按照name进行排序
@Test
public void test1(){
Employee e1 = new Employee("名1", 25, new MyDate(1990,10,13));
Employee e6 = new Employee("名1", 24, new MyDate(1990,10,13));
Employee e2 = new Employee("名2", 21, new MyDate(1993,10,13));
Employee e3 = new Employee("学1", 21, new MyDate(1993,6,13));
Employee e4 = new Employee("张2", 21, new MyDate(1993,6,20));
Employee e5 = new Employee("名5", 26, new MyDate(1997,3,13));
TreeSet set = new TreeSet();
set.add(e1);
set.add(e2);
set.add(e3);
set.add(e4);
set.add(e5);
set.add(e6);
Iterator it = set.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
//定制排序:创建TreeSet时传入Comparator对象。这里按照生日进行排序
@Test
public void test2(){
Comparator com = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof Employee && o2 instanceof Employee){
Employee e1 = (Employee)o1;
Employee e2 = (Employee)o2;
MyDate bir1 = e1.getBirthday();
MyDate bir2 = e2.getBirthday();
if(bir1.getYear() == bir2.getYear()){
if(bir1.getMonth() == bir2.getMonth()){
return bir1.getDay() - bir2.getDay();
}
return bir1.getMonth() - bir2.getMonth();
}
return bir1.getYear() - bir2.getYear();
}
return 0;
}
};
Employee e1 = new Employee("名1", 25, new MyDate(1990,10,13));
Employee e6 = new Employee("名1", 24, new MyDate(1990,10,13));
Employee e2 = new Employee("名2", 21, new MyDate(1993,10,13));
Employee e3 = new Employee("学1", 21, new MyDate(1993,6,13));
Employee e4 = new Employee("张2", 21, new MyDate(1993,6,20));
Employee e5 = new Employee("名5", 26, new MyDate(1997,3,13));
TreeSet set = new TreeSet(com);
set.add(e1);
set.add(e2);
set.add(e3);
set.add(e4);
set.add(e5);
set.add(e6);
Iterator it = set.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
class Employee implements Comparable{
private String name;
private int age;
private MyDate birthday;
//自然排序:需要Employee实现Comparable接口。这里按照name进行排序
public int compareTo(Object o){
if(o instanceof Employee){
Employee e = (Employee)o;
return this.name.compareTo(e.getName());
}
return 0;
}
public Employee(String name, int age, MyDate birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public MyDate getBirthday() {
return birthday;
}
public void setBirthday(MyDate birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
", birthday=" + birthday +
'}';
}
//同时需要重写equals方法,因为TreeSet中如contains等方法都需要equals判断
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return age == employee.age &&
Objects.equals(name, employee.name) &&
Objects.equals(birthday, employee.birthday);
}
@Override
public int hashCode() {
return Objects.hash(name, age, birthday);
}
}
class MyDate{
private int month;
private int day;
private int year;
public MyDate(int year, int month, int day) {
this.month = month;
this.day = day;
this.year = year;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
@Override
public String toString() {
return "MyDate{" +
"month=" + month +
", day=" + day +
", year=" + year +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyDate myDate = (MyDate) o;
return month == myDate.month &&
day == myDate.day &&
year == myDate.year;
}
@Override
public int hashCode() {
return Objects.hash(month, day, year);
}
}
Map接口
HashMap:主要实现类
LinkedHashMap:使用链表维护添加进Map中的顺序,故遍历Map时,是按添加顺序遍历的
TreeMap:按照添加进Map中元素的key的指定属性进行排序。故key必须是同一类的对象
Hashtable(子类Properties):Hashtable古老实现类,线程安全
Map与Collection并列存在,用于保存具有映射关系的数据:key-value
Map中的key和value都可以是任何引用类型的数据
Map中的key用Set来存放,不可重复,即同一个Map对象所对应的类,须重写hashCode()、equals()方法
各类Set其实就是Map的特例,Map中的key-value的value=null即为Set
常用String类作为Map的key
key和value之间存在单项一对一关系,即通过指定的key总能找到唯一的、确定的value
Java遍历Map对象的四种方式
文中讲了四种遍历方法,最快捷常用的是:
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}
HashMap
向HashMap中添加元素时,会调用key所在类的equals方法,判断两个key是否相同,若相同则只能添加后面添加的那个元素(覆盖前面元素)
HashMap遍历key集、value集。key是Set,value是Collection、key-value对是一个Set entrySet()
public void test1(){
HashMap hm = new HashMap();
hm.put(123, "asdad");
hm.put("AA", 12532);
hm.put(1234, new SimpleDateFormat("mm:ss").format(new Date()));
System.out.println(hm);
//遍历key集
Set set = hm.keySet();
Iterator it = set.iterator();
while(it.hasNext()){//迭代器遍历
System.out.println(it.next());
}
for(Object o: set){//增强for循环遍历
System.out.println(o);
}
//遍历value集
for(Object o: set){//通过key遍历来遍历value
System.out.println(hm.get(o));
}
Collection coll = hm.values();
for(Object o: coll){//通过value集的增强for循环遍历
System.out.println(o);
}
//遍历key-value对
//通过上述key遍历来遍历value可以得到,另一种方法是key-value对是一个Set entrySet()
Set entry = hm.entrySet();
for(Object obj: entry){
Map.Entry ety = (Map.Entry)obj;
System.out.println(ety.getKey() + "-->" + ety.getValue());
}
}
TreeMap
TreeSet是特殊的TreeMap,按照添加进Map中元素的key的指定属性进行排序。因此TreeMap的key必须是同一类的对象,且有自然排序、定制排序。
自然排序:key元素所在类要实现Comparable接口重写compareTo()、equals()、hashCode()方法
定制排序:创建TreeMap时传入Comparator对象
Hashtable
Hashtable古老实现类,线程安全
与HashMap不同,Hashtable不允许使用null作为key和value
与HashMap一样,Hashtable也不能保证其中key-value对的顺序
Hashtable判断两个key相等、两个value相等的标准,与HashMap一致
Hashtable的子类Properties:常用来处理属性文件,key、value都为String类型
import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.*;
public class TestHashMap{
@Test
public void test1() throws IOException{
Properties pro = new Properties();
pro.load(new FileInputStream(new File("abc.properties")));
String user = pro.getProperty("user");
String pass = pro.getProperty("password");
System.out.println(user + ":" + pass);
}
}
//abc.properties
//user=root
//pass=123456
//out: root:123456
Collections:操作集合的工具类
Arrays是一个操作数组的工具类;Collections是一个操作Set、List、Map等集合的工具类
Collections提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制方法
1.排序操作:
Collections.reverse(list);//反转list中元素的顺序
Collections.shuffle(list);//对list集合元素进行随机排序
Collections.sort(list); //根据元素的自然顺序对list集合元素进行升序排序
Collections.sort(list, Comparator); //根据指定的Comparator产生的顺序对list集合元素进行排序
Collections.swap(list, int, int)//将list中i处、j处元素进行交换
2.查找、替换:
Collections.max(Collection) //根据自然顺序,返回集合中最大元素
Collections.max(Collection, Comparator) //根据指定顺序,返回集合中最大元素
Collections.frequency(Collection) //返回指定元素的出现次数
Collections.copy(List dest, List src) //将src的内容复制到dest中
Collections.replaceAll(List, Object oldVal, Object newVal) //替换
这里注意Collections.copy(dest list, src list)需要提前设置dest list大小:
public void test1(){
List list = new ArrayList();
list.add(123);
list.add(13);
list.add(12);
list.add(12);
list.add(4456);
System.out.println(list);
List list2 = Arrays.asList(new Object[list.size()]);//没有这句设置list2空间的话error
Collections.copy(list2, list);
System.out.println(list2);
}
3.同步控制:
Collections类中提供了多个synchronizedXxX()方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题(因为ArrayList、HashSet、HashMap等等全部都线程不安全,所以多线程时可使用Collections的同步控制来达到线程安全)
public void test1(){
List list = new ArrayList(); //线程不安全
list.add(123);
list.add(13);
list.add(12);
list.add(12);
list.add(4456);
List list2 = Collections.synchronizedList(list);
System.out.println(list2); //list2是线程安全的
}
HashMap与concurrentHashMap
1.HashMap:
2.concurrentHashMap:
欢迎转载,欢迎错误指正与技术交流,欢迎交友谈心
文章标题:java集合
文章字数:3.3k
本文作者:Brain Cao
发布时间:2018-12-16, 15:54:20
最后更新:2020-03-15, 16:26:00
原始链接:https://braincao.cn/2018/12/16/java-collection/版权声明:本文为博主原创文章,遵循 BY-NC-SA 4.0 版权协议,转载请保留原文链接与作者。