Is there a way to highlight a row in GlazedLists? - java

I have a list which is used to monitor arrival of certain entities in a strictly ascending numeric sequence and want to display an entry where there is an apparent break in the sequence.
Is there any way to highlight an entry in GlazeLists?

It's difficult to be certain about whether you're asking about how to highlight new elements within a list, or literally highlight a row in a UI component backed by a GlazedLists EventList.
For now I'll assume the former but feel free to clarify.
There is the notion of ListEvents within the GlazedLists package that allows one to get a small peak into changes that affect a list. It's not something I've played with much, and it seems rather rudimentary, but it's possible to use this mechanism given the right circumstances.
Here's a sample class which has a BasicEventList containing some integers. I've created a ListEventListener and attached it to the EventList. The ListEvents tell you where the element was inserted. It also contains a reference to the eventlist, so it's possible to get the newly inserted value, and also the value of the element preceding it. I do a quick comparison to see whether they're out of sequence.
Of course there are some major caveats here. The event handling is asynchronous, so it's entirely possible that the underlying list will changed considerably between the time of the original trigger and the time at which the listener is processing the event. In my sample it's ok because I'm only using append operations. Also I'm only using a BasicEventList; if it were a SortedList then the items would be inserted in different indexes, so the method I use for getting the current and previous values would be extremely unreliable. (There may be ways around this but I haven't applied myself to this problem in all honesty.)
At the very least you can use the listener to at least alert you to a list change and have another method outside of the listener class perform a scan of your list to determine whether there are items out of order.
import ca.odell.glazedlists.BasicEventList;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.GlazedLists;
import ca.odell.glazedlists.event.ListEvent;
import ca.odell.glazedlists.event.ListEventListener;
public class GlazedListListen {
private final EventList<Integer> numbers = new BasicEventList<Integer>();
public GlazedListListen() {
numbers.addListEventListener(new MyEventListListener());
numbers.addAll(GlazedLists.eventListOf(1,2,4,5,7,8));
}
class MyEventListListener implements ListEventListener<Integer> {
#Override
public void listChanged(ListEvent<Integer> le) {
while (le.next()) {
if (le.getType() == ListEvent.INSERT) {
final int startIndex = le.getBlockStartIndex();
if (startIndex == 0) continue; // Inserted at head of list - nothing to compare with to move on.
final Integer previousValue = le.getSourceList().get(startIndex-1);
final Integer newValue = le.getSourceList().get(startIndex);
System.out.println("INSERTING " + newValue + " at " + startIndex);
if ((newValue - previousValue) > 1) {
System.out.println("VALUE OUT OF SEQUENCE! " + newValue + " # " + startIndex);
}
}
}
}
}
public static void main(String[] args) {
new GlazedListListen();
}
}
Note: I've only tested against GlazedLists v1.8.

Related

Listener for counter until variable changing

Is there a way to build listener that detect if date are still transmitted to variable and if yes do one thing and when not do other?
For example
Until “int counter1” increasing set boolean (true) or print or change another int for 1
int counter (not increasing or decreasing anymore) set Boolean (false) print different thing change another int for 2.
Basically variable changing plus or minus do one thing stop changing do other thing start changing again go back to doing first thing etc etc.
Is there a way to do this?
Without obvious whole if statements compering way.
Handmade
Most simple way is to access that variable through getters and setters. You can put preferred logic into your setter and track all mutations from there.
public class Main {
static int observable = 0;
static void setObservable(int newValue) {
if (observable != newValue) {
System.out.printf("Observable int has been changed from %d to %d.%n", observable, newValue);
observable = newValue;
}
}
public static void main(String[] args) {
observable = 1; // Nothing notified us that value has been changed
setObservable(2); // Console output 'Observable int changed from 1 to 2.'
}
}
Built-in solutions
There are plenty other ways to implement the same functionality: create actual java bean with getters and setters, implement observable and observer interfaces on your own or use ready built-in solutions, for example IntegerProperty:
IntegerProperty intProperty = new SimpleIntegerProperty();
intProperty.addListener((observable, oldValue, newValue) -> {
if (!oldValue.equals(newValue) ) {
System.out.printf("Value has been changed from %d to %d!%n", oldValue.intValue(), newValue.intValue());
}
});
intProperty.setValue(1); // Output: Value has been changed from 0 to 1!
intProperty.setValue(2); // Output: Value has been changed from 1 to 2!
intProperty.setValue(2); // No output
System.out.println(intProperty.intValue()); // Output: 2
stopped changing
As for "stopped changing" listener, it's a little bit more complex issue. Depending on exact situation, there are several possible solutions I can think of:
1) if your loop is predictable and determined by you, just code the logic manually as it's required
/* listening for changes up there */
System.out.println("I'll go get some coffee");
Thread.sleep(60000); // stopped changing, eh?
/* do your stuff */
/* Continue listening for changes below */
2) if your loop is unpredictable but designed by you, you can try make it a little bit more predictable, design set of rules and protocols to follow, for example if new value is exactly zero, system will pause and switch to another task
3) you can also run background task which will periodically check last updated time, to determine if system is idle
There a lot of possible solutions to suggest, but I can't come up with something more specific without knowing more details

What is the fastest and most concise/correct way to implement this model class backed by values in a 2-dimensional array?

I solved this problem using a graph, but unfortunately now I'm stuck with having to use a 2d array and I have questions about the best way to go about this:
public class Data {
int[][] structure;
public data(int x, int y){
structure = new int[x][y]
}
public <<TBD>> generateRandom() {
// This is what my question is about
}
}
I have a controller/event handler class:
public class Handler implements EventHandler {
#Override
public void onEvent(Event<T> e) {
this.dataInstance.generateRandom();
// ... other stuff
}
}
Here is what each method will do:
Data.generateRandom() will generate a random value at a random location in the 2d int array if there exists a value in the structure that in not initialized or a value exists that is equal to zero
If there is no available spot in the structure, the structure's state is final (i.e. in the literal sense, not the Java declaration)
This is what I'm wondering:
What is the most efficient way to check if the board is full? Using a graph, I was able to check if the board was full on O(1) and get an available yet also random location on worst-case O(n^2 - 1), best case O(1). Obviously now with an array improving n^2 is tough, so I'm just now focusing on execution speed and LOC. Would the fastest way to do it now to check the entire 2d array using streams like:
Arrays.stream(board).flatMapToInt(tile -> tile.getX()).map(x -> x > 0).count() > board.getWidth() * board.getHeight()
(1) You can definitely use a parallel stream to safely perform read only operations on the array. You can also do an anyMatch call since you are only caring (for the isFull check) if there exists any one space that hasn't been initialized. That could look like this:
Arrays.stream(structure)
.parallel()
.anyMatch(i -> i == 0)
However, that is still an n^2 solution. What you could do, though, is keep a counter of the number of spaces possible that you decrement when you initialize a space for the first time. Then the isFull check would always be constant time (you're just comparing an int to 0).
public class Data {
private int numUninitialized;
private int[][] structure;
public Data(int x, int y) {
if (x <= 0 || y <= 0) {
throw new IllegalArgumentException("You can't create a Data object with an argument that isn't a positive integer.");
}
structure = new int[x][y];
int numUninitialized = x * y;
}
public void generateRandom() {
if (isFull()) {
// do whatever you want when the array is full
} else {
// Calculate the random space you want to set a value for
int x = ThreadLocalRandom.current().nextInt(structure.length);
int y = ThreadLocalRandom.current().nextInt(structure[0].length);
if (structure[x][y] == 0) {
// A new, uninitialized space
numUninitialized--;
}
// Populate the space with a random value
structure[x][y] = ThreadLocalRandom.current().nextInt(Integer.MIN_VALUE, Integer.MAX_VALUE);
}
}
public boolean isFull() {
return 0 == numUninitialized;
}
}
Now, this is with my understanding that each time you call generateRandom you take a random space (including ones already initialized). If you are supposed to ONLY choose a random uninitialized space each time it's called, then you'd do best to hold an auxiliary data structure of all the possible grid locations so that you can easily find the next random open space and to tell if the structure is full.
(2) What notification method is appropriate for letting other classes know the array is now immutable? It's kind of hard to say as it depends on the use case and the architecture of the rest of the system this is being used in. If this is an MVC application with a heavy use of notifications between the data model and a controller, then an observer/observable pattern makes a lot of sense. But if your application doesn't use that anywhere else, then perhaps just having the classes that care check the isFull method would make more sense.
(3) Java is efficient at creating and freeing short lived objects. However, since the arrays can be quite large I'd say that allocating a new array object (and copying the data) over each time you alter the array seems ... inefficient at best. Java has the ability to do some functional types of programming (especially with the inclusion of lambdas in Java 8) but only using immutable objects and a purely functional style is kind of like the round hole to Java's square peg.

JavaFX bad design: Row identity in observable lists behind the TableView?

Suppose I am displaying very long table with TableView. Manual says, TableView
is designed to visualize an unlimited number of rows of data
So, since million of rows won't fit the RAM, I would introduce some caching. This means, that ObservableList#get() is allowed to return different instances for the same row index.
Is this true?
What about opposite? May I return the same instance for all row indices filled with different data?
I noticed, that this implies some problem with row editing. At which moment should I pass data to the store? Looks like TableView never calls ObservableList#set() but just mutates obtained instance.
Where to intercept?
UPDATE
Also imagine this very big table was updated at server side. Suppose, one million of records were added.
The only way to report about it -- is by firing observable list addition event, while an addition event also contains reference to all added rows. Which is nonsense -- why send data, which is not event displayed?
I think the intention of the statement in the Javadocs that you quote
is designed to visualize an unlimited number of rows of data
is meant to imply that the TableView imposes no (additional) constraints on the size of the table data: in other words that the view is essentially scalable at a constant memory consumption. This is achieved by the "virtualization": the table view creates cells only for the visible data, and reuses them for different items in the backing list as, e.g., the user scrolls. Since in a typical application the cells (which are graphical) consume far more memory than the data, this represents a big performance saving and allows for as many rows in the table as could feasibly be handled by the user.
There are still, of course, other constraints on the table data size that are not imposed by the table view. The model (i.e. observable list) needs to store the data and consequently memory constraints will (in the default implementation) impose a constraint on the number of rows in the table. You could implement a caching list (see below) to reduce the memory footprint, if needed. And as #fabian points out in the comments below the question, user experience is likely to impose constraints long before you reach that point (I'd recommend using pagination or some kind of filtering).
Your question about identity of elements retrieved from the list is pertinent in a caching implementation: it basically boils down to whether a list implementation is obliged to guarantee list.get(i) == list.get(i), or whether it is enough merely to guarantee list.get(i).equals(list.get(i)). To the best of my knowledge, TableView only expects the latter, so an implementation of ObservableList that caches a relatively small number of elements and recreates them as needed should work.
For proof of concept, here is an implementation of an unmodifiable caching observable list:
import java.util.LinkedList;
import java.util.function.IntFunction;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javafx.collections.ObservableListBase;
public class CachedObservableList<T> extends ObservableListBase<T> {
private final int maxCacheSize ;
private int cacheStartIndex ;
private int actualSize ;
private final IntFunction<T> generator ;
private final LinkedList<T> cache ;
public CachedObservableList(int maxCacheSize, int size, IntFunction<T> generator) {
this.maxCacheSize = maxCacheSize ;
this.generator = generator ;
this.cache = new LinkedList<T>();
this.actualSize = size ;
}
#Override
public T get(int index) {
int debugCacheStart = cacheStartIndex ;
int debugCacheSize = cache.size();
if (index < cacheStartIndex) {
// evict from end of cache:
int numToEvict = cacheStartIndex + cache.size() - (index + maxCacheSize);
if (numToEvict < 0) {
numToEvict = 0 ;
}
if (numToEvict > cache.size()) {
numToEvict = cache.size();
}
cache.subList(cache.size() - numToEvict, cache.size()).clear();
// create new elements:
int numElementsToCreate = cacheStartIndex - index ;
if (numElementsToCreate > maxCacheSize) {
numElementsToCreate = maxCacheSize ;
}
cache.addAll(0,
IntStream.range(index, index + numElementsToCreate)
.mapToObj(generator)
.collect(Collectors.toList()));
cacheStartIndex = index ;
} else if (index >= cacheStartIndex + cache.size()) {
// evict from beginning of cache:
int numToEvict = index - cacheStartIndex - maxCacheSize + 1 ;
if (numToEvict < 0) {
numToEvict = 0 ;
}
if (numToEvict >= cache.size()) {
numToEvict = cache.size();
}
cache.subList(0, numToEvict).clear();
// create new elements:
int numElementsToCreate = index - cacheStartIndex - numToEvict - cache.size() + 1;
if (numElementsToCreate > maxCacheSize) {
numElementsToCreate = maxCacheSize ;
}
cache.addAll(
IntStream.range(index - numElementsToCreate + 1, index + 1)
.mapToObj(generator)
.collect(Collectors.toList()));
cacheStartIndex = index - cache.size() + 1 ;
}
try {
T t = cache.get(index - cacheStartIndex);
assert(generator.apply(index).equals(t));
return t ;
} catch (Throwable exc) {
System.err.println("Exception retrieving index "+index+": cache start was "+debugCacheStart+", cache size was "+debugCacheSize);
throw exc ;
}
}
#Override
public int size() {
return actualSize ;
}
}
And here's a quick example using it, that has 100,000,000 rows in the table. Obviously this is unusable from a user experience perspective, but it seems to work perfectly well (even if you change the cache size to be smaller than the number of displayed cells).
import java.util.Objects;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
public class CachedTableView extends Application {
#Override
public void start(Stage primaryStage) {
CachedObservableList<Item> data = new CachedObservableList<>(100, 100_000_000, i -> new Item(String.format("Item %,d",i)));
TableView<Item> table = new TableView<>();
table.setItems(data);
TableColumn<Item, String> itemCol = new TableColumn<>("Item");
itemCol.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
itemCol.setMinWidth(300);
table.getColumns().add(itemCol);
Scene scene = new Scene(table, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static class Item {
private final StringProperty name = new SimpleStringProperty();
public Item(String name) {
setName(name) ;
}
public final StringProperty nameProperty() {
return this.name;
}
public final String getName() {
return this.nameProperty().get();
}
public final void setName(final String name) {
this.nameProperty().set(name);
}
#Override
public boolean equals(Object o) {
if (o.getClass() != Item.class) {
return false ;
}
return Objects.equals(getName(), ((Item)o).getName());
}
}
public static void main(String[] args) {
launch(args);
}
}
There's obviously quite a lot more to do if you want to implement the list so that it is modifiable; start by thinking about exactly what behavior you would need for set(index, element) if index is not in the cache... and then subclass ModifiableObservableListBase.
For editing:
I noticed, that this implies some problem with row editing. At which moment should I pass data to the store? Looks like TableView never calls ObservableList#set() but just mutates obtained instance.
You have three options that I can see:
If your domain objects use JavaFX properties, then the default behavior is to update the property when editing is committed. You can register listeners with the properties and update the backing store if they change.
Alternatively, you can register an onEditCommit handler with the TableColumn; this will get notified when an edit is committed in the table, and so you could update the store from this. Note that this will replace the default edit commit behavior, so you will also need to update the property. This gives you the opportunity to veto the update to the cached property if the update to the store fails for some reason, and is probably the option you want.
Thirdly, if you implement the editing cells yourself, instead of using default implementations such as TextFieldTableCell, you could invoke methods on the model directly from the controls in the cell. This is probably not desirable, as it violates the standard design patterns and avoids the usual editing notifications built into the table view, but it may be a useful option in some cases.
Also imagine this very big table was updated at server side. Suppose, one million of records were added.
The only way to report about it -- is by firing observable list addition event, while an addition event also contains reference to all added rows.
That's not true, as far as I can tell. ListChangeListener.Change has a getAddedSublist() method, but the API docs for this state it returns
a subList view of the list that contains only the elements added
so it should simply return getItems().sublist(change.getFrom(), change.getTo()). Of course, this simply returns a sublist view of the cached list implementation, so doesn't create the objects unless you explicitly request them. (Note that getRemoved() might potentially cause more problems, but there should be some way to work around that too.)
Finally, to bring this full circle, while the observable list implementation is doing work here of caching the elements and making the model "unlimited" in the number of rows it can support (up to Integer.MAX_VALUE), it wouldn't be possible to use this in the table view if the table view didn't implement "virtualization". A non-virtualized implementation of table view would create cells for each item in the list (i.e. it would call get(i) for 0 <= i < items.size(), creating a cell for each), place the cells in a scroll pane implementation, and the memory consumption would blow up even with the caching in the list. So "unlimited" in the Javadocs really does mean that any limit is deferred to implementation of the model.

Move Elements In An ArrayList Down One That Are Higher Than Specific Index

I have a server that creates a new Thread when a client joins and puts it in an ArrayList (that is of the type EchoThread). It works great - creates a new JLabel for each one and updates their positions in my game near instantly, however if one leaves (that isn't the last client in the arraylist) then I want the server to move all of the clients (that are higher than the index of the client that left) down by one. I have tried many things, some partially working. Here is the method if somebody leaves, where i is equal to the index of the client that left:
public static void removeClient(int i) throws Exception {
}
I tried a couple of things, such as using a for loop to move each of the EchoThread's in the arrayList down by one, however they dont seem to work:
The ArrayList:
static ArrayList<EchoThread> clients = new ArrayList<EchoThread>();
The for loop that I tried:
for(int ii = i+1; ii < clients.size()-1; ii++){
EchoThread e = clients.get(ii);
clients.set(ii-1, e);
}
I also tried to make a temporary arraylist that gets all of the elements in the clients arraylist added, except the index i, and then set the clients arraylist to equal the temporary arraylist, but it still failed.
Somebody said I could use a linked list, but I don't know how?
Edit:
p = a JPanel
f = a JFrame
clients = an arrayList<EchoThread>
clientinfo = a JLabel in EchoThread class
clientname = an arraylist of strings
clientpos = an arraylist of the positions of each client
info = a JLabel to show number of clients that are connected
Method:
public static void removeClient(int i) throws Exception {
p.remove(clients.get(i).clientinfo);
p.revalidate();
f.validate();
f.repaint();
clients.get(i).clientinfo.setVisible(false);
clients.remove(i);
clientnames.remove(i);
clientpos.remove(i);
info.setText("Clients Connected: " + clients.size());
for(int ii = i+1; ii < clients.size()-1; ii++){
EchoThread e = clients.get(ii);
clients.set(ii-1, e);
}
}
You must make sure you are calling removeClient in a thread-safe way. I'm guessing but it sounds like perhaps you're calling it from each EchoThread, which is not safe without synchronization, because (a) the threads can trample all over each other's modifications to the list; and (b) there is no guarantee threads will even see each other's modifications to the list.
Usually you could just add the synchronized modifier on any methods that modify the ArrayList, which provides both mutual exclusion (preventing multiple threads from modifying it at once and corrupting it) and a happens-before relationship between calls to those methods (making sure threads see each other's changes to the list). However, that won't be enough here, because you are also using the method to modify Swing components. With few exceptions, Swing components must be accessed only from the single event dispatch thread, so if you want to update the frame and you are on a different thread, you must first switch to the event dispatch thread.
In Java 8+, modify the beginning of your removeClient method as follows:
public static void removeClient(int i) {
if (!SwingUtilities.isEventDispatchThread()) {
SwingUtilities.invokeLater(() -> removeClient(i));
return;
}
// ...
In old versions of Java:
public static void removeClient(final int i) {
if (!SwingUtilities.isEventDispatchThread()) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
removeClient(i);
}
});
return;
}
// ...
Once you are on the correct thread, removing the item from the list and moving the other items down the list really is as simple as calling clients.remove(i);, as the other answerers have said. You don't need that extra for-loop and it won't work.
Perhaps your problem is that although the list does move the items down, you have given those items their own index field, so after removing the item, its own index field no longer matches its actual position in the list? You could iterate the items in the list and reset the index field of each item to match its real position:
for (int i = 0; i < clients.size(); i++)
clients.get(i).index = i;
(assuming you have some field index on your EchoThread objects).
However, that might not be the best approach. You don't necessarily need to keep track of the index, since you can perfectly well remove the object itself without knowing the index:
EchoThread client = ...;
clients.remove(client);
How to update your other lists, clientnames, clientpos? Well, you could find out the index by calling indexOf on the first list. But that is ugly. You shouldn't really have all these separate lists. You should preferably have a class that encapsulates the various different fields for a single client including the "name" and "pos" fields, and make a list of those objects. You already have the EchoClient objects, so maybe you can just move the fields to that.
The API already does this for you with the remove method.
Taken from the docs:
public E remove(int index)
Removes the element at the specified position in this list. Shifts any subsequent elements to the left (subtracts one from their indices).
have you tried clients.remove(i);
Here's the implementation of ArrayList#remove. As you can see, it uses System.arraycopy to slide the tail of the list down so you have an uninterrupted list.
/**
* Removes the element at the specified position in this list.
* Shifts any subsequent elements to the left (subtracts one from their
* indices).
*
* #param index the index of the element to be removed
* #return the element that was removed from the list
* #throws IndexOutOfBoundsException {#inheritDoc}
*/
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}

Simple database-like collection class in Java

The problem: Maintain a bidirectional many-to-one relationship among java objects.
Something like the Google/Commons Collections bidi maps, but I want to allow duplicate values on the forward side, and have sets of the forward keys as the reverse side values.
Used something like this:
// maintaining disjoint areas on a gameboard. Location is a space on the
// gameboard; Regions refer to disjoint collections of Locations.
MagicalManyToOneMap<Location, Region> forward = // the game universe
Map<Region, <Set<Location>>> inverse = forward.getInverse(); // live, not a copy
Location parkplace = Game.chooseSomeLocation(...);
Region mine = forward.get(parkplace); // assume !null; should be O(log n)
Region other = Game.getSomeOtherRegion(...);
// moving a Location from one Region to another:
forward.put(parkplace, other);
// or equivalently:
inverse.get(other).add(parkplace); // should also be O(log n) or so
// expected consistency:
assert ! inverse.get(mine).contains(parkplace);
assert forward.get(parkplace) == other;
// and this should be fast, not iterate every possible location just to filter for mine:
for (Location l : mine) { /* do something clever */ }
The simple java approaches are: 1. To maintain only one side of the relationship, either as a Map<Location, Region> or a Map<Region, Set<Location>>, and collect the inverse relationship by iteration when needed; Or, 2. To make a wrapper that maintains both sides' Maps, and intercept all mutating calls to keep both sides in sync.
1 is O(n) instead of O(log n), which is becoming a problem. I started in on 2 and was in the weeds straightaway. (Know how many different ways there are to alter a Map entry?)
This is almost trivial in the sql world (Location table gets an indexed RegionID column). Is there something obvious I'm missing that makes it trivial for normal objects?
I might misunderstand your model, but if your Location and Region have correct equals() and hashCode() implemented, then the set of Location -> Region is just a classical simple Map implementation (multiple distinct keys can point to the same object value). The Region -> Set of Location is a Multimap (available in Google Coll.). You could compose your own class with the proper add/remove methods to manipulate both submaps.
Maybe an overkill, but you could also use in-memory sql server (HSQLDB, etc). It allows you to create index on many columns.
I think you could achieve what you need with the following two classes. While it does involve two maps, they are not exposed to the outside world, so there shouldn't be a way for them to get out of sync. As for storing the same "fact" twice, I don't think you'll get around that in any efficient implementation, whether the fact is stored twice explicitly as it is here, or implicitly as it would be when your database creates an index to make joins more efficient on your 2 tables. you can add new things to the magicset and it will update both mappings, or you can add things to the magicmapper, which will then update the inverse map auotmatically. The girlfriend is calling me to bed now so I cannot run this through a compiler - it should be enough to get you started. what puzzle are you trying to solve?
public class MagicSet<L> {
private Map<L,R> forward;
private R r;
private Set<L> set;
public MagicSet<L>(Map forward, R r) {
this.forward = map;
this.r = r;
this.set = new HashSet<L>();
}
public void add(L l) {
set.add(l);
forward.put(l,r);
}
public void remove(L l) {
set.remove(l);
forward.remove(l);
}
public int size() {
return set.size();
}
public in contains(L l){
return set.contains(l);
}
// caution, do not use the remove method from this iterator. if this class was going
// to be reused often you would want to return a wrapped iterator that handled the remove method properly. In fact, if you did that, i think you could then extend AbstractSet and MagicSet would then fully implement java.util.Set.
public Iterator iterator() {
return set.iterator();
}
}
public class MagicMapper<L,R> { // note that it doesn't implement Map, though it could with some extra work. I don't get the impression you need that though.
private Map<L,R> forward;
private Map<R,MagicSet<L>> inverse;
public MagicMapper<L,R>() {
forward = new HashMap<L,R>;
inverse = new HashMap<R,<MagicSet<L>>;
}
public R getForward(L key) {
return forward.get(key);
}
public Set<L> getBackward(R key) {
return inverse.get(key); // this assumes you want a null if
// you try to use a key that has no mapping. otherwise you'd return a blank MagicSet
}
public void put (L l, R r) {
R oldVal = forward.get(l);
// if the L had already belonged to an R, we need to undo that mapping
MagicSet<L> oldSet = inverse.get(oldVal);
if (oldSet != null) {oldSet.remove(l);}
// now get the set the R belongs to, and add it.
MagicSet<L> newSet = inverse.get(l);
if (newSet == null) {
newSet = new MagicSet<L>(forward, r);
inverse.put(r,newSet);
}
newSet.add(l); // magically updates the "forward" map
}
}

Categories