java集合基础
来源:互联网 发布:java培训班达内免费 编辑:程序博客网 时间:2024/06/16 05:13
一.理解集合
- Collection:List、Set
- Map:Hashtable、HashMap、WeakHashMap
集合类型主要有3种:set(集)、list(列表)和map(映射)。
1.collection接口
Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。JDK提供的类都是继承自Collection的“子接口”(List和Set)。
遍历Collection中的每一个元素:使用iterator()的方法,该方法返回一个迭代子,使用该迭代子即可逐一访问Collection中每一个元素。
Iterator it = collection.iterator(); // 获得一个迭代子 while(it.hasNext()) { Object obj = it.next(); // 得到下一个元素 }
add(E o)
确保此 collection 包含指定的元素(可选操作)。boolean addAll(Collection<? extends E> c)
将指定 collection 中的所有元素都添加到此 collection 中(可选操作。void clear()
移除此 collection 中的所有元素(可选操作)。boolean
contains(Object o)
如果此 collection 包含指定的元素,则返回 true。boolean containsAll(Collection<?> c)
如果此 collection 包含指定 collection 中的所有元素,则返回true。boolean equals(Object o)
比较此 collection 与指定对象是否相等。int hashCode()
返回此 collection 的哈希码值。boolean
isEmpty()
如果此 collection 不包含元素,则返回true。Iterator<E> iterator()
返回在此 collection 的元素上进行迭代的迭代器。boolean remove(Object o)
从此 collection 中移除指定元素的单个实例,如果存在的话(可选操作)。boolean
removeAll(Collection<?> c)
移除此 collection 中包含在指定 collection 中的所有元素(可选操作)。boolean
retainAll(Collection<?> c)
保留此 collection 中包含在指定 collection 的元素(可选操作)。int
size()
返回此 collection 中的元素数。Object[]
toArray()
返回包含此 collection 中所有元素的数组。<T> T[] toArray(T[] a)返回包含此 collection 中所有元素的数组;返回数组的运行时类型与指定数组的运行时类型相同。
1.1 List接口
List
接口继承了 Collection
接口以定义一个允许重复项的有序集合。
list的特殊方法List subList(int fromIndex, int toIndex)
处理subList()
时,位于fromIndex
的元素在子列表中,而位于toIndex
的元素不是。
Iterator:只能正向遍历集合,适用于获取移除元素。ListIerator:继承Iterator,可以双向列表的遍历,同样支持元素的修改。
list方法举例:
import java.util.Collections;import java.util.LinkedList;import java.util.List;public class TestSet { public static void main(String[] args) { List<Integer> list=new LinkedList<Integer>(); for(int i=0;i<=9;i++){ list.add(i); } Collections.shuffle(list);//随机排序 System.out.println("shuffle:"+list); Collections.reverse(list); System.out.println("reverse:"+list);//原来list序列基础上逆序输出 Collections.sort(list); System.out.println("sort:"+list);//正序输出 //折半查找 System.out.println(Collections.binarySearch(list, 4)); }}输出结果:
shuffle:[6, 2, 7, 3, 8, 4, 9, 0, 5, 1]reverse:[1, 5, 0, 9, 4, 8, 3, 7, 2, 6]sort:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]4
list接口扩容机制
集合是我们在Java编程中使用非常广泛的,容器的量变得非常大的时候,它的初始容量就会显得很重要了,因为扩容是需要消耗内存的。同样的道理,Collection的初始容量也显得异常重要。所以:对于已知的情景,请为集合指定初始容量。
举例:
import java.util.ArrayList;import java.util.List;public class User { private String userName; private Integer age; public User(Integer age,String userName){ this.age=age; this.userName=userName; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public static void main(String[] args) { User user = null; long begin1 = System.currentTimeMillis(); List<User> list1 = new ArrayList<User>();//不指定初始容量 for(int i = 0 ; i < 100000; i++){ user = new User(i,"caka"+i); list1.add(user); } long end1 = System.currentTimeMillis(); System.out.println("list1 time:" + (end1 - begin1)); long begin2 = System.currentTimeMillis(); List<User> list2 = new ArrayList<User>(100000); //指定初始容量 for(int i = 0 ; i < 100000; i++){ user = new User(i,"caka"+i); list2.add(user); } long end2 = System.currentTimeMillis(); System.out.println("list2 time:" + (end2 - begin2)); }}输出结果:
list1 time:43list2 time:16从上面的运行结果可以看出ArrayList的扩容机制是比较消耗资源的
ArrayList每次新增一个元素,就会检测ArrayList的当前容量是否已经到达临界点,如果到达临界点则会扩容1.5倍。ArrayList不是线程安全的,只能用在单线程环境下。
线程不安全验证举例:
add方法的底层实现
public boolean add(E e) { ensureCapacity(size + 1); elementData[size++] = e; return true; }
size++这边,主要分为两个步骤:1)将add的元素放到size位置;2)将size加1
假设size=10.若线程A在10位置存放了值caka,获得size=10,但还没来得及将size加1写入主存。此时线程B在也在10位置存放了值july,也获得size=10,而后A、B分别将size加1后写入主存,size=11,即两个线程执行两次add()后size只加了1。
举例如下:
import java.util.ArrayList;public class User { static ArrayList<String> list = new ArrayList<String>(); public static void main(String[] args) { for (int i = 0; i < 20; i++) { new Thread(new Runnable() { @Override public void run() { list.add("caka"); } }).start(); } while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("size:"+list.size()); } } }
输出结果应该是20,但是实际却可能小于20.ArryList在多线程环境下不安全
参考资料说要想实现线程安全需要这样写:List<String> list = Collections.synchronizedList(new ArrayList<String>())
对于List中常用的ArrayList的总结:
- 无参构造方法构造的ArrayList的容量默认为10
- ArrayList在每次增加元素,当容量不足以容纳当前的元素个数时,就设置新的容量为旧的容量的1.5倍加1。如果设置后的新容量还不够,则直接新容量设置为传入的参数,再用Arrays.copyof()方法将元素拷贝到新的数组。
从中可以看出,当容量不够时,每次增加元素,都要将原来的元素拷贝到一个新的数组中,非常耗时,也因此建议在事先能确定元素数量的情况下,才使用ArrayList,否则使用LinkedList。
- 在查找给定元素索引值等的方法中,源码都将该元素的值分为null和不为null两种情况处理,ArrayList中允许元素为null。
- LinkedList类允许为空,是非同步的;如果要实现同步,需要自己在创建List的时候构造一个同步List。(List list = Collections.synchronizedList(new LinkedList(....)));
- ArraryList实现了可变大小的数组。允许所有元素,包括null,是非同步的。每个ArraryList实例都有一个容量,用于存储元素数组的大小。该容量可随着不断增加新元素而自动增加。每次增加为原来的一半。
- Vector类类似与ArraryList类。但Vetor是同步的。所以当一个Iterator被创建而且被使用的时候,另一个线程改变了Vetor的状态。比如增加或者删除元素。这时条用Iterator的方法就会抛出ConcurrentModificationException. 需要捕获该异常。Vector数据增长每次增长为原来的一倍。
元素。
1.2 set接口
set接口不允许集合中存在重复项。具体的Set
实现类依赖添加的对象的 equals()
方法来检查等同性。“集合框架”支持 Set
接口两种普通的实现:HashSet
和TreeSet,添加到 HashSet
的对象需要采用恰当分配散列码的方式来实现hashCode()
方法。添加到TreeSet
的元素必须是可排序的,必须实现Comparable
set集合实例:
import java.util.HashSet;import java.util.Set;import java.util.TreeSet;public class TestSet { public static void main(String[] args) { Set<String> set = new HashSet<String>(); set.add("旺仔牛奶"); set.add("旺仔牛奶");//重复性验证,结果只输出一次 set.add("b旺仔牛奶"); set.add("a旺仔牛奶"); set.add("c旺仔牛奶"); set.add("d旺仔牛奶"); System.out.println(set); Set<String> sortedSet = new TreeSet<String>(set); System.out.println(sortedSet);//排序输出 }}输出结果:
[b旺仔牛奶, 旺仔牛奶, d旺仔牛奶, c旺仔牛奶, a旺仔牛奶][a旺仔牛奶, b旺仔牛奶, c旺仔牛奶, d旺仔牛奶, 旺仔牛奶]
1.3 queue接口
Queue继承了Collection接口。LinkedList实现了Queue接 口。Queue用于模拟队列。通常是指“先进先出(FIFO)”。新元素插入到队列的尾部,取出元素会返回队列头部的元素。通常,队列不允许随机访问队列中的元素。
Queue接口中定义了如下的几个方法:
add
put
offer
remove
poll
take
element
peek
阻塞队列
java.ulil.concurrent包有阻塞队列的4个变种。
LinkedBlockingQueue:LinkedBlockingQueue的容量默认情况下为Integer.MAX_VALUE,但是也可以选择指定其最大容量,它是基于链表的队列,此队列按 FIFO(先进先出)排序元素。
ArrayBlockingQueue 在构造时需要指定容量, 并可以选择是否需要公平性,如果公平参数被设置true,等待时间最长的线程会优先得到处理。
PriorityBlockingQueue是一个带优先级的 队列,而不是先进先出队列。元素按优先级顺序被移除,PriorityQueue保存队列元素的顺序不是按照元素添加的顺序来保存的,而是在添加元素的时候对元素的大小排序后再保存的。因此在PriorityQueue中使用peek()或pool()取出队列中头部的元素,取出的不是最先添加的元素,而是最小的元素。该队列也没有上限有容量限制的,与ArrayList一样,所以在优先阻塞 队列上put时是不会受阻的。虽然此队列逻辑上是无界的,但是由于资源被耗尽,所以试图执行添加操作可能会导致 OutOfMemoryError,但是如果队列为空,那么取元素的操作take就会阻塞,所以它的检索操作take是受阻的。
package example;import java.util.PriorityQueue;public class PriorityQueueTest { public static void main(String[] args){ PriorityQueue priorityQueue = new PriorityQueue(); priorityQueue.offer(6); priorityQueue.add(-3); priorityQueue.add(20); priorityQueue.offer(18); System.out.println(priorityQueue); }}
输出结果:
[-3, 6, 20, 18]
DelayQueue(基于PriorityQueue来实现的)是一个存放Delayed 元素的无界阻塞队列,只有在延迟期满时才能从中提取元素。该队列的头部是延迟期满后保存时间最长的 Delayed 元素。如果延迟都还没有期满,则队列没有头部,并且poll将返回null。当一个元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回一个小于或等于零的值时,则出现期满,poll就以移除这个元素了。此队列不允许使用 null 元素。放入DelayQueue的元素还将要实现compareTo方法,DelayQueue使用这个来为元素排序。
public interface Delayed extends Comparable<Delayed> { long getDelay(TimeUnit unit); }
2.map接口
该接口描述了从不重复的键到值的映射。键和值都可以为 null
。但是,不能把Map
作为一个键或值添加给自身。
相关方法:
Object put(Object key, Object value)返回值是被替换的值Object remove(Object key)void putAll(Map mapping)
void clear()
Object get(Object key)返回指定键关联的值
boolean containsKey(Object key)包含指定键的映射返回true
boolean containsValue(Object value)此 Map 将一个或多个键映射到指定值,返回 true
int size()
boolean isEmpty()
集合框架提供两种常规的Map
实现:HashMap
和TreeMap。在
Map
中插入、删除和定位元素,HashMap
是最好的选择,(使用HashMap
要求添加的键类明确定义了hashCode()
实现)如果要按顺序遍历键,那么TreeMap
会更好。根据集合大小,先把元素添加到HashMap
,再把这种映射转换成一个用于有序键遍历的TreeMap
。
1.使用entries来遍历(如果遍历的是一个空的map对象,for-each循环将抛出NullPointerException,因此在遍历前你总是应该检查空引用。)
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()); }2.通过keySet或values来实现遍历
Map<Integer, Integer> map = new HashMap<Integer, Integer>(); //遍历map中的键 for (Integer key : map.keySet()) { System.out.println("Key = " + key); } //遍历map中的值 for (Integer value : map.values()) { System.out.println("Value = " + value); }3.使用Iterator遍历
Map<Integer, Integer> map = new HashMap<Integer, Integer>(); Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator(); while (entries.hasNext()) { Map.Entry<Integer, Integer> entry = entries.next(); System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue()); }
补充一种:通过Map.value()遍历所有value,但不能遍历Key.
for(Integer v : map.values()) { System.out.println("value =" + v);}
注意点:
由于作为Key的对象将通过计算其散列函数来确定与之相对应的value的位置。所以作为Key的对象都必须实现hashCode和equals方法。这两种方法继承自根类Object,如果需要自定义的类当作key的话,散列函数的定义是:如果两个对象相同,比如object1.equals(object2)= true; 则他们的hashCode必须相同。但如果两个对象不同,他们的hashCode不一定不同。
如果相同的对象有不同的hashCode,哈希表的操作会出现get方法返回null.所以一定要同时复写equals和hashCode方法。
hashCode()缺省情况下返回的是对象的内存地址。所以每个java对象都可以生成hashCode;equals是根类的方法,比较两个对象地址是否相同。
HashMap类:
HashMap允许为null。是非同步的,HashMap的迭代器(Iterator)是fail-fast迭代器。如果某个集合对象创建了Iterator或者ListIterator,然后其它的线程试图“结构上”更改集合对象,将会抛出ConcurrentModificationException异常。HashMap使用put(key, value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。当给put()方法传递键和值时,先对键调用hashCode()方法,返回的hashCode用于找到bucket位置来储存Entry对象。hashMap使用containsKey和containsValue判断是否包含key值或者value值。Java 5提供了ConcurrentHashMap,线程安全。
HashTable类:
HashTable不允许null值。是同步的。使用Enumeration作为迭代器。速度更慢。
- [ java ] java基础集合!
- JAVA基础之集合
- java基础之集合
- java基础:集合connection
- java基础之 集合
- java基础10 集合
- java基础--集合
- java基础---集合类
- java基础知识点集合
- java基础 集合迭代器
- JAVA基础之集合
- Java集合框架基础
- java基础--HashSet集合
- java基础_09_集合
- 【Java基础】集合
- Java基础之 集合
- java基础__集合
- JAVA基础集合
- hdu 1051
- linux-ha heartbeat高可用的安装配置(http服务高可用)
- Nginx使用webbench进行压力测试
- 使类和成员的可访问性最小化
- [AHK]修改系统时间
- java集合基础
- 使用Charels对Android模拟器上的app进行抓包
- 获取文件夹中的文件名写入文件
- 算法面试100题——2.设计包含min函数的栈
- Python爬天气预报,再发到自己的邮箱
- Android学习笔记七之ContentProvider
- Retrofit2.0 公共参数(固定参数)
- 【权限设计】采用BigInteger进行的权限设计
- mongodb常用命令脚本化-自动化运维