Java小白修炼手册--第二阶段--Java SE--集合框架-Collection 和集合操作-Map

目录

集合框架

集合框架-Collection:

Collection

List和Set

集合持有对象的引用

集合中常见方法

add方法

size、clear、 isEmpty

contains方法

remove方法

addAll、containsAll、removeAll

Iterator

hasNext、next方法

remove方法

增强型for循环

泛型机制

泛型在集合中的应用

新的方法:foreach

集合操作线性表:

List

ArrayList和LinkedList

ArrayList和LinkedList区别

​get和set

插入和删除

List subList(int start,int end)获取指定范围内的子集

List转换为数组

数组转换为List

Collections是集合的工具类

Collection 是顶级接口

List排序

Colletions. sort方法实现排序

Comparable

Comparator

队列和栈

Queue

Deque 

stack( 栈)

集合操作-Map

Map接口

Map接口

put( )方法

get( )方法

containsKey ( )方法

size()

remove(Object key)

HashMap

Hash表原理

hashCode方法

重写hashCode方法

装在因子及HashMap优化

Map的遍历

使用keySet( )方法

使用entrySet( )方法

使用Collection values();方法

有序的Map

LinkedHashMap实现有序Map



 

集合框架

Java集合框架(Java Collections Framework,JCF)是为表示和操作集合而规定的一种统一的标准的体系结构。任何集合框架都包含三大块内容:对外的接口、接口的实现和对集合运算的算法。

集合框架-Collection:

Collection

集合框架
 * java.util.Collection接口,该接口是所有接口的顶级接口,规定了所有集合都应当 具备的功能方法
 * Collection 下面又分为不同的集合类型,常见的有两个:
 * java.util.Set;接口: 不可重复集,常用实现类java.util.HashSet
 * java.util.List:可重复集,并且有序,常见 实现类:java.util.ArrayListjava.util.LinkedList
 * 这里指的重复元素是否可以重复,二重复元素的判定标准是元素自身equals比较是否为true

List和Set

  • 在实际开发中,需要将使用的对象存储于特定数据结构的容器中。JDK提供 了这样的容器----集合(Collection)。
  • Collection是一个接口, 定义了集合相关的操作方法,其有两个子接口: ListSet
  • List:可重复集,元素是否重复,取决于元素的equals( )比较的结果
  • Set:不可重复集
     



集合持有对象的引用

集合中存储的都是引|用类型元素, 并且集合只保存每个元素对象的引用,而并非将元素对象本身存入集合。

public void testRef( {
    Collection<Cell> cells = new ArrayList <Cell> 0;
    cells.add(new Cell(1, 2));
    Cellcell = new Cell(2, 3);
    cells.add(cell);
    System.out.println(cell);// (2,3)
    System.out.printIn(cells);// [(1,2), (2,3)]
    cell.drop();
    System.out.printn(cell);// (3,3)
    System.out.println(cells);// [(1,2), (3,3)]
}

集合中常见方法

方式 方法 说明
集合中的操作 boolean add(E e) 向当前集合中添加给定的元素,返回值为true时表示该元素成功添加到集合中
int size( ) 返回当前集合的元素个数
boolean isEmpty( ) 判断当前集合是否为空集(集合不含有任何元素,size为0时)
void clear( ) 清空集合
boolean contains(E e)

判断当前集合是否包含给定元素,判断依据是给定元素与集合现有元素是否存在.

equals比较为true的情况,存在则认为包含。

boolean remove(E e) 删除给定元素,删除的也是与给定元素equals比较为true的元素。对于List集合而言重复元素只会删除一次

集合间操作

boolean addAll(Collection c) 将给定集合中的所有元素添加到当前集合中。
boolean containsAll(Collection c) 判断当前集合是否包含给定集合中的所有元素。
removeAll(Collection c) 删除交集,即:删除当前集合中与给定集合的共有元素。但是给定的集合元素不受影响。

 

 

 

 

 

 

 

 

 

 

 

 

add方法

  • Collection定义了一个add方法用于向集合中添加新元素。
  • boolean add(E e)
  • 该方法会将给定的元素添加进集合,若添加成功则返回true,否则返回false

size、clear、 isEmpty

int size( )
该方法用于返回当前集合中的元素总数。
void clear( )
该方法用于清空当前集合。
boolean isEmpty( )
该方法用于判断当前集合中是否不包含任何元素
 

package collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;


public class CollectionDemo1 {
	public static void main(String[] args) {	
		Collection c = new ArrayList();
//		Collection c = new HashSet();
		/*
		 * boolean add(E e)
		 * 向当前集合中添加给定的元素,返回值为true时表示该元素成功添加到集合中
		 */
		c.add("one");
		c.add("two");
		c.add("three");
		c.add("four");
		c.add("five");
//		c.add("one");//List集合可以存放,Set集合就不行了。
		System.out.println(c);
		/*
		 * int size()
		 * 返回当前集合的元素个数
		 */
		int size = c.size();
		System.out.println("size:"+size);
		/*
		 * boolean isEmpty()
		 * 判断当前集合是否为空集(集合不含有任何元素,size为0时)
		 */
		boolean isEmpty = c.isEmpty();
		System.out.println("是否为空集:"+isEmpty);
		
		/*
		 * void clear()
		 * 清空集合
		 */
		c.clear();
		
		System.out.println(c);
		System.out.println("size:"+c.size());
		System.out.println("是否为空集:"+c.isEmpty());
		
	}
}











contains方法

  • boolean contains(Object o)
  • 该方法会用于判断给定的元素是否被包含在集合中。若包含则返回true,否则返回false。
  • 这里需要注意的是,集合在判断元素是否被包含在集合中是根据每个元素的equals(方法进行比较后的结果。
  • 通常有必要重写equals(保证contains(方法的合理结果

remove方法

  • boolean remove(E e)
  • 删除给定元素,删除的也是与给定元素equals比较为true的元素。对于List集合而言重复元素只会删除一次
package collection;

import java.util.ArrayList;
import java.util.Collection;

/**
 * 集合的很多操作与元素的equals方法有关。
 *
 */
public class CollectionDemo2 {
	public static void main(String[] args) {
		Collection c = new ArrayList();
		c.add(new Point(1,2));
		c.add(new Point(3,4));
		c.add(new Point(5,6));
		c.add(new Point(7,8));
		c.add(new Point(1,2));
		System.out.println(c);
		
		
		Point p = new Point(1,2);
		/*
		 * boolean contains(E e)
		 * 判断当前集合是否包含给定元素,判断依据是给定元素与集合现有元素是否存在
		 * equals比较为true的情况,存在则认为包含。
		 */
		boolean contains = c.contains(p);
		System.out.println("是否包含:"+contains);
		
		/*
		 * boolean remove(E e)
		 * 删除给定元素,删除的也是与给定元素equals比较为true的元素。对于List
		 * 集合而言重复元素只会删除一次
		 */
		c.remove(p);
		System.out.println(c);
	}
}







package collection;

import java.util.ArrayList;
import java.util.Collection;

/**
 * 集合只能存放引用类型元素,并且保存的也是元素的引用(地址)
 *
 */
public class CollectionDemo3 {
	public static void main(String[] args) {
		Point p = new Point(1,2);
		Collection c = new ArrayList();
		c.add(p);
		
		System.out.println("p:"+p);//(1,2)
		System.out.println("c:"+c);//[(1,2)]
		
		p.setX(2);
		System.out.println("p:"+p);//(2,2)
		System.out.println("c:"+c);//[(2,2)]
	}
}







addAll、containsAll、removeAll

boolean addAll(Collection<? extends E> c) 
该方法需要我们传入一个集合, 并将该集合中的所有元素添加到当前集合中。
如果此collection由于调用而发生更改,则返回true
boolean containsAll(Collection<?> c)
该方法用于判断当前集合是否包含给定集合中的所有元素,若包含则返回true。
removeAll(Collection c)

删除交集,即:删除当前集合中与给定集合的共有元素。但是给定的集合元素不受影响。

package collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;

/**
 * 集合间的操作
 * @author 范传奇
 *
 */
public class CollectionDemo4 {
	public static void main(String[] args) {
		Collection c1 = new ArrayList();
		c1.add("java");
		c1.add("c++");
		c1.add(".net");
		System.out.println("c1:"+c1);
		
		Collection c2 = new HashSet();
		c2.add("android");
		c2.add("ios");
		c2.add("java");
		System.out.println("c2:"+c2);
		/*
		 * boolean addAll(Collection c)
		 * 将给定集合中的所有元素添加到当前集合中。
		 */
//		c2.addAll(c1);//重复元素不能放入Set集合两次
		c1.addAll(c2);
		System.out.println("c1:"+c1);
		System.out.println("c2:"+c2);
		
		
		Collection c3 = new ArrayList();
		c3.add(".net");
		c3.add("ios");
//		c3.add("php");
		System.out.println("c3:"+c3);
		/*
		 * boolean containsAll(Collection c)
		 * 判断当前集合是否包含给定集合中的所有元素。
		 * 
		 * 注意区别昨天学习的contains方法,如果是:
		 * c1.contains(c3)
		 * 则是判断当前c1集合中是否有一个元素是c3集合。此时是把c3当成一个元素看待
		 * 由于c1集合中没有一个元素是一个集合类型,因此返回为false.
		 */
		boolean contains = c1.containsAll(c3);
		System.out.println("c1是否包含c3所有元素:"+contains);
		
		/*
		 * removeAll(Collection c)
		 * 删除交集,即:删除当前集合中与给定集合的共有元素。但是给定的集合元素不受
		 * 影响。
		 * 
		 */
		c1.removeAll(c3);//删除交集,删除c1当中与c3集合的共有元素
		System.out.println("c1:"+c1);//c1元素减少了
		System.out.println("c3:"+c3);//c3元素没有变化
	}
}

Iterator

 java.util.Iterator接口
 该接口是迭代器接口,规定了迭代器遍历集合的相关操作,不同的集合实现类都提供了一个用于遍历自身元素的迭代器实现类。我们无需知道每种集合提供的迭代器实现类的名字,只需要当它是Iterator看待即可。迭代器遍历集合的步骤遵循:问,取,删
其中删除元素不是遍历过程中的必须操作。

hasNext、next方法

  • 迭代器用于遍历集合元素。获取迭代器可以使用Collection定义的方法:Iterator iterator( )
  • 迭代器Iterator是个接口,集合在重写Collectioniterator( )方法时利用内部类提供了迭代器的实现。
  • Iterator提供了统一的遍历集合元素的方式,其提供了用于遍历集合的两个方法:
  1. boolean hasNext( ):判断集合是否还有元素可以遍历。
  2. E next( ):返回迭代的下一个元素
     


remove方法

  • 在使用迭代器遍历集合时,不能通过集合的remove方法删除集合元素,否则会抛出并发更改异常。我们可以通过迭代器自身提供的remove( )方法来删除通过next(迭代出的元素。
  • - - -void remove( )
  • 迭代器的删除方法是在原集合中删除元素
  • 这里需要注意的是,在调用remove方法前必须通过迭代器的next( )方法迭代过元素,那么删除的就是这个元素。并且不能再次调用remove方法,除非再次调用next( )后方可再次调用。
package collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

/**
 * 集合的遍历
 * Collection提供了统一的遍历集合元素的操作:迭代器
 * 
 * 方法:
 * Iterator iterator()
 * 该方法会获取一个用于遍历当前集合的迭代器
 * 
 * java.util.Iterator接口
 * 该接口是迭代器接口,规定了迭代器遍历集合的相关操作,不同的集合实现类都提供了
 * 一个用于遍历自身元素的迭代器实现类。我们无需知道每种集合提供的迭代器实现类
 * 的名字,只需要当它是Iterator看待即可。迭代器遍历集合的步骤遵循:问,取,删
 * 其中删除元素不是遍历过程中的必须操作。
 * @author 范传奇
 *
 */
public class CollectionDemo5 {
	public static void main(String[] args) {
		Collection c = new ArrayList();
		c.add("one");
		c.add("#");
		c.add("two");
		c.add("#");
		c.add("three");
		c.add("#");
		c.add("four");
		c.add("#");
		c.add("five");
		System.out.println(c);
		
		//获取遍历该集合的迭代器
		Iterator it = c.iterator();
		/*
		 * boolean hasNext()
		 * 判断集合是否还有下一个元素可以遍历(第一次遍历时相当于询问有没有第一个元素)
		 */
		while(it.hasNext()) {
			/*
			 * E next()
			 * 获取集合下一个元素,同样的,第一次调用时获取的是第一个元素
			 */
			String str = (String)it.next();
			System.out.println(str);
			//遍历的过程中删除所有#
			if("#".equals(str)) {
				/*
				 * 迭代器有一个要求,遍历的过程中不可以通过集合的方法增删元素
				 * 否则遍历过程中会抛出异常:ConcurrentModificationException
				 */
//				c.remove(str);
				/*
				 * 迭代器提供了remove方法,删除的是通过next获取的元素
				 */
				it.remove();
			}
		}
		
		System.out.println(c);
	}
}


 


增强型for循环

  • Java5.0之后推出了一个新的特性,增强for循环,也称为新循环。该循环不通用于传统循环的工作,其只用于遍历集合或数组。
  • 语法:
  •         for( 元素类型  e  集合或数组  ){
  •                  循环体
  •        }
  •          
  • 新循环并非新的语法,而是在编译过程中,编译器会将新循环转换为迭代器模式。所以新循环本质上是迭代器。

泛型机制

泛型在集合中的应用

  • 泛型是Java SE 5.0引入的特性,泛型的本质是参数化类型。在类、接口和方法的定义过程中,所操作的数据类型被传入的参数指定。
  • Java泛型机制广泛的应用在集合框架中。所有的集合类型都带有泛型参数,这样在创建集合时可以指定放入集合中元素的类型。Java编译 器可以据此进行类型检查,这样可以减少代码在运行时出现错误的可能性。
     
  • ArrayList类的定义中,<E>中的E为泛型参数,在创建对象时可以将类型作为参数传递,此时, 类定义所有的E将被替换成传入的参数;
package collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

/**
 * JDK5推出时,推出了一个特性:增强型for循环。
 * 该特性使得我们可以用相同的语法遍历数组和集合。
 * 
 * 语法:
 * for(接收元素的变量定义 : 集合或数组){
 * 	循环体
 * }
 * 
 * JDK5推出时还推出了一个特性:泛型
 * 泛型又称为参数化类型,可以在使用一个类时指定该类中某个属性或者方法的参数,返回值
 * 的类型。使得使用时更灵活。
 * 泛型在集合中广泛应用,用于指定集合中的元素类型。支持泛型的类在使用时若不指定泛型
 * 的实际类型时,默认为Object
 * 
 *
 */
public class NewForDemo {
	public static void main(String[] args) {
		String[] array = {"one","two","three","four","five"};
		for(int i=0;i<array.length;i++) {
			String str = array[i];
			System.out.println(str);
		}
		/*
		 * 新循环语法是编译器认可的而非虚拟机,我们使用新循环遍历数组时会被
		 * 编译器改为使用普通for循环遍历。
		 */
		for(String str : array) {
			System.out.println(str);
		}
		
		/*
		 * Collection<E>,集合定义时有一个泛型类型E,那么使用时可以指定E表示的
		 * 实际类型是什么,下面的例子中实际类型指定的为Integer
		 */
		Collection<Integer> c = new ArrayList<Integer>();
		c.add(1);//自动装箱
		c.add(2);
		c.add(3);
		c.add(4);
		c.add(5);
//		c.add("one");//编译不通过,编译器会检查传入的参数是否为Integer
		System.out.println(c);
		//迭代器使用时同样要指定泛型,类型与遍历的集合指定的泛型一致即可。
		Iterator<Integer> it = c.iterator();
		while(it.hasNext()) {
			Integer i = it.next();//获取元素时无需再造型。
			System.out.println(i);
		}
		/*
		 * 新循环遍历集合会被编译器改为迭代器遍历,因此使用新循环遍历集合的过程
		 * 中不能通过集合的方法增删元素。
		 */
		for(Integer i : c) {//集合指定泛型后,这里可以直接用泛型指定类型接收
			System.out.println(i);
		}
		/*
		 * 使用新循环(迭代器)遍历集合不是并发安全的,也不会和集合的增删元素做
		 * 互斥,因此在多线程使用下,要自行维护多个线程对该集合的操作,避免一个
		 * 线程遍历的过程中其他线程进行增删元素操作,否则遍历这里会抛出异常。
		 */
	}
}

新的方法:foreach

JDK8之后集合推出了一个新的方法:foreach,可以使用lambda表达式进行遍历。

package collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * JDK8之后集合推出了一个新的方法:foreach,可以使用lambda表达式进行遍历。
 * 如果一个集合是并发安全的集合,使用foreach方法遍历是可以和增删元素互斥的。
 * 注:
 * 哪怕是一个并发安全的集合,迭代器方式遍历也不和增删元素互斥,只能自行使用同步块
 * 维护互斥关系。
 * 
 * 我们常用的集合实现类:ArrayList,LinkedList,HashSet都不是线程安全的。
 * @author 范传奇
 *
 */
public class ForeachDemo {
	public static void main(String[] args) {
		//JDK7之后,实例化集合时,泛型可以只写<>,而不用再写一遍实际类型。
		Collection<String> c = new ArrayList<>();
		c.add("one");
		c.add("two");
		c.add("three");
		c.add("four");
		c.add("five");
		System.out.println(c);
		
		for(String s : c) {
			System.out.println(s);
		}
		
		c.forEach(
			(e)->System.out.println(e)	
		);
		
		
		/*
		 * 将现有的集合转换为并发安全的集合
		 */
		List<String> list = new ArrayList<>();
		list.add("1");
		list.add("2");
		list.add("3");
		System.out.println(list);
		//将给定的list集合转换为线程安全的list集合
		list = Collections.synchronizedList(list);
		System.out.println(list);		
		/*
		 * 同样的也有synchronizedSet方法,可以将一个set集合转换为线程安全的
		 * 
		 */
		
		
	}
}

 

package collection;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 线程安全的集合foreach方法和增删元素是互斥的,但是新循环(迭代器)则不是。
 */
public class ForeachDemo2 {
	public static void main(String[] args) {
		//创建一个线程安全的集合
		List<String> list = Collections.synchronizedList(new ArrayList<>());
		list.add("1");
		list.add("2");
		list.add("3");
		list.add("4");
		list.add("5");
		System.out.println(list);
		Thread t1 = new Thread() {
			public void run() {
				//新循环遍历(迭代器)
//				for (String s : list) {
//					System.out.println(getName()+":"+s);
//					try {
//						Thread.sleep(1000);
//					} catch (InterruptedException e) {
//					}
//				}
				//使用forEach遍历 
				list.forEach(//forEach会和下面线程调用add方法互斥保证并发安全
						(e)->{
							System.out.println(getName()+":"+e);
							try {
								Thread.sleep(1000);
							} catch (InterruptedException e1) {
							}
						}
				);
				System.out.println(getName()+":遍历集合完毕!");
			}
		};
		
		Thread t2 = new Thread() {
			public void run() {
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
				}
				System.out.println(getName()+":开始添加新元素");
				list.add("6");
				System.out.println(getName()+":新元素添加完毕!");
				System.out.println(list);
			}
		};
		t1.start();
		t2.start();
	}
}

集合操作线性表:

List

ArrayList和LinkedList

  • List接口是Collection的子接口,用于定义线性表数据结构。可以将List理解为存 放对象的数组,只不过其元素个数可以动态的增加或减少。
  • List接口的两个常见实现类为ArrayListLinkedList ,分别用动态数组和链表的方式实现了List接口。
  • 可以认为ArrayListLinkedList的方法在逻辑上完全一样,只是在性能上有定的差别。 ArrayList更适合 于随机访问,而LinkedList更适合于插入和删除。在性能要求不是特别苛刻的情形下可以忽略这个差别。

ArrayList和LinkedList区别

  • java.util.ArrayList:内部由数组实现,查询性能更好
  •  java.util.LinkedList:内部由链表实现,增删元素性能更好。


get和set

List除了继承Collection定义的方法外,还根据其线性表的数据结构定义了一系列方法,其中最常用的就是基于下标的get和set方法:
E get(int index)
获取集合中指定下标对应的元素,下标从0开始。
E set(int index, E elment)
将给定的元素存入给定位置,并将原位置的元素返回。
 

插入和删除

List根据下标的操作还支持插入与删除操作。
void add(int index,E element):
将给定的元素插入到指定位置,原位置及后续元素都顺序向后移动。
E remove(int index):
删除给定位置的元素,并将被删除的元素返回。
 

package collection;

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

/**
 * java.util.List集合
 * List集合可以保存重复元素,并且有序。提供了一套通过下标操作元素的方法。
 * 
 * 常用实现类:
 * java.util.ArrayList:内部由数组实现,查询性能更好
 * java.util.LinkedList:内部由链表实现,增删元素性能更好。
 * 如果不是对性能有特别苛刻要求下,一般都使用ArrayList
 *
 */
public class ListDemo {
	public static void main(String[] args) {
		
		List<String> list = new ArrayList<>();
		list.add("one");
		list.add("two");
		list.add("three");
		list.add("four");
		System.out.println(list);
		
		/*
		 * E get(int index)
		 * 获取指定下标处对应的元素
		 */
		//获取List集合中第三个元素
		String str = list.get(2);
		System.out.println(str);
		
		for(int i=0;i<list.size();i++) {
			str = list.get(i);
			System.out.println(str);
		}
		
		/*
		 * E set(int i,E e)
		 * 将给定元素设置到指定位置,返回值为原位置对应的元素(被替换的元素)。
		 */
		//[one,2,three,four]
		String old = list.set(1,"2");//指定的下标如果超范围会抛异常
		System.out.println(list);
		System.out.println(old);
		
		
		/*
		 * 重载的add方法:
		 * void add(int index,E e)
		 * 将给定元素插入到指定位置
		 */
		//[one,2,3,three,four]
		list.add(2,"3");
		System.out.println(list);
		
		/*
		 * 重载的remove方法
		 * E remove(int index)
		 * 删除并返回指定位置上的元素
		 */
		//[one,2,3,four]
		old = list.remove(3);
		System.out.println(list);
		System.out.println(old);//three
	}
}

List subList(int start,int end)获取指定范围内的子集

package collection;

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

/**
 * List subList(int start,int end)
 * 获取指定范围内的子集
 *
 */
public class ListDemo2 {
	public static void main(String[] args) {
		List<Integer> list = new ArrayList<>();
		for(int i=0;i<10;i++) {
			list.add(i);
		}
		System.out.println(list);
		
		List<Integer> subList = list.subList(3, 8);
		System.out.println(subList);
		//将subList中每个元素扩大10倍
		for(int i=0;i<subList.size();i++) {
			subList.set(i,subList.get(i) * 10);
		}	
		//[30,40,50,60,70]
		System.out.println(subList);
		/*
		 * 对子集元素的操作就是对原集合对应的操作
		 */
		System.out.println(list);
		
		/*
		 * 删除list集合中2-8的元素
		 */
		list.subList(2,9).clear();
		System.out.println(list);
	}
}

List转换为数组

List的toArray方法用于将集合转换为数组。但实际上该方法是在Collection中定义的,所以所有的集合都具备这个功能。
其有两个方法:
    Object[ ] toArray( )
    < T > T [ toArray(TO a)

其中第二个方法是比较常用的,我们可以传入一个指定类型的数组,该数组的元素类型应与集合的元素类型一致。返回值则是转换后的数组,该数组会保存集合中所有的元素。
 

package collection;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;

/**
 * 集合转换为数组
 *
 */
public class CollectionToArrayDemo {
	public static void main(String[] args) {
		Collection<String> c = new ArrayList<>();
		c.add("one");
		c.add("two");
		c.add("three");
		c.add("four");
		System.out.println(c);
		
		String[] arr = c.toArray(new String[c.size()]);
		System.out.println(arr.length);
		System.out.println(Arrays.toString(arr));
	}
}

数组转换为List

  • Arrays类中提供了一个 静态房方法asList ,使用该方法我们可以将一个数组转换为对应的List集合 。
  • 其方法定义为: static <T>List<T> asList<T.. a>
  • 返回的List的集合元素类型由传入的数组的元素类型决定。
  • 并且要注意的是,返回的集合我们不能对其增删元素否则会抛出异常。并且对集合的元索进行修改会影响数组对应的元素。
     
package collection;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 数组转换为集合
 * 使用数组的工具类Arrays的asList方法,可以将一个数组转换为一个List集合。
 * 不支持转换为Set集合,因为Set集合不能放重复元素。
 *
 */
public class ArrayToListDemo {
	public static void main(String[] args) {
		String[] array = {"one","two","three","four"};
		System.out.println("array:"+Arrays.toString(array));
		
		List<String> list = Arrays.asList(array);
		System.out.println("list:"+list);
		/*
		 * 修改集合元素就是修改原数组对应的元素
		 */
		list.set(1,"2");
		System.out.println("list:"+list);
		System.out.println("array:"+Arrays.toString(array));
		//由于数组是定长的,因此该集合不支持增删元素,会抛出异常的。
//		list.add("five");//
		
		//单独创建一个集合,将数组转换的集合元素导入就可以随意增删了
		/*
		 * 所有的集合都支持一个参数为Collection的构造方法,作用是在创建当前集合的
		 * 同时包含给定集合的所有元素。
		 */
		//创建list2集合的同时包含list所有元素
		List<String> list2 = new ArrayList<>(list);
		System.out.println("list2:"+list2);
		list2.add("five");
		System.out.println("list2:"+list2);
	}
}

Collections是集合的工具类

Collection 是顶级接口

List排序

Colletions. sort方法实现排序

Collections是集合的工具类,它提供了很多便于我们操作集合的方法,其中就有用于集合排序的sort方法。
该方法定义为:
void sort(List<T> list)
该方法的作用是对给定的集合元素进行自然排序。

package collection;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

/**
 * 集合的排序
 * 集合的工具类java.util.Collections提供了一个静态方法sort,可以对List集合进行
 * 自然排序,即:从小到大
 *
 */
public class SortListDemo1 {
	public static void main(String[] args) {
		List<Integer> list = new ArrayList<>();
		Random random = new Random();
		for(int i=0;i<10;i++) {
			list.add(random.nextInt(100));
		}
		System.out.println(list);
		
		Collections.sort(list);
		
		System.out.println(list);
		//反转集合,如果是排序后的集合,则变为从大到小
		Collections.reverse(list);
		System.out.println(list);
		
		//打乱集合
		Collections.shuffle(list);
		System.out.println(list);
	}
}

Comparable

Collectionssort方法是对集合元索进行自然排序,那么两个元素对象之间就一定要有大小之分。这个大小之分是如何界定的?实际上,在使用Collectionssort排序的集合元素都必须是Comparable接的实现类,该接口表示其子类是可比较的,因为实现该接口必须重写抽象方法:
- int compareTo(T t);

  • 该方法用于使当前对象与给定对象进行比较。
  1. -若当前对象大于给定对象,那么返回值应为> 0的整数。
  2. -若小于给定对象,那么返回值应为< 0的整数。
  3. -若两个对象相等,则应返回0。
     
package collection;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * 排序自定义元素的集合
 *
 */
public class SortListDemo2 {
	public static void main(String[] args) {
		List<Point> list = new ArrayList<>();
		list.add(new Point(3,4));
		list.add(new Point(8,9));
		list.add(new Point(6,5));
		list.add(new Point(7,2));
		list.add(new Point(1,8));
		System.out.println(list);
		/*
		 * 该sort方法要求集合元素必须实现Comparable,否则编译不通过。
		 * 当我们调用某个API时,该API要求我们为其修改其他额外的代码时,这样的操作
		 * 称为侵入性。修改的代码越多,侵入性越强。侵入性不利于系统架构的健康,不利
		 * 与程序的维护,应当尽量避免。
		 * 
		 */
//		Collections.sort(list);
//		Collections.sort(list, new Comparator<Point>() {
//			public int compare(Point o1, Point o2) {
//				int len1 = o1.getX()*o1.getX()+o1.getY()*o1.getY();
//				int len2 = o2.getX()*o2.getX()+o2.getY()*o2.getY();
//				return len1-len2;
//			}
//		});		
		
		//lambda表达式创建比较器
		Collections.sort(list, (o1,o2)->
			o1.getX()*o1.getX()+o1.getY()*o1.getY()-
			o2.getX()*o2.getX()-o2.getY()*o2.getY()
		);		
		System.out.println(list);
		
	}
}


Comparator

  • 一旦Java类实现了Comparable接口, 其比较逻辑就已经确定;如果希望在排序的操作中临时指定比较规则,可以采用Comparator接口回调的方式。
  • Comparator接要求实现类必须重写其定义的方法:  int compare(T o1,T o2)
  • 该方法的返回值要求:
  1. -若o1> o2则返回值应>0
  2. -若01 < o2则返回值应<0
  3. -若o1= =o2则返回值应为0
package collection;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 排序字符串
 *
 */
public class SortListDemo3 {
	public static void main(String[] args) {
		List<String> list = new ArrayList<>();		
		list.add("苍老师");
		list.add("传奇");
		list.add("小泽老师");
		System.out.println(list);
		/*
		 * String本身实现了Comparable接口,比较规则为按照字符的unicode编码的
		 * 大小比较,先看首字符,如果一样再看第二个字符。
		 * 对于中文而言,几乎没有规律可言。
		 */
//		Collections.sort(list);
		/*
		 * 自定义一个比较器,比较规则为按照字符多少排序,字少的在前,字多的在后
		 */
		Collections.sort(list,(o1,o2)->o1.length()-o2.length());
		System.out.println(list);
	}
}

练习:生成10个随机数(100)以内,按照大小顺序排序.要求自定义有比较规则

package collection;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;

/**
 * 生成10个随机数(100)以内,按照大小顺序排序.要求自定义有比较规则
 *
 */
public class Test {
	public static void main(String[] args) {
		List<Integer> list = new ArrayList<>();
		Random random = new Random();
		for(int i=0;i<10;i++){
			list.add(random.nextInt(100));
		}
		System.out.println(list);
		
		Collections.sort(list, new Comparator<Integer>(){
			public int compare(Integer o1, Integer o2) {
				return 	o2 -o1;
			}
		});
		System.out.println(list);

		Collections.sort(list, (o1,o2)->o2-o1);
		System.out.println(list);
		
	}
}

 

 

 

队列和栈

Queue

 java.util.Queue;接口,队列接口.集成自己的Collection.
常用实现类:java.util.LinkedList;

  •   队列( Queue )是常用的数据结构,可以将队列看成特殊的线性表,队列限制了对线性表的访问方式:只能从线性表的一端添加(offer)元素,从另一端取出(poll)元素。
  • 队列遵循先进先出( FIFO First Input First Output )的原则。
  • JDK中提供了Queue接口,同时使得LinkedList实现了该接口(选择LinkedList实现Queue的原因在于Queue经常要进行添加和删除的操作,而LinkedList在这方面效率较高)

package collection;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;

/**
 * 队列
 * 队列是经典的数据结构,可以保存一组元素,但是保存元素必须遵循先进先出原则.
 * 
 * java.util.Queue;接口,队列接口.集成自己的Collection.
 * 常用实现类:java.util.LinkedList;
 *
 */
public class QueueDemo {

	public static void main(String[] args) {
		Queue<String> queue = new LinkedList<>();
		//入队操作,将元素添加到队列末尾
		queue.offer("one");
		queue.offer("two");
		queue.offer("threee");
		queue.offer("four");
		queue.offer("five");
		queue.offer("six");
		System.out.println(queue);
		
		//poll 出队操作,获取队首元素同时,将其从队列删除
		String str = queue.poll();
		System.out.println(str);
		System.out.println(queue);
		
		//peek 引用队首元素,获取队首元素后该元素不会出队
		str = queue.peek();
		System.out.println(str);
		System.out.println(queue);
		
		System.out.println("size:"+queue.size());
		//迭代器遍历队列后,,元素还在队列
		for(String s : queue){
			System.out.println(s);
		}
		System.out.println(queue);
		
		/*
		 * 用 poll 方法遍历队列,这种操作是一次性的,遍历后队列中就没有元素了
		 */
		
//		while(queue.size()>0){
//			String s = queue.poll();
//			System.out.println(s);
//		}
//		System.out.println(queue);
		
		while(!queue.isEmpty()){
			String s = queue.poll();
			System.out.println(s);
		}
		System.out.println(queue);
		
	
	}

}

Deque 

java.util.Deque 接口.双端队列.Deque接口继承自Qeque接口.双端队列的特点是
 * 队列两端都可以进行出入队操作
 * 实现类:java.util.LinkedList;

  • DequeQueue的子接口, 定义了所谓“双端队列”即从队列的两端分别可以入队( offer )和出队( poll),LinkedList实现了该接口。
  • 如果将Deque限制为只能从端入队和出队,则可实现"栈”( Stack )的数据结构,对于栈而言, 入栈称之为push ,出栈称之为pop。
  • 栈遵循先进后出( FILO First Input Last Output )的原则。

 

package collection;

import java.util.Deque;
import java.util.LinkedList;

/**
 * java.util.Deque 接口.双端队列.Deque接口继承自Qeque接口.双端队列的特点是
 * 队列两端都可以进行出入队操作
 * 实现类:java.util.LinkedList;
 *
 */
public class DequeDemo {
	public static void main(String[] args) {
		Deque<String> deque = new LinkedList<>();
		deque.offer("one");
		deque.offer("two");
		deque.offer("three");
		System.out.println(deque);
		//队首入队
		deque.offerFirst("four");
		System.out.println(deque);
		//队尾入队
		deque.offerLast("five");
		System.out.println(deque);
		
		String str = deque.poll();
		System.out.println(str);
		System.out.println(deque);
		//队尾出队
		str = deque.pollLast();
		System.out.println(str);
		System.out.println(deque);
		//队首出队
		str = deque.pollFirst();
		System.out.println(str);
		System.out.println(deque);
		
		//peekFirst 引用队首元素,获取队首元素后该元素不会出队。
		str = deque.peekFirst();
		System.out.println(str);
		//peekLast 引用队尾元素,获取队首元素后该元素不会出队。
		str =  deque.peekLast();
		System.out.println(str);
	}
}

stack( 栈)

栈( stack )是限定仅在表尾进行插入和删除操作的线性表

进栈 push( )

入栈:在栈顶(数组的尾部)添加指定的元素,并返回新数组的长度。

出栈 pop( )

出栈:删除栈顶(数组的尾部)的一个元素,并返回删除的元素。

 

 

package collection;

import java.util.Deque;
import java.util.LinkedList;

/**
 * 栈结构,栈是经典的数据结构之一,可以保存一组元素,但是存取元素必须遵循先进后出
 * 原则。
 * 使用栈一般是为了实现如:后退,前进这样的功能
 *
 */
public class StackDemo {
	public static void main(String[] args) {
		Deque<String> stack = new LinkedList<>();
		//push(入栈)
		stack.push("one");
		stack.push("two");
		stack.push("three");
		stack.push("four");
		stack.push("five");
		System.out.println(stack);
		//pop (出栈)
		String str = stack.pop();
		System.out.println(str);
		System.out.println(stack);
	}
}

集合操作-Map

Map接口

  • java.util.Map;查找表
  1. Map 体现的样子是一个多行两列的表格,其中左列为Key,右列为value
  2. Map总是根据Key获取对应的value ,因此我们可以将查询的条件作为Key,对应的值作为value保存.
  3. Map有一个要求,Key是不允许重复的.
  • 常用实现类:  HashTable、LinkedHashMap、HashMap、TreeMap
  • java.util.HashMap:散列表,当今查询速度最快的数据结构


Map接口

  • Map接口定义的集合又称查找表,用于存储所谓"Key-Value"映射对。Key可以看 成是Value的索引,作为Key的对象在集合中不可以重复。
  • 根据内部数据结构的不同, Map接有多种实现类,其中常用的有内部为hash表实现的HashMap和内部为排序二叉树实现的TreeMap

put( )方法

Map接口中定义了向Map中存放元素的put方法:
一  V put(K key, V value)
Key-Value对存入Map ,如果在集合中已经包含该Key ,则操作将替换该Key所对应的Value,返回值为该Key原来所对应的Value (如果没有则返回null )。
 

package map;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;


public class MapDemo {
	public static void main(String[] args) {
		Map<String,Integer> map = new HashMap<>();
		/*
		 * V put(K k,V v);
		 * 将给定的key,value 保存到Map中
		 * 由于Map要求Key不允许重复,因此如果使用Map中已有key保存value,则是替换
		 * value 操作,此时put 方法返回为就是被替换的value,否则返回值为null.
		 */
		map.put("语文", 99);
		map.put("数学", 98);
		map.put("英语", 97);
		map.put("物理", 96);
		Integer num = map.put("化学", 99);
		System.out.println(map);
		System.out.println(num);
		
		num = map.put("数学", 77);//数学作为key 已经存在,因此77会替换原来的98
		System.out.println(map);
		System.out.println(num);//98
		
		
	}
}

get( )方法

Map接中定义了从Map中获取元素的get方法:
- V get(Object key)
返回参数key所对应的Value对象,如果不存在则返回null

package map;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class MapDemo {
	public static void main(String[] args) {
		Map<String,Integer> map = new HashMap<>();
		
		/*
		 * V get(Object key)
		 * 根据给定的key获取对应的value ,若是给定的key不存在,则返回值为null
		 */
		//获取语文的成绩?
		num = map.get("语文");
		System.out.println(num);//98
		
		num = map.get("体育");
		System.out.println(num);//null
		
		
	}
}


containsKey ( )方法

Map接口中定义了判断某个key是否在Map中存在
-- boolean containsKey(Object key);
若Map中包含给定的key则返回true ,否则返回false
 

package map;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;


public class MapDemo {
	public static void main(String[] args) {
		Map<String,Integer> map = new HashMap<>();
		
		 boolean has = map.containsKey("数学");
         System.out.println("是否有数学科目:"+ has); //true
		
	}
}

size()

获取当前Map元素个数

//获取当前Map元素个数
int size = map.size();
System.out.println("size:"+size);

remove(Object key)

删除给定的key 所对应的这组键值对,返回值为该key对应的value.

num = map.remove("语文");
System.out.println(map);//{物理=96, 数学=77, 化学=99, 英语=97}

System.out.println(num);//99


HashMap

哈希表
那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?答案是肯定的,这就是我们要提起的哈希表。哈希表((Hash table)既满足了数据的查找方便,同时不占用太多的内容空间,使用也十分方便。

哈希表有多种不同的实现方法,我接下来解释的是最常用的一种方法—— 拉链法,我们可以理解为“链表的数组” .
 

HashMap的存储结构

  • HashMap底层是以数组方式进行存储的。将key-value键值对作为数组的一个元素进行存储。
  • Key-value都是Map.Entry中的属性。其中将key的值进行hash之后进行存储,即每一个key都是计算hash值,然后再存储。每一个hash值对应一个数组下标,数组下标是根据hash值和数组长度计算得来的。
  • 由于不同的key值可能具有相同的hash值,即一个数组的某个位置出现两个相同的元素,对于这种情况,hashmap采用链表的形式进行存储.

Hash表原理


hashCode方法

  • HashMap的原理中我们可以看到, keyhashCode()方法的返回值对HashMap存储元素时会起着很重要的作用。而hashCode()方法实际上是在Object中定义的。那么应当妥善重写该方法:
  • 对于重写了equals方法的对象, -般要妥善的重写继承自Object类的hashCode方法( Object提供的hashCode方法将返回该对象所在内存地址的整数形式)
  • 重写hashCode方法是需注意两点:其一、与equals方法的一致性,即equals比较返回true的两个对象其hashCode方法返回值应该相同;其二、hashCode返回的数值应符合hash算法的要求,试想如果有很多对象的hashCode方法返回值都相同,则会大大降低hash表的效率,一般情况下可以使用IDE (如Eclipse )提供的工具自动生成hashCode方法。

重写hashCode方法

public int hashCode() {
    finalint prime = 31;
    int result = 1;
    result = prime * result + age;
    result = prime * result + ((gender == nul)? 0:
    gender.hashCodeO);
    result = prime * result + ((name== nul) ? 0 :
    name.hashCode();
    long temp;
    Temp = Double.doubleToLongBits(salary);
    result = prime * result +(int) (temp ^ (temp>>> 32));
    return result;
}

装在因子及HashMap优化

  • Capacity :容量,hash表里bucket(桶)的数量,也就是散列数组大小。
  • Initial capacity :初始容量,创建hash表时,初始bucket的数量,默认构建容量是16.也可以使用特定容量。
  • Size :大小,当前散列表中存储数据的数量。
  • Load factor :加载因子,默认值0.75(就是75%),当向散列表增加数据时如果size/capacity的值大于Load factor则发生扩容并且重新散列(rehash)。
  • 性能优化:加载因子较小时,散列查找性能会提高,同时也浪费了散列桶空间容量。0.75是性能和空间相对平衡结果。在创建散列表时指定合理容量减少rehash提高性能。
     


Map的遍历

  • Map提供了三种遍历方式:
  1. 遍历所有的Key
  2. 遍历所有的Key-Value对
  3. 遍历所有的Value(不常用)

建议:使用keysetentryset来进行遍历能取得相同的结果,但两者的遍历速度是有差别的,keySet( )的速度比entrySet( )慢了很多,也就是keySet方式遍历Map的性能不如entrySet性能好,为了提高性能,以后多考虑用entrySet( )方式来进行遍历。

 源码比对:

public V get(Object key) {|
    if (key == nu11)
        return getForNullKey();
    Entry<K,V> entry = getEntry(key);

    return nu11 = entry ? nu1l : entry. getValue();
}

final Entry<K, V> getEntrykobject key) {
    if (size == 0) {
        return nu11;
    }
    int hash = (key = nu11) ? θ : hash(key);
    for (Entry<K,V> e = table[indexFor(hash, table. 1ength)];
        e != nu1l;
        e = e.next) {http://blog. csdn. net/
        object k;
        if (e.hash == hash 8&&
            ((k = e.key) == key | (key != nu11 && key. equals()))
            return e;
        }

        return nu1l ;
}

使用keySet( )方法

遍历所有Key的方法:
-Set<K>  keySet ( )
该方法会将当前Map中所有的key存入-个Set集合后返回。

将Map中所有的key存放到Set集合中。应为Set集合有迭代器,可以通过迭代器循环key,在通过get()方法,得到每个key所对应的value;

   /*
    *Set keySet()
    * 将当前Map中所有的key以一个Set集合形式返回,遍历该集合等于遍历所有key
    */
    Set<String> keySet = map.keySet();
    for(String key: keySet){
	    System.out.println("key:"+key);
    }


使用entrySet( )方法

遍历所有的键值对的方法:
- Set <Entry <K,V>>  entrySet( )
该方法会将当前Map中每一组key-value对封装为一个Entry对象并存入一个Set集合后返回。

java.util.Map.Entry
Entry的每一个实例表示Map的一组键值对,其中提供了两个常用方法:
getKey getValue 分别用来获取 对应key 和value

Set<Map.Entry<K,V>> entrySet() //返回此映射中包含的映射关系的 Set 视图。 Map.Entry表示映射关系。entrySet():迭代后可以e.getKey(),e.getValue()取key和value。返回的是Entry接口 。

/*
* 遍历每一组键值对
* Set<Entry> entrySet();
* 
* java.util.Map.Entry
* Entry的每一个实例表示Map的一组键值对,其中提供了两个常用方法:
* getKey 和 getValue 分别用来获取 对应key 和value
*/
		Set<Entry<String,Integer>> entrySet = map.entrySet();
		for(Entry<String,Integer> e : entrySet){
			String key = e.getKey();
			Integer value = e.getValue();
			System.out.println(key+":"+value);
		}

使用Collection values();方法

遍历所有value,该方法会将当前Map中所有的value以一个集合形式返回

获取集合中的所有的value值,也就是获取集合中的所有的值----没有键,没有对应关系,

Collection<Integer> values = map.values();
		for(Integer value: values){
			System.out.println(value);
}
		


有序的Map


LinkedHashMap实现有序Map

  • 使用Map接口的哈希表和链表实现,具有可预知的迭代顺序。此实现与HashMap的不同之处在于:
  • LinkedHashMap维护着一个双向循环链表。此链表定义了迭代顺序,该迭代顺序通常就是存放元素的顺序。
  • 需要注意的是,如果在Map中重新存入已有的key,那么key的位置不会发生改变,只是将value值替换。
     

统计各点PM2.5最大值(要求顺序)

public void testLinkedHashMap(
    String[ positions, int] datas) {
    LinkedHashMap <String,Integer> map 
                            = new LinkedHashMap < String,Integer> 0;
    for(int i=0; i<positions.length; i++){
        Integer data = map.get(positions[i]);
        int max = data==null ? 0 : data;
        max = max> datas[] ? max:datas[i];
        map.put(positions[i], max);
    }
    System.out.println(map);
}

猜你喜欢

转载自blog.csdn.net/c202003/article/details/106676577
今日推荐