我需要一种能够以二维方式存储信息的数据结构。例如,想象一个包含用户项评级的表。我需要为所有用户存储所有评级。比方说,用户u1。我需要为用户u1和u2以及u3和所有其他用户存储评级。但问题是我还需要存储所有项目的所有评级。例如,我需要存储所有用户为每个项目提供的评级。所以我需要类似地图的东西,对于用户来说,密钥是用户ID,值是评级集。我可以轻松地做到这一点。但我的问题是如何存储物品的评级。例如,密钥是项目ID的映射,值是提供的评级集合,是该项目的用户。我想上传一个表,但由于我没有足够的声誉,我不能这样做。所以想象一个像二维矩阵的表,行是用户,列是项。是否有可以做到这一点的数据结构?或者我应该建立两个不同的地图?也许有一个比Map更好的选择但是因为我必须为我的问题选择一个标题我写了地图。
由于
答案 0 :(得分:6)
Table<Integer, String, Double> table = HashBasedTable.create();
table.put(1, "a", 2.0);
double v = table.get(1, "a"); // getting 2.0
答案 1 :(得分:2)
这是我自己的相应Table对象的版本。请注意,使用现有图书馆提供的东西是好的。但尝试自己的实施将有助于您更好地了解所涉及的问题。因此,您可以尝试在我的实现中添加“删除”方法等来完成它。
我更喜欢将数据保存在表中,而不是在User
和Item
中实现映射,因为该表可以强制通过行和列添加每个新的评级。如果您将地图分成两个独立的对象,则无法强制执行此操作。
请注意,当我在getCol
和getRow
中返回内部地图的保护副本时,我会返回对实际值的引用,而不是其副本,以便您可以更改用户的评级(假设您为此选择了一个可变对象而不更改表结构。另请注意,如果您的用户和项目对象是可变的,这会影响他们的equals
或hashCode
,那么该表将会出现无法预测的行为。
public class Table<K1, K2, V> {
// Two maps allowing us to retrieve the value through the row or the
// column key.
private Map<K1, Map<K2, V>> rowMap;
private Map<K2, Map<K1, V>> colMap;
public Table() {
rowMap = new HashMap<>();
colMap = new HashMap<>();
}
/**
* Allows us to create a key for the row, and place it in the structure
* while there are still no relations for it.
*
* @param key
* The key for which a new empty row will be created.
*/
public void addEmptyRow(K1 key) {
if (!rowMap.containsKey(key)) {
rowMap.put(key, new HashMap<K2, V>());
}
}
/**
* Allows us to create a key for the column, and place it in the
* structure while there are still no relations for it.
*
* @param key
* The key for which a new empty column will be created.
*/
public void addEmptyCol(K2 key) {
if (!colMap.containsKey(key)) {
colMap.put(key, new HashMap<K1, V>());
}
}
/**
* Insert a value into the table using the two keys.
*
* @param rowKey
* Row key to access this value
* @param colKey
* Column key to access this value
* @param value
* The value to be associated with the above two keys.
*/
public void put(K1 rowKey, K2 colKey, V value) {
Map<K2, V> row;
Map<K1, V> col;
// Find the internal row. If there is no entry, create one.
if (rowMap.containsKey(rowKey)) {
row = rowMap.get(rowKey);
} else {
row = new HashMap<K2, V>();
rowMap.put(rowKey, row);
}
// Find the internal column, If there is no entry, create one.
if (colMap.containsKey(colKey)) {
col = colMap.get(colKey);
} else {
col = new HashMap<K1, V>();
colMap.put(colKey, col);
}
// Add the value to both row and column.
row.put(colKey, value);
col.put(rowKey, value);
}
/**
* Get the value associated with the given row and column.
*
* @param rowKey
* Row key to access the value
* @param colKey
* Column key to access the value
* @return Value in the given row and column. Null if mapping doesn't
* exist
*/
public V get(K1 rowKey, K2 colKey) {
Map<K2, V> row;
row = rowMap.get(rowKey);
if (row != null) {
return row.get(colKey);
}
return null;
}
/**
* Get a map representing the row for the given key. The map contains
* only column keys that actually have values in this row.
*
* @param rowKey
* The key to the row in the table
* @return Map representing the row. Null if there is no row with the
* given key.
*/
public Map<K2, V> getRow(K1 rowKey) {
// Note that we are returning a protective copy of the row. The user
// cannot change the internal structure of the table, but is allowed
// to change the value's state if it is mutable.
if (rowMap.containsKey(rowKey)) {
return new HashMap<>(rowMap.get(rowKey));
}
return null;
}
/**
* Get a map representing the column for the given key. The map contains
* only row keys that actually have values in this column.
*
* @param colKey
* The key to the column in the table.
* @return Map representing the column. Null if there is no column with
* the given key.
*/
public Map<K1, V> getCol(K2 colKey) {
// Note that we are returning a protective copy of the column. The
// user cannot change the internal structure.
if (colMap.containsKey(colKey)) {
return new HashMap<>(colMap.get(colKey));
}
return null;
}
/**
* Get a set of all the existing row keys.
*
* @return A Set containing all the row keys. The set may be empty.
*/
public Set<K1> getRowKeys() {
return new HashSet(rowMap.keySet());
}
/**
* Get a set of all the existing column keys.
*
* @return A set containing all the column keys. The set may be empty.
*/
public Set<K2> getColKeys() {
return new HashSet(colMap.keySet());
}
}
我有方法addEmptyRow
和addEmptyCol
的原因是我认为为用户和项目保留单独的数据结构可能是多余的。一旦你将它们添加到这样的表中,你就可以通过getRowKeys
和getColKeys
来获取它们,所以不需要单独保存它们,除非你想用{{1}之外的任何东西构造它们。 }}
请注意,此表使用键的值 - 两个等效的Set
键是相同的键,您应该相应地设计User和Item对象。
通过equals
,User
和Item
的适当定义,您可以执行类似
Rating
然后在表中查询特定用户,如下所示:
Table<User, Item, Rating> table = new Table<>();
table.addEmptyCol(new Item("Television"));
table.addEmptyCol(new Item("Sofa"));
User user = new User("Anakin");
Item item = new Item("Light Sabre");
table.put(user, item, new Rating(5));
Item item1 = new Item("Helmet");
table.put(user, item1, new Rating(7));
Rating rating = table.get(user, item);
rating.setRating(rating.getRating() + 10);
User user1 = new User("Obi-Wan");
table.put(user1, item, new Rating(8));
table.put(user1, new Item("Television"), new Rating(0));
或显示所有项目的评分列表,如下所示:
Map<Item, Rating> anakinsRatings = table.getRow(user);
for (Map.Entry<Item, Rating> entry : anakinsRatings.entrySet()) {
System.out.println(user + " rated " + entry.getKey() + " as "
+ entry.getValue().getRating());
}
正如我所说,实施尚未完成 - 表格中没有 for (Item currItem : table.getColKeys()) {
Map<User, Rating> itemMap = table.getCol(currItem);
if (itemMap.isEmpty()) {
System.out.println("There are currently no ratings for \""
+ currItem
+ "\"");
} else {
for (Map.Entry<User, Rating> entry : table.getCol(currItem).entrySet()) {
System.out.println("\""
+ currItem
+ "\" has been rated "
+ entry.getValue().getRating() + " by "
+ entry.getKey());
}
}
}
,例如,没有toString
,remove
,removeRow
,{{1等等。
答案 2 :(得分:1)
所以,你有两个一对多的关系。用户具有(给出)许多评级,并且项目具有许多评级;这给出了许多用户关系的关系,这是你的问题。为什么不简单地按照描述进行建模:
public class Rating {
private User ratedBy;
private Item itemRated;
public Item getItem() { return itemRated; }
}
public class User {
private Set<Rating> allRatings = new HashSet<>();
public Rating getRatingFor(Item item) {
for(Rating rating: allRatings) {
if(item.equals(rating.getItem()) {
return rating;
}
}
return null;
}
}
public class Item {
private Set<Rating> allRatings = new HashSet<>();
}
根据需要...和getters / setters等。然后,您可以通过以下方式获得评分:
User user1 = new User();
// ... do stuff to populate ratings
Rating itemRatingByUser = user1.getRatingFor(item);
答案 3 :(得分:0)
快速而肮脏的解决方案是使用二维密钥。 假设user和item的id属于同一类型,则可以创建一个类,该类只是id和key类型的持有者。 (类型可以是User或Item的类,如果可用,或者只是枚举值)。然后使地图具有此类型的键。当然,每个评级至少会被引用两次(每种类型一次)
答案 4 :(得分:0)
您需要的数据结构称为表。一个表有两个键和一个对象,或多或少看起来像一个excel表,其中两个键集作为列和行。由此得名。有各种各样的表实现。我认为现在使用Guava实现是正常的。 guava's table interface的解释就在这里。