Java集合知识点详细概括加代码解释

集合Collection (List And Set)

1.在实际开发中,需要将使用的对象存储于特定的数据结构容器中。JDK提供了这样的容器-集合(Collection)。

2.Collection 是一个接口,其子接口有:List和Set

3.List和Set的区别:List是有序可重复集(可以存储重复元素),Set是无序不可重复集(不能存储重复元素)。元素是否重复取决于元素的equals()比较结果。

集合持有对象的引用

集合中存储的都是引用类型元素,并且集合只保存每个元素的引用,而非将元素本身存入集合。基本类型数据会自动装箱为Integer类型数据。

Collection 中的常用方法

add()方法

1.Collection 定义了一个add方法用于向集合中添加新元素

		-boolean add(E e) E表示的是数据类型
该方法会将给定的元素加进集合 若添加成功则返回true,否则返回false

演示代码如下:

public void testAdd() {
	Collection<String> c = new ArrayList<String>();
	System.out.println(c);//[]
	c.add("a");
	c.add("b");
	c.add("c");
	System.out.println(c);//[a,b,c]
}

contains()方法

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

演示代码如下:

public void  testContains() {
	Collection<Person> persons = new ArrayList<Person>();
	persons.add(new Person("张三",20));
	persons.add(new Person("李四",21));
	persons.add(new Person("王五",22));
	persons.add(new Person("赵六",23));
	Person p =new Person("李四",21);
	//List集合contains方法和对象的equals方法有关
	boolean flag = persons.contains(p);
	//如果Person类不重写equals方法将为false
	System.out.println(flag);//true
}

int size(),void clear,boolean isEmpty

-int size()返回当前集合中元素的总数
-void clear() 该方法清空当前集合
-boolean isEmpty()该方法用于判断集合是否为空

演示代码如下:

public void testSizeAndClearAndIsEmpty() {
	Collection<String> c = new HashSet<String>();
	System.out.println(c.isEmpty());//true
	c.add("Java");
	c.add("C");
	c.add("php");
	c.add("C#");
	c.add("Java");
	System.out.println(c);//无序不可重复:[C#, Java, C, php]
	System.out.println("isEmpty:"+c.isEmpty()+",size:"+c.size());//false  4
	c.clear();
	System.out.println("isEmpty:"+c.isEmpty()+",size:"+c.size());//true 0
}

addAll()方法和containsAll()方法

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

演示代码如下:

public void testAddAllAndContainsAll() {
	Collection <String >c1 = new ArrayList<String>();
	c1.add("java");
	c1.add("c");
	c1.add("php");
	c1.add("c#");
	c1.add("js");
	System.out.println(c1);//java c php c# js
	Collection <String> c2 = new HashSet<String>();
	c2.addAll(c1);
	System.out.println(c2);//无序 java php c js c#
	Collection <String>c3= new ArrayList<String>();
	c3.add("java");
	c3.add("c");
	System.out.println(c1.containsAll(c3));//true
}
}

迭代器 Iterator

迭代器用于遍历集合元素,获取迭代器可以使用Collection定义的方法:

Iterator <E> it = 集合名.iterator();

迭代器是一个接口,集合在重写了Collection的iterator()方法时利用内部类提供了迭代器的实现。

Iterator提供了统一的遍历集合的方式,其提供了用于遍历集合的两种方法:

boolean hasNext():判断集合是否还有元素可以遍历
E next():返回迭代的下一个元素

演示代码如下:

public void testHashNextAndNext() {
	Collection<String> c = new HashSet<String> ();
	c.add("java");
	c.add("c");
	c.add("c#");
	c.add("php");
	c.add("python");
	//获取迭代器
	Iterator<String> it = c.iterator();
	//判断是否有元素 然后遍历
	while(it.hasNext()) {
		//返回元素
		String s = it.next();
		System.out.println(s);
	}
}

remove()方法

在使用迭代器遍历集合时,不能通过集合的remove()删除集合中的元素,否则会抛出并发更改异常。

void remove()
1.我们通常使用迭代器自身提供的remove()方法来删除通过next()方法迭代出来的元素。
2.迭代器的删除方法是在原集合中删除元素。
3.这里需要注意的是:在调用remove方法前必须通过迭代器的next方法迭代过的元素,
那么删除的就是这个元素,并且不能再次调用remove方法,除非再次调用next方法后
方可调用remove()方法

演示代码如下:

public void testRemove() {
	Collection<String> c = new HashSet<String>();
	c.add("java");
	c.add("C");
	c.add("C#");
	c.add("PHP");
	c.add("python");
	System.out.println(c);
	Iterator<String> it = c.iterator();
	while(it.hasNext()) {
		String s = it.next();
		if(s.indexOf("C")!= -1) {  //不等于-1 是找到 等于-1没有找到
			it.remove();//删除包含字母C的元素
		}
	}
	System.out.println(c);//python java php 把包含C字母的元素全部删除
}

增强for循环

java5.0版本之后推出了一个新特性,增强for循环,也称为新循环,该循环不同于传统的循环工作,只用于遍历数组和集合。

语法:

for (元素类型 e:集合或者数组){
	循环体;
}
新循环并非新语法,而是在编译过程中,编译器会将新循环转化为迭代器模式
所以可以说新循环的本质是迭代器

演示代码如下:

	public void testFor() {
		Collection<String> c = new HashSet<String>();
		c.add("java");
		c.add("c");
		c.add("php");
		c.add("c#");
		c.add("python");
		for (String string : c) {
			System.out.println(string.toUpperCase()+" ");
			//C# PYTHON JAVA C PHP
		}
}

泛型机制

1.泛型机制是JavaSE 5.0引入的特性,泛型的本质是参数化类型,在类 接口 和方法的定义过程中,所操作的数据类型被传入的参数指定。

2.Java泛型机制广泛的应用在集合框架中,所有的集合类型都带有泛型参数,这样在创建集合时可以指定放入集合中元素的类型,java编译器可以据此类型进行检查,这样就可以减少代码在运行时出现的错误可能性。

3.ArrayList类的定义中,中的E为泛型参数,在创建对象时可以将类型作为参数传递,此时类定义所有的E将被替换成传入的参数。

演示代码如下:

public void testE() {
	ArrayList<String> list = new ArrayList<String>();
	list.add("One");//只能添加String类型的数据
}

集合操作-线性表

List

1.List接口是Collection的子接口,用于定义线性表数据结构,可以将List理解为存放对象的数组,只不过元素个数可以动态的增加或者减少。

2.List接口的两个常见实现类为ArrayList和LinkedList分别用动态数组和链表的方式实现了List接口

3.可以认为ArrayList和LinkedList的方法在逻辑上完全一样,只是在性能上有一定的差别。ArrayList更适合于随机访问,LinkedList更适合插入和删除。

常用方法

List除了继承Collection定义的方法外,还根据其线性表的数据结构定义了一系列的方法,其中最常用的就是基于下标的get和set方法。

E get(int index) 获取集合中指定下标对应的元素,下标从0开始
E set(int index,E element)将指定的元素存入指定的位置,并将原位置元素返回

演示代码如下:

public void testGetAndSet() {
	List<String> list = new ArrayList <String>();
	list.add("java");
	list.add("c++");
	list.add("php");
	list.add("python");
	list.add("c#");
	//get方法遍历List
	for (int i = 0; i < list.size(); i++) {
		System.out.println(list.get(i).toUpperCase());
		//JAVA C++ PHP PYTHON C#
	}
	String value = list.set(1, "hadoop");
	System.out.println(value);//c++
	System.out.println(list);//java hadoop php python c#
	//先拿到hadoop 将 hadoop插入索引3的位置 把python 返回,然后再把python插入索引1的位置
	list.set(1, list.set(3, list.get(1)));
	System.out.println(list);
	//java python php hadoop c#
}

根据下标的操作进行插入和删除

void add(int index,E element)将给定的元素插入到指定位置,原位置以及后续
元素都顺序向后移动
E remove(int index):删除给定位置的元素,并将被删除的元素返回

演示代码如下:

public void testInsertAndRemove() {
	List<String> list = new ArrayList<String>();
	list.add("java");
	list.add("c++");
	System.out.println(list);//java c++
	
	list.add(1,"cpp");
	System.out.println(list);//java cpp c++
	
	String a = list.remove(2);
	System.out.println(list+".."+a);//java cpp...c++
}

获取子List(sublist)

注意sublist获取的List与原List占用相同存储空间,对子List的操作会影响原List
List<E>sublist(int fromIndex,int toIndex) fromIndex和toIndex是截取子
List的首尾下标,前包后不包。

演示代码如下:

public void testSubList() {
	List<Integer> list = new ArrayList<Integer>();
	for (int i = 0; i < 10; i++) {
		list.add(i);
	}
	System.out.println(list);//0-9
	
	List<Integer> sublist = list.subList(3, 8);//3 4 5 6 7
	//subList获得的List和源List占有相同的资源
	for (int i = 0; i < sublist.size(); i++) {
		sublist.set(i, sublist.get(i)*10);
	}
	System.out.println(sublist);//30 40 50 60 70
	//用的是同一块 存储空间 对子List操作会影响原List
	System.out.println(list);// 0 1 2 30 40 50 60 70 8 9
	//注意clear只能删除连续元素
	list.subList(3, 8).clear();
	System.out.println(list);// 0 1 2  8 9
}

List与数组之间的转化

1.List的toArray方法用于将集合转化为数组,但实际上该方法是在collection的接口中定义的,所以所有的集合都具备这个功能。

Object[] toArray()
<T> T[] toArray(T[] a)
其中第二个方法是比较常用的,我们可以传入一个指定的类型的数组,该数组元素类型
一致。返回值是转换后的数组,该数组会保留集合中的所有元素

演示代码如下:

public void testToArray() {
	List<String> list = new ArrayList<String>();
	list.add("a");
	list.add("b");
	list.add("c");
	String [] strArr = list.toArray(new String[] {});
	System.out.println(Arrays.toString(strArr));//[a,b,c]
}

2.Arrays数组类中提供了一个静态方法asList 使用该方法我们可以将一个数组转化为对应的List集合

static <T> List<T> asList<T...a>
返回的List集合元素类型由传入的数组的元素类型决定
返回的集合我们不能对其增删元素,否则会抛出异常,并且对集合的元素进行修改
会影响数组对应的元素。

演示代码如下:

public void testArrayToList() {
	String [] strArr = {"a","b","c"};
	List<String> list = Arrays.asList(strArr);
	System.out.println(list);
	//list.add("d");//抛出异常
	//System.out.println(list);
	List<String> list1 = new ArrayList<String>();
	list1.addAll(Arrays.asList(strArr));
	list1.add("d");
	System.out.println(list1);
	/*
	 * 数组转化为List是不能增删的 如果需要增删 可以利用空集合调用addAll方法将转换后的集合全部添加 新集合
	 * 	就可以进行操作了。
	 */
}

List排序

1.Collections是集合的工具类,它提供了很多便于我们操作集合的方法,其中就有用于集合排序的sort方法

该方法定义为:
-void sort(List<T> list)
该方法的作用是对给定的集合元素进行自然排序

演示代码如下:

	public void testSort() {
	List<Integer> list = new ArrayList<Integer>();
	Random r = new Random();
	for (int i = 0; i <10;i++) {
		list.add(r.nextInt(100));
	}
	System.out.println(list);//[7, 77, 94, 15, 6, 83, 35, 30, 24, 49]
	Collections.sort(list);//从小到大排序
	System.out.println(list);//[6, 7, 15, 24, 30, 35, 49, 77, 83, 94]
}

Comparable

Collections的sort方法是对集合的自然排序,那么两个元素对象之间就一定要分大小,这个大小之分是如何判断的呢?实际上在使用Collections的sort排序的集合元素都必须是Comparable接口的实现类。

该接口表示子类是可以比较的,因为实现接口必须重写抽象方法

int compareTo(T t);
该方法用于使当前对象与给定对象进行比较
若当前对象大于给定对象 返回值为大于0的整数
若当前对象小于给定对象	返回值为小于0的整数
若两个对象相等        则返回0

演示代码如下:

public void testComparable() {
	/*
	 * Cell实现了Comparable 接口
	 * compareTo 方法逻辑为 按照x值大小进行排序
	 */
	List<Cell> cells = new ArrayList<Cell>();
	cells.add(new Cell(2,3));
	cells.add(new Cell(5,1));
	cells.add(new Cell(3,2));
	Collections.sort(cells);
	System.out.println(cells);
	////2,3   3,2   5,1
}

Comparator

1.一旦Java类实现了Comparable接口,其比较逻辑就已经确定,如果希望在排序的操作中,临时指定比较规则,可以采用Comparator接口回调的方式
2.Comparator 接口要求实现类必须重写其定义的方法:

int compare(T o1,T o2)
若o1>o2 方法的返回值应大于0
若o1<o2 方法的返回值应小于0
若o1=o2则返回0

演示代码如下:

	public void testComparator() {
	List<Cell> cells = new ArrayList<Cell>();
	cells.add(new Cell(2,3));
	cells.add(new Cell(5,1));
	cells.add(new Cell(3,2));
	//临时按照y排序 this.y-o.y
	Collections.sort(cells, new Comparator<Cell>() {
		@Override
		public int compare(Cell o1, Cell o2) {
			// TODO Auto-generated method stub
			return o1.y-o2.y;
		}
	});
	System.out.println(cells);//5,1 3,2 2,3
	//临时回调
	Collections.sort(cells);
	System.out.println(cells);//2,3 3,2 5,1
}

比较总结

1.对于集合比较实用Collection.sort()

2.对于集合中的对象比较,需要指定比较逻辑,需要实现Comparable接口并重写compareTo方法自定义逻辑

3.对于需要临时改变比较规则,需要使用Collections.sort(List,Comparator)采用回调方式重写Comparator接口的compare方法自定义逻辑。

Queue和Deque

Queue

1.队列Queue是常用的数据结构,可以将队列看成特殊的线性表,队列限制了对线性表的访问方式,只能从线性表的一端添加元素(offer)从另一端取出(poll)元素

2.队列遵循先进先出的原则

3.JDK提供了Queue接口,同时使得LinkedList实现了该接口,选择LinkedList实现Queue接口的原因在于Queue经常要进行添加和删除的操作,而LinkedList在这方面效率比较高。

4.Queue接口主要方法如下:

boolean offer(E e):将一个对象添加至队尾,如果添加成功则返回true
E poll():从队首删除并返回一个元素
E peek():返回队首元素但是不删除

演示如下代码:

public void testQueue() {
	Queue<String> queue = new LinkedList<String>();
	queue.add("a");
	queue.offer("b");
	queue.offer("c");
	System.out.println(queue);//a b c
	String str = queue.peek();
	System.out.println(str);//a
	String str1 = queue.poll();
	System.out.println(str1);//a
	System.out.println(queue);//b c
	
	while(!queue.isEmpty()) {
		str = queue.poll();
		//遍历只能用删除队首 poll方法 删除后队首元素会变为第二个元素 而peek不会改变队首元素
		System.out.println(str+" ");//b c
	}
}

Deque

1.Deque是Queue的子接口,定义了所谓的 双向队列。即从队列的两端分别可以入队(offer)和出队(poll)。LinkedList实现了该接口

2.如果将Deque限制为只能从一端入队和出队,则可实现 栈 Stack 的数据接口
入栈称为push 出栈称为pop

3.栈遵循先进后出的原则

public void testStack() {
	Deque<String> stack = new LinkedList<String>();
	stack.push("a");
	stack.push("b");
	stack.push("c");
	System.out.println(stack);//c b a
	String s = stack.peek();
	System.out.println(s);//c
	while(!stack.isEmpty()) {
		 s = stack.pop();
		 System.out.println(s+" ");// c  b  a
	}
	System.out.println(stack);//[]
}
}

Map接口

1.Map接口定义的集合又称之为查找表,用于存储所谓的"Key-Value"映射对。Key可以看成是Value的索引,作为key的对象在集合中不可以重复。

2.根据内部数据结构不同,Map接口有多种实现类,其中常用的有内部为hash表实现的HashMap和内部为排序二叉树实现的TreeMap

put()方法

1.Map接口中定义了向Map中存放元素的put方法

V put(K key,V value)

2.将key-value对存入Map,如果在集合中已经包含了该Key,则操作将替换该key所对应的value,返回值为该key原来所对应的value,如果没有包含该Key返回null。
演示代码如下:

//创建成员变量
Map<String,Person> persons = new HashMap<String,Person>();
@Before //表示在每执行一次test就执行一次before
public void testPut() {
	persons.put("张三", new Person("张三",80));
	persons.put("闫伟", new Person("闫伟",81));
	//System.out.println(persons);
}

3.Map接口中定义了从Map中获取元素的get方法

V get(Object key)返回参数key所对应的value对象,如果不存在则返回null

演示代码如下:

@Test
public void testGet() {
	Person  p = persons.get("张三");
	System.out.println(p);//Person [name=张三, age=80]
}

containsKey()方法

Map接口中定义了判断某个key是否在Map中存在

boolean containsKey(Object key)
若Map中包含给定的key则返回true

演示代码如下:

@Test
public void testContainsKey() {
	System.out.println(persons.containsKey("闫伟"));//true
}

hashCode方法

1.从hashMap的原理中我们可以看到,key的hashCode()方法的返回值对HashMap存储元素时会起着
非常重要的作用,而hashCode()方法实际上是在Object中定义的

2.对于重写了equals方法的对象,一般要妥善的重写继承自Object类的hashCode方法
(Object提供的hashCode方法 将返回该对象所在内存地址的整数形式)

3.重写hashCode方法应注意两点:

 		-与equals方法的一致性,即equals比较返回true的两个对象其hashCode方法返回值应该相同
 		-hashCode返回的数值应符合hash算法的要求,如果有很多对象的hashCode返回值都相同 则会大大降低
  			hash表的效率,一般情况下使用IDE eclipse提供的工具自动生成hashCode方法

装载因子及hashMap优化

1.Capacity:容量,hash表里bucket的数量 也就是散列数组大小

2.Inintal capacity:初始容量,创建hash表时,初始bucket的数量,默认构建容量是16
也可以使用特定的容量

3.Size:大小,当前散列表中存储数据的数量

4.Load factor:加载因子 默认值为0.75,当向散列表增加数据时 如果size/capacity的值大于factor则扩容
并且重新散列(rehash)

5.性能优化:加载因子较小时 散列查找性能会提高,同时也浪费了散列桶的容量。0.75是性能和空间相对平衡结果
在创建散列表时 指定合理的容量,减少rehash提高性能

Map遍历

1.Map提供了三种遍历方式

遍历所有的key
遍历所有的key-value对
遍历所有的value---不常用

2.遍历所有key的方法

Set<K> keySet()
该方法返回当前Map中所有Key存入一个Set集合

演示代码如下:

@Test
public void testKeySet() {
	Set<String> keyset = persons.keySet();
	System.out.println(keyset);//[张三, 闫伟]
	for (String string : keyset) {
		System.out.println("key:"+string);
		/*
		 * key:张三
		 * key:闫伟
		 */
	}
}

entrySet()方法 遍历所有键值对

Set<Entry<k,v>> entrySet()
该方法将当前Map中的每一组key-value对封装为一个Entry对象并存入一个
Set集合返回

演示代码如下:

public void testEntrySet() {
	Set<Entry<String,Person>> entrySet = persons.entrySet();
	//[张三=Person [name=张三, age=80], 闫伟=Person [name=闫伟, age=81]]
	System.out.println(entrySet);
	
	for (Entry<String, Person> entry : entrySet) {
		System.out.println(entry.getKey()+":"+entry.getValue());
		/*
		 * 张三:Person [name=张三, age=80]
		 * 闫伟:Person [name=闫伟, age=81]
		 */
	}
}

有序的Map

1.使用Map接口的哈希表和链表实现,具有可预知迭代顺序,此实现与HashMap的不同之处在于:
LinkedHashMap维护着一个双向循环链表,此链表定义了迭代顺序,该迭代顺序通常就是存放元素的顺序。

2.需要注意,如果在Map中重新存入已有Key,那么key位置不会发生改变,改变的是value值。

演示代码如下:

@Test
public void testLinkedHashMap() {
	LinkedHashMap<String,Person> map = new LinkedHashMap<String,Person>();
	map.put("李志豪", new Person("李志豪",99));
	map.put("李四", new Person("李四",18));
	System.out.println(map);
	//{李志豪=Person [name=李志豪, age=99], 李四=Person [name=李四, age=18]}
	map.put("李四", new Person("李四",66));
	System.out.println(map);
	//{李志豪=Person [name=李志豪, age=99], 李四=Person [name=李四, age=66]}
}

猜你喜欢

转载自blog.csdn.net/qq_38118222/article/details/89298699