繁体   English   中英

java中的复杂集合

[英]complex collection in java

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

  1. 通过对象的主键访问(唯一)
  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);
    }
}

这个类(我认为)做你想要的,它不会不必要地存储任何东西。

时间复杂度:

  • put() O(log n)时间(以便通过二级密钥排序)。

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

    该方法在log(n)时间内运行“随机访问”列表(其提供接近恒定时间的位置访问)。

    primarySecondaryKeyList是“随机访问”列表,因为它使用ArrayList )。

  • 通过主键获取对象的平均O(1)时间O(n)如果主键类型具有非常糟糕的散列函数,则最坏情况变为O(n) )。
    (见https://en.wikipedia.org/wiki/Hash_table

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

但是,请注意,如果您的主键只是一个Integer或一个Long ,最坏情况下O(n)将永远不会发生,因为它们的哈希函数只是它们的数值,因此它们非常准确(通常会导致O(1)时间对于get() s)。

用法:

以下示例假定您使用Long s作为主键 存储String ,并将Integer作为辅助键存储 但是,该类是通用的,因此您可以使用任何类型的主键存储任何类型的对象(只需确保它具有相当好的hashCode()函数!)和任何与自身可Comparable辅助键(所有原始Number包装器) ,像IntegerLongShort等,已经可以自己Comparable了)。

要使用默认初始容量进行初始化:

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

初始容量为50:

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

将字符串"hello"与主键937234L和辅助键2放在一起:

myCollection.put("hello", 937234L, 2);

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


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

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

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周期? 您是否需要它才能成为最容易编写和维护的代码? 或者你只是想把一些有用的东西放在一起? 我实际上并没有要求你回答这些问题,但是在考虑这类问题时你应该考虑它们。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM