java中的复杂集合

时间:2015-06-09 16:52:16

标签: java collections

我需要使用以下访问方法定义用户定义对象的集合:

  1. 通过对象的主键访问(唯一)
  2. 通过在辅助密钥(非唯一)上排序的集合的索引进行访问
  3. 每个对象都有一个主键和一个辅助键(不保证唯一)。将对象添加到集合时,应将其插入某个位置,以便对辅助键值进行排序。 应使用主键或集合中的位置提取或删除每个对象。

    实施此系列的最佳方法是什么? (主键和对象的地图将提供一半的访问权限,排序的列表将提供另一半。如何根据我的要求组合这些?)

2 个答案:

答案 0 :(得分:2)

您可以轻松地自己编写这样的内容,其中只有(普通)HashMap只有值的主键,然后是另一个PriorityQueue对主键的辅助键密钥(作为辅助密钥并不总是唯一的,因此您不能使用Map类型。)

解决方案:

这是一个完整的,有效的例子:

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

public class MyCollection<T, TPrimaryKey, TSecondaryKey extends Comparable<TSecondaryKey>> {
    private HashMap<TPrimaryKey, T> primaryKeyMap;
    private List<PrimarySecondaryKeyPair> primarySecondaryKeyList;

    public MyCollection() {
        primaryKeyMap = new HashMap<TPrimaryKey, T>();
        primarySecondaryKeyList = new ArrayList<PrimarySecondaryKeyPair>();
    }

    public MyCollection(int initialCapacity) {
        primaryKeyMap = new HashMap<TPrimaryKey, T>(initialCapacity);
        primarySecondaryKeyList = new ArrayList<PrimarySecondaryKeyPair>(initialCapacity);
    }

    private class PrimarySecondaryKeyPair implements Comparable<PrimarySecondaryKeyPair> {
        public TPrimaryKey primaryKey;
        public TSecondaryKey secondaryKey;

        public PrimarySecondaryKeyPair(TPrimaryKey primaryKey, TSecondaryKey secondaryKey) {
            this.primaryKey = primaryKey;
            this.secondaryKey = secondaryKey;
        }

        @Override
        public int compareTo(MyCollection<T, TPrimaryKey, TSecondaryKey>.PrimarySecondaryKeyPair o) {
            return secondaryKey.compareTo(o.secondaryKey);
        }
    }

    public void put(T object, TPrimaryKey primaryKey, TSecondaryKey secondaryKey) { // put by primaryKey
        primaryKeyMap.put(primaryKey, object);
        PrimarySecondaryKeyPair keyPair = new PrimarySecondaryKeyPair(primaryKey, secondaryKey);
        int insertionPoint = Collections.binarySearch(primarySecondaryKeyList, keyPair);
        primarySecondaryKeyList.add((insertionPoint > -1) ? insertionPoint : (-insertionPoint) - 1, keyPair);
    }

    public T getByPrimaryKey(TPrimaryKey primaryKey) {
        return primaryKeyMap.get(primaryKey);
    }

    public T getByIndex(int index) {
        return getByPrimaryKey(primarySecondaryKeyList.get(index).primaryKey);
    }

    public void deleteByPrimaryKey(TPrimaryKey primaryKey) {
        primaryKeyMap.remove(primaryKey);
        for (int i = 0; i < primarySecondaryKeyList.size(); i++) {
            if (primarySecondaryKeyList.get(i).primaryKey.equals(primaryKey)) {
                primarySecondaryKeyList.remove(i);
            }
        }
    }

    public void deleteByIndex(int index) {
        primaryKeyMap.remove(primarySecondaryKeyList.remove(index).primaryKey);
    }
}

这个课程(我认为)可以做你想要的,也不会不必要地存储任何东西。

时间复杂度:

  • O(log n) put()的时间(为了按二级密钥排序)。

    从这个使用的Collections.binarySearch()方法的JavaDoc :

      

    此方法在log(n)时间内运行,随机访问&#34; list(提供接近恒定时间的位置访问)。

    primarySecondaryKeyList是&#34;随机访问&#34;列表,因为它使用ArrayList)。

  • 主键获取对象的平均O(1)时间(如果您的主键类型具有真正的错误哈希函数,则最坏情况变为O(n)) 。
    (见https://en.wikipedia.org/wiki/Hash_table

  • 按索引排序的索引的平均O(1)时间,因为这取决于获取主键(除非您想要将集合中的每个对象存储两次,否则必须这样做)占用两倍的空间。)
  • 按次要键排序的索引删除的常量O(1)时间。
  • O(n)最糟糕的情况是通过主键删除(从主键映射中删除很容易,但是必须迭代次键到主键列表)

但请注意,如果您的主键只是O(n)Integer,那么最糟糕的情况Long将永远不会发生,因为它们的哈希函数只是它们的数值,使它们成为非常准确(通常会导致O(1) s get()的时间。

用法:

以下示例假设您存储String s Long s作为主键,并且 {{1 s作为辅助键。但是,该类是通用的,因此您可以使用任何类型的主键存储任何类型的对象(只需确保它具有合适的Integer函数!)以及任何对其自身hashCode()的辅助键(所有原始Comparable包装器,如NumberIntegerLong等,已经Short给自己了。

使用默认初始容量初始化:

Comparable

初始容量为50:

MyCollection<String, Long, Integer> myCollection = new MyCollection<String, Long, Integer>();

将字符串MyCollection<String, Long, Integer> myCollection = new MyCollection<String, Long, Integer>(50); 与主键"hello"和辅助键937234L放在一起:

2

......其他一切都应该是自我解释的;)

那应该符合您想要的要求和收集类型。
希望它有所帮助。 :)

答案 1 :(得分:1)

  

(主键和对象的地图将提供一半的访问权限,排序的列表将提供另一半。如何根据我的要求组合这些?)

public class YourCollection{
   private Map<UserObject> yourMap;
   private List<UserObject> yourList;

   public void add(UserObject obj){
      yourMap.put(obj.primaryKey, obj);
      yourList.add(obj);
      //sort list on secondaryKey
   }

   public UserObject get(String key){
      return yourMap.get(key);
   }

   public UserObject get(int index){
      return yourList.get(index);
   }
}

这只是基本的shell,但这个想法就在那里。您选择哪种类型的列表和哪种类型的地图取决于您。你可以/也应该在这个类中添加泛型。

编辑 - 此外,对于给定问题,没有最佳方法。你必须考虑不同方法的利弊,并根据你的目标权衡它们。例如,你需要这个来占用最少的内存吗?您是否需要使用最少的CPU周期?您是否需要它才能成为最容易编写和维护的代码?或者你只是想把一些有用的东西放在一起?我实际上并没有要求你回答这些问题,但在考虑这类问题时你应该考虑它们。