集合的介绍(2)

Set

  • SetJava中的集合类,是一个继承自Collection接口,提供了一种无顺序,不重复的集合。常用的子类包括HashSet, TreeSet(不能填入null)等。
// 下面举 HashSet 为例子:
package chapter06;

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

public class Collection_HashSet {
public static void main(String[] args) {

// TODO 集合 - Collection - Set(底层是 数组 + 链表)
// HashSet : Hash + Set
// Hash : 哈希算法,散列算法 - 导致生成的set是一个无序且不重复的数组
// ArrayList : 数组
// LinkedList :
HashSet set = new HashSet();
// TODo 增加数据
set.add("zhangsan");
set.add("zhangsan");
set.add("lisi");
set.add("wangwu");

// TODo 修改数据

// TODo 删除数据
//set.remove("wangwu");

// TODo 查询数据
for (Object o : set) {
System.out.println(o); // 只能遍历输出,无法通过下标获取,因为这个是无序的
}

System.out.println(set); // [lisi, zhangsan, wangwu] 只有一个zhangsan且不按插入顺序来排列

// 二 . 常用方法
HashSet set1 = new HashSet();

ArrayList list = new ArrayList();
list.add("zhangsan");
list.add("lisi");
list.add("wamngwu");
// 1. .addAll(collection) - (将其他集合插入)
set1.addAll(list);
System.out.println(set1); // [lisi, wamngwu, zhangsan]
// 2. .toArray() - (将集合变成数组)
Object[] objects = set.toArray();
System.out.println(objects); // [Ljava.lang.Object;@4eec7777,成功转化为数组
// 3. .isEmpty() - (判断数组是否为空)
System.out.println(set.isEmpty()); // false
// 4. .clear() - (清空set)
// set.clear();
// 5. .contains() - (是否包含某一项)
System.out.println(set.contains("zhangsan")); // true
// 6. .size() - (获取set的长度)
System.out.println(set.size()); // 3
// 7. .clone() - (复制set)
Object clone = set.clone();
System.out.println(clone); // [zhangsan, lisi, wangwu] 成功复制

// 三 . 重复数据
/*
* 在java中存在多中数据类型,上面提到的是基本数据类型,也就是变量赋值中的内存保存的
* 就是变量的值,如int等,在内存中保存的就是这个值,因此可以很简单的就是用set进行去重
* 操作,但是我们在实际开发中,使用的大多数是对象的形式来保存数据的,在这里因为数据是引用
* 数据类型,在内存中保存的是这个对象的内存只想,由此可见,即便两个对象的所有属性和方法都是
* 一样的,此时插入set中是不能进行去重操作的,因为两个对象是两个数据,它们的内存指向是不一样的,
* 在set中,对数据的去重操作首先是查询它的hashcode查看是否一样,如果一样在进行.equals()对比
* 两个条件均为一样set才会认为这两个对象是一样的,进行去重操作
* */

// 这里我设置一个class类
class User{
public int id;
public String name;

@Override
// 类似于内存地址,修改对象的hashCode方法,使得new出来的对象的hashcode = id
public int hashCode() {
return id;
}

@Override
// 判断两个对象的属性是否完全相同 , 重写equals方法,变成判断对象的属性
public boolean equals(Object obj) {
if ( obj instanceof User ) {
User otherUser = (User)obj;
if ( otherUser.id == this.id && otherUser.name.equals(this.name) ) {
return true;
}
return false;
} else {
return false;
}
}

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

HashSet set2 = new HashSet();

// new 3 个对象,其中两个保持属性一致
User user1 = new User();
user1.id = 1001;
user1.name = "zhangsan";
System.out.println(user1.hashCode()); // 1001 (hashCode被我们复写了)

User user2 = new User();
user2.id = 1001;
user2.name = "zhangsan";
System.out.println(user2.hashCode()); // 1001 (hashCode被我们复写了)

User user3 = new User();
user3.id = 1002;
user3.name = "lisi";

set2.add(user1);
set2.add(user2);
set2.add(user3);

System.out.println(set2); // 此时就仅输出 : [User[1001, zhangsan], User[1002, lisi]],两个元素
}
}

Queue(队列)

  • 队列是一种特殊的线性表,遵循先入先出、后入后出的基本原则,一般来说,它只允许在表的前端进行删除操作,而在表的后端进行插入操作,但是java的某些队列运行在任何地方插入删除;比如我们常用的 LinkedList 集合,它实现了Queue 接口,因此,我们可以理解为 LinkedList 就是一个队列;

image

阻塞和非阻塞

阻塞队列

  • 入列(添加元素)时,如果元素数量超过队列总数,会进行等待(阻塞),待队列的中的元素出列后,元素数量未超过队列总数时,就会解除阻塞状态,进而可以继续入列;
  • 出列(删除元素)时,如果队列为空的情况下,也会进行等待(阻塞),待队列有值的时候即会解除阻塞状态,进而继续出列;
  • 阻塞队列的好处是可以防止队列容器溢出;只要满了就会进行阻塞等待;也就不存在溢出的情况;只要是阻塞队列,都是线程安全的;

非阻塞队列

  • 不管出列还是入列,都不会进行阻塞,
  • 入列时,如果元素数量超过队列总数,则会抛出异常,
  • 出列时,如果队列为空,则取出空值;

一般情况下,非阻塞式队列使用的比较少,一般都用阻塞式的对象比较多;阻塞和非阻塞队列在使用上的最大区别就是阻塞队列提供了以下2个方法:

  • 出队阻塞方法 : take()
  • 入队阻塞方法 : put()

有界和无界

有界:有界限,大小长度受限制
无界:无限大小,其实说是无限大小,其实是有界限的,只不过超过界限时就会进行扩容,就行ArrayList 一样,在内部动态扩容

package chapter06;

import java.util.concurrent.ArrayBlockingQueue;

public class Collection_Queue {
public static void main(String[] args) throws Exception {

// TODO 集合 - Collection - Queue
// ArrayBlockingQueue : Array + Blocking(阻塞,堵住) + Queue
ArrayBlockingQueue queue = new ArrayBlockingQueue(3); // 构建一个长度为3的队列

// add方法如果增加数据增加不了,直接发生错误怕(不存在堵塞)。
// queue.add("zhangsan");
// queue.add("lisi");
// queue.add("wangwu");
// queue.add("zhaoliu"); // 发生错误,Queue full

// put方法如果增加数据增加不了,程序会卡在最后一个环节不继续往下运行(存在堵塞)。
// queue.put("zhangsan");
// System.out.println("第一个人挂号");
// queue.put("lisi");
// System.out.println("第二个人挂号");
// queue.put("wangwu");
// System.out.println("第三个人挂号");
// queue.put("zhaoliu");
// System.out.println("第四个人挂号");

// offer方法同样可以增加数据,但是返回值为布尔类型,郑家成功返回true,失败返回false(不存在堵塞)
boolean zhangsan = queue.offer("zhangsan");
System.out.println(zhangsan);
boolean lisi = queue.offer("lisi");
System.out.println(lisi);
boolean wangwu = queue.offer("wangwu");
System.out.println(wangwu);
boolean zhaoliu = queue.offer("zhaoliu");
System.out.println(zhaoliu);

// poll用于拿取数据,每拿一个,队列中就减少一个,直到队列为空,此时继续拿的话就会返回null(不存在堵塞)
System.out.println(queue.poll());// 拿出第一个 zhangsan 此时队列中的数据为 [lisi,wangwu]
System.out.println(queue.poll());// 拿出第二个 lisi 此时队列中的数据为 [wangwu]
System.out.println(queue.poll());// 拿出第三个 wangwu 此时队列中的数据为 []
System.out.println(queue.poll());// 此时队列以及为空,返回null

// take用于拿取数据,每拿一个,队列中就减少一个,直到队列为空,此时继续拿的话就会卡住程序的运行(存在堵塞)
// System.out.println(queue.take());
// System.out.println(queue.take());
// System.out.println(queue.take());
// System.out.println(queue.take());

// 当然,队列也是属于集合,同样可以使用以下的这些方法
// queue.size()
// queue.isEmpty()
// queue.clear();
// queue.contains()

System.out.println(queue);
}
}